88import Foundation
99import MultiformatsKit
1010
11+ /// A namespace contains types and utilities for working with IPLD (InterPlanetary Linked Data) values,
12+ /// which are used to represent and encode arbitrary, self-describing, content-addressable data.
13+ public enum IPLD : Equatable {
1114
12- public struct IPLD {
13-
14- /// A type-safe and thread-safe representation of JSON-compatible values, used for encoding and
15- /// decoding arbitrary JSON data.
15+ /// A type-safe and thread-safe representation of JSON-compatible values, used for encoding and decoding
16+ /// arbitrary JSON data.
1617 public enum JSONValue : Codable , Equatable , Hashable {
1718
1819 /// A `String` object.
1920 case string( String )
2021
2122 /// A `Double` object.
22- case number( Double )
23+ case number( Int )
2324
2425 /// A `Bool` value.
2526 case bool( Bool )
@@ -40,7 +41,7 @@ public struct IPLD {
4041 self = . nil
4142 } else if let bool = try ? container. decode ( Bool . self) {
4243 self = . bool( bool)
43- } else if let number = try ? container. decode ( Double . self) {
44+ } else if let number = try ? container. decode ( Int . self) {
4445 self = . number( number)
4546 } else if let string = try ? container. decode ( String . self) {
4647 self = . string( string)
@@ -65,147 +66,111 @@ public struct IPLD {
6566 try container. encode ( value)
6667 case . nil :
6768 try container. encodeNil ( )
68- case . array( let values ) :
69- try container. encode ( values )
70- case . dictionary( let dictionary ) :
71- try container. encode ( dictionary )
69+ case . array( let value ) :
70+ try container. encode ( value )
71+ case . dictionary( let value ) :
72+ try container. encode ( value )
7273 }
7374 }
7475 }
7576
77+ /// An enum representing all valid IPLD (InterPlanetary Linked Data) values.
78+ public enum IPLDValue : Codable , Equatable , Hashable {
7679
77- // TODO: Fix this to remove the errors.
78- // public enum IPLDValue: Codable, Equatable, Hashable {
79- //
80- // /// A `String` object.
81- // case string(String)
82- //
83- // /// A `Double` object.
84- // case number(Double)
85- //
86- // /// A `Bool` object.
87- // case bool(Bool)
88- //
89- // /// A `nil` object.
90- // case `nil`
91- //
92- // /// An `Array` of `IPLDValue` objects.
93- // case array([IPLDValue])
94- //
95- // /// A `Dictionary` object, with `IPLDValue` as the value.
96- // case dictionary([String: IPLDValue])
97- //
98- // /// A `CID` object.
99- // // case cid(CID)
100- //
101- // /// A `Data` object.
102- // case bytes(Data)
103- //
104- // public init(from decoder: Decoder) throws {
105- // let container = try decoder.singleValueContainer()
106- //
107- // if container.decodeNil() {
108- // self = .nil
109- // } else if let bool = try? container.decode(Bool.self) {
110- // self = .bool(bool)
111- // } else if let number = try? container.decode(Double.self) {
112- // self = .number(number)
113- // } else if let string = try? container.decode(String.self) {
114- // self = .string(string)
115- // } else if let array = try? container.decode([IPLDValue].self) {
116- // self = .array(array)
117- // } else if let object = try? container.decode([String: IPLDValue].self) {
118- // self = .dictionary(object)
119- // // } else if let cid = try? container.decode(CID.self) {
120- // // self = .cid(cid)
121- // } else if let data = try? container.decode(Data.self) {
122- // self = .bytes(data)
123- // } else {
124- // throw DecodingError.dataCorruptedError(
125- // in: container,
126- // debugDescription: "Unsupported IPLD value"
127- // )
128- // }
129- // }
130- //
131- // public func encode(to encoder: Encoder) throws {
132- // var container = encoder.singleValueContainer()
133- //
134- // switch self {
135- // case .string(let value):
136- // try container.encode(value)
137- // case .number(let value):
138- // try container.encode(value)
139- // case .bool(let value):
140- // try container.encode(value)
141- // case .nil:
142- // try container.encodeNil()
143- // case .array(let values):
144- // try container.encode(values)
145- // case .dictionary(let dictionary):
146- // try container.encode(dictionary)
147- // // case .cid(let cid):
148- // // try container.encode(cid)
149- // case .bytes(let data):
150- // try container.encode(data)
151- // }
152- // }
153- // }
154- //
155- // public static func jsonToIpld(_ value: JSONValue) -> IPLDValue {
156- // switch value {
157- // case .string(let str):
158- // // Handle special IPLD markers
159- // if let json = try? JSONSerialization.jsonObject(with: Data(str.utf8)) as? [String: Any],
160- // let onlyKey = json.keys.first, json.count == 1 {
161- // if onlyKey == "$link", let linkStr = json["$link"] as? String, let cid = try? CID(string: linkStr) {
162- // return .cid(cid)
163- // }
164- // if onlyKey == "$bytes", let base64Str = json["$bytes"] as? String, let data = Data(base64Encoded: base64Str) {
165- // return .bytes(data)
166- // }
167- // }
168- // return .string(str)
169- // case .number(let num):
170- // return .number(num)
171- // case .bool(let bool):
172- // return .bool(bool)
173- // case .nil:
174- // return .nil
175- // case .array(let arr):
176- // return .array(arr.map { jsonToIpld($0) })
177- // case .dictionary(let dict):
178- // var newDict: [String: IPLDValue] = [:]
179- // for (key, val) in dict {
180- // newDict[key] = jsonToIpld(val)
181- // }
182- // return .dictionary(newDict)
183- // }
184- // }
185- //
186- // public static func ipldToJson(_ value: IPLDValue) -> JSONValue {
187- // switch value {
188- // case .string(let str):
189- // return .string(str)
190- // case .number(let num):
191- // return .number(num)
192- // case .bool(let bool):
193- // return .bool(bool)
194- // case .nil:
195- // return .nil
196- // case .array(let arr):
197- // return .array(arr.map { ipldToJson($0) })
198- // case .dictionary(let dict):
199- // var newDict: [String: JSONValue] = [:]
200- // for (key, val) in dict {
201- // newDict[key] = ipldToJson(val)
202- // }
203- // return .dictionary(newDict)
204- // case .cid(let cid):
205- // return .dictionary(["$link": .string(cid.description)])
206- // case .bytes(let data):
207- // return .dictionary(["$bytes": .string(data.base64EncodedString())])
208- // }
209- // }
80+ /// A `JSONValue` object.
81+ case jsonValue( JSONValue )
21082
83+ /// A `CID` object.
84+ case cid( CID )
85+
86+ /// A `Data` object.
87+ case bytes( Data )
88+
89+ /// An array of `IPLDValue` objects.
90+ case array( [ IPLDValue ] )
91+
92+ /// A `Dictionary` object, with `IPLDValue` as the value.
93+ case dictionary( [ String : IPLDValue ] )
94+
95+ public init ( from decoder: Decoder ) throws {
96+ // Decode as JSONValue, then use the adapter to make IPLDValue.
97+ let jsonValue = try JSONValue ( from: decoder)
98+ self = IPLD . jsonToIPLD ( jsonValue)
99+ }
100+
101+ public func encode( to encoder: Encoder ) throws {
102+ // Convert self to JSONValue, then encode.
103+ let jsonValue = IPLD . ipldToJSON ( self )
104+ try jsonValue. encode ( to: encoder)
105+ }
106+ }
107+
108+ /// Converts a `JSONValue` to `IPLDValue`.
109+ ///
110+ /// - Parameter value: A `JSONValue` object.
111+ /// - Returns: An `IPLDValue` object.
112+ public static func jsonToIPLD( _ value: JSONValue ) -> IPLDValue {
113+ switch value {
114+ case . array( let array) :
115+ return . array( array. map { jsonToIPLD ( $0) } )
116+ case . dictionary( let dictionary) :
117+ // Special case for { "$link": "..." }
118+ if dictionary. count == 1 , let jsonValue = dictionary [ " $link " ] , case let . string( linkString) = jsonValue {
119+ if let cid = try ? CID ( string: linkString) {
120+ return . cid( cid)
121+ }
122+ }
123+
124+ // Special case for { "$bytes": "..." }
125+ if dictionary. count == 1 , let jsonValue = dictionary [ " $bytes " ] , case let . string( bytesString) = jsonValue {
126+ if let data = Data ( base64Encoded: bytesString) {
127+ return . bytes( data)
128+ }
129+ }
130+
131+ // Plain object: recursively convert each value.
132+ var newDictionary : [ String : IPLDValue ] = [ : ]
133+ for (key, value) in dictionary {
134+ newDictionary [ key] = jsonToIPLD ( value)
135+ }
136+
137+ return . dictionary( newDictionary)
138+ case . string( let value) :
139+ return . jsonValue( . string( value) )
140+ case . number( let value) :
141+ return . jsonValue( . number( value) )
142+ case . bool( let value) :
143+ return . jsonValue( . bool( value) )
144+ case . nil :
145+ return . jsonValue( . nil )
146+ }
147+ }
148+
149+ /// Converts an `IPLDValue` to `JSONValue`.
150+ ///
151+ /// - Parameter value: A `IPLDValue` object.
152+ /// - Returns: A `JSONValue` object.
153+ public static func ipldToJSON( _ value: IPLDValue ) -> JSONValue {
154+ switch value {
155+ case . array( let array) :
156+ return . array( array. map { ipldToJSON ( $0) } )
157+ case . dictionary( let dictionary) :
158+ var newDictionary : [ String : JSONValue ] = [ : ]
159+ for (key, value) in dictionary {
160+ newDictionary [ key] = ipldToJSON ( value)
161+ }
162+
163+ return . dictionary( newDictionary)
164+ case . cid( let cid) :
165+ return . dictionary( [
166+ " $link " : . string( ( try ? cid. encode ( ) ) ?? " " )
167+ ] )
168+ case . bytes( let data) :
169+ return . dictionary( [
170+ " $bytes " : . string( data. base64EncodedString ( ) )
171+ ] )
172+ case . jsonValue( let json) :
173+ return json
174+ }
175+ }
211176}
0 commit comments