damus

nostr ios client
git clone git://jb55.com/damus
Log | Files | Refs | README | LICENSE

AnyEncodable.swift (9284B)


      1 #if canImport(Foundation)
      2 import Foundation
      3 #endif
      4 
      5 /**
      6  A type-erased `Encodable` value.
      7 
      8  The `AnyEncodable` type forwards encoding responsibilities
      9  to an underlying value, hiding its specific underlying type.
     10 
     11  You can encode mixed-type values in dictionaries
     12  and other collections that require `Encodable` conformance
     13  by declaring their contained type to be `AnyEncodable`:
     14 
     15      let dictionary: [String: AnyEncodable] = [
     16          "boolean": true,
     17          "integer": 42,
     18          "double": 3.141592653589793,
     19          "string": "string",
     20          "array": [1, 2, 3],
     21          "nested": [
     22              "a": "alpha",
     23              "b": "bravo",
     24              "c": "charlie"
     25          ],
     26          "null": nil
     27      ]
     28 
     29      let encoder = JSONEncoder()
     30      let json = try! encoder.encode(dictionary)
     31  */
     32 @frozen public struct AnyEncodable: Encodable {
     33     public let value: Any
     34 
     35     public init<T>(_ value: T?) {
     36         self.value = value ?? ()
     37     }
     38 }
     39 
     40 @usableFromInline
     41 protocol _AnyEncodable {
     42     var value: Any { get }
     43     init<T>(_ value: T?)
     44 }
     45 
     46 extension AnyEncodable: _AnyEncodable {}
     47 
     48 // MARK: - Encodable
     49 
     50 extension _AnyEncodable {
     51     public func encode(to encoder: Encoder) throws {
     52         var container = encoder.singleValueContainer()
     53 
     54         switch value {
     55         #if canImport(Foundation)
     56         case is NSNull:
     57             try container.encodeNil()
     58         #endif
     59         case is Void:
     60             try container.encodeNil()
     61         case let bool as Bool:
     62             try container.encode(bool)
     63         case let int as Int:
     64             try container.encode(int)
     65         case let int8 as Int8:
     66             try container.encode(int8)
     67         case let int16 as Int16:
     68             try container.encode(int16)
     69         case let int32 as Int32:
     70             try container.encode(int32)
     71         case let int64 as Int64:
     72             try container.encode(int64)
     73         case let uint as UInt:
     74             try container.encode(uint)
     75         case let uint8 as UInt8:
     76             try container.encode(uint8)
     77         case let uint16 as UInt16:
     78             try container.encode(uint16)
     79         case let uint32 as UInt32:
     80             try container.encode(uint32)
     81         case let uint64 as UInt64:
     82             try container.encode(uint64)
     83         case let float as Float:
     84             try container.encode(float)
     85         case let double as Double:
     86             try container.encode(double)
     87         case let string as String:
     88             try container.encode(string)
     89         #if canImport(Foundation)
     90         case let number as NSNumber:
     91             try encode(nsnumber: number, into: &container)
     92         case let date as Date:
     93             try container.encode(date)
     94         case let profile_url as URL:
     95             try container.encode(profile_url)
     96         #endif
     97         case let array as [Any?]:
     98             try container.encode(array.map { AnyEncodable($0) })
     99         case let dictionary as [String: Any?]:
    100             try container.encode(dictionary.mapValues { AnyEncodable($0) })
    101         case let encodable as Encodable:
    102             try encodable.encode(to: encoder)
    103         default:
    104             let context = EncodingError.Context(codingPath: container.codingPath, debugDescription: "AnyEncodable value cannot be encoded")
    105             throw EncodingError.invalidValue(value, context)
    106         }
    107     }
    108 
    109     #if canImport(Foundation)
    110     private func encode(nsnumber: NSNumber, into container: inout SingleValueEncodingContainer) throws {
    111         switch Character(Unicode.Scalar(UInt8(nsnumber.objCType.pointee)))  {
    112         case "B":
    113             try container.encode(nsnumber.boolValue)
    114         case "c":
    115             try container.encode(nsnumber.int8Value)
    116         case "s":
    117             try container.encode(nsnumber.int16Value)
    118         case "i", "l":
    119             try container.encode(nsnumber.int32Value)
    120         case "q":
    121             try container.encode(nsnumber.int64Value)
    122         case "C":
    123             try container.encode(nsnumber.uint8Value)
    124         case "S":
    125             try container.encode(nsnumber.uint16Value)
    126         case "I", "L":
    127             try container.encode(nsnumber.uint32Value)
    128         case "Q":
    129             try container.encode(nsnumber.uint64Value)
    130         case "f":
    131             try container.encode(nsnumber.floatValue)
    132         case "d":
    133             try container.encode(nsnumber.doubleValue)
    134         default:
    135             let context = EncodingError.Context(codingPath: container.codingPath, debugDescription: "NSNumber cannot be encoded because its type is not handled")
    136             throw EncodingError.invalidValue(nsnumber, context)
    137         }
    138     }
    139     #endif
    140 }
    141 
    142 extension AnyEncodable: Equatable {
    143     public static func == (lhs: AnyEncodable, rhs: AnyEncodable) -> Bool {
    144         switch (lhs.value, rhs.value) {
    145         case is (Void, Void):
    146             return true
    147         case let (lhs as Bool, rhs as Bool):
    148             return lhs == rhs
    149         case let (lhs as Int, rhs as Int):
    150             return lhs == rhs
    151         case let (lhs as Int8, rhs as Int8):
    152             return lhs == rhs
    153         case let (lhs as Int16, rhs as Int16):
    154             return lhs == rhs
    155         case let (lhs as Int32, rhs as Int32):
    156             return lhs == rhs
    157         case let (lhs as Int64, rhs as Int64):
    158             return lhs == rhs
    159         case let (lhs as UInt, rhs as UInt):
    160             return lhs == rhs
    161         case let (lhs as UInt8, rhs as UInt8):
    162             return lhs == rhs
    163         case let (lhs as UInt16, rhs as UInt16):
    164             return lhs == rhs
    165         case let (lhs as UInt32, rhs as UInt32):
    166             return lhs == rhs
    167         case let (lhs as UInt64, rhs as UInt64):
    168             return lhs == rhs
    169         case let (lhs as Float, rhs as Float):
    170             return lhs == rhs
    171         case let (lhs as Double, rhs as Double):
    172             return lhs == rhs
    173         case let (lhs as String, rhs as String):
    174             return lhs == rhs
    175         case let (lhs as [String: AnyEncodable], rhs as [String: AnyEncodable]):
    176             return lhs == rhs
    177         case let (lhs as [AnyEncodable], rhs as [AnyEncodable]):
    178             return lhs == rhs
    179         default:
    180             return false
    181         }
    182     }
    183 }
    184 
    185 extension AnyEncodable: CustomStringConvertible {
    186     public var description: String {
    187         switch value {
    188         case is Void:
    189             return String(describing: nil as Any?)
    190         case let value as CustomStringConvertible:
    191             return value.description
    192         default:
    193             return String(describing: value)
    194         }
    195     }
    196 }
    197 
    198 extension AnyEncodable: CustomDebugStringConvertible {
    199     public var debugDescription: String {
    200         switch value {
    201         case let value as CustomDebugStringConvertible:
    202             return "AnyEncodable(\(value.debugDescription))"
    203         default:
    204             return "AnyEncodable(\(description))"
    205         }
    206     }
    207 }
    208 
    209 extension AnyEncodable: ExpressibleByNilLiteral {}
    210 extension AnyEncodable: ExpressibleByBooleanLiteral {}
    211 extension AnyEncodable: ExpressibleByIntegerLiteral {}
    212 extension AnyEncodable: ExpressibleByFloatLiteral {}
    213 extension AnyEncodable: ExpressibleByStringLiteral {}
    214 extension AnyEncodable: ExpressibleByStringInterpolation {}
    215 extension AnyEncodable: ExpressibleByArrayLiteral {}
    216 extension AnyEncodable: ExpressibleByDictionaryLiteral {}
    217 
    218 extension _AnyEncodable {
    219     public init(nilLiteral _: ()) {
    220         self.init(nil as Any?)
    221     }
    222 
    223     public init(booleanLiteral value: Bool) {
    224         self.init(value)
    225     }
    226 
    227     public init(integerLiteral value: Int) {
    228         self.init(value)
    229     }
    230 
    231     public init(floatLiteral value: Double) {
    232         self.init(value)
    233     }
    234 
    235     public init(extendedGraphemeClusterLiteral value: String) {
    236         self.init(value)
    237     }
    238 
    239     public init(stringLiteral value: String) {
    240         self.init(value)
    241     }
    242 
    243     public init(arrayLiteral elements: Any...) {
    244         self.init(elements)
    245     }
    246 
    247     public init(dictionaryLiteral elements: (AnyHashable, Any)...) {
    248         self.init([AnyHashable: Any](elements, uniquingKeysWith: { first, _ in first }))
    249     }
    250 }
    251 
    252 extension AnyEncodable: Hashable {
    253     public func hash(into hasher: inout Hasher) {
    254         switch value {
    255         case let value as Bool:
    256             hasher.combine(value)
    257         case let value as Int:
    258             hasher.combine(value)
    259         case let value as Int8:
    260             hasher.combine(value)
    261         case let value as Int16:
    262             hasher.combine(value)
    263         case let value as Int32:
    264             hasher.combine(value)
    265         case let value as Int64:
    266             hasher.combine(value)
    267         case let value as UInt:
    268             hasher.combine(value)
    269         case let value as UInt8:
    270             hasher.combine(value)
    271         case let value as UInt16:
    272             hasher.combine(value)
    273         case let value as UInt32:
    274             hasher.combine(value)
    275         case let value as UInt64:
    276             hasher.combine(value)
    277         case let value as Float:
    278             hasher.combine(value)
    279         case let value as Double:
    280             hasher.combine(value)
    281         case let value as String:
    282             hasher.combine(value)
    283         case let value as [String: AnyEncodable]:
    284             hasher.combine(value)
    285         case let value as [AnyEncodable]:
    286             hasher.combine(value)
    287         default:
    288             break
    289         }
    290     }
    291 }