1 /**
2  Property wrapper for `Record`'s data members that takes part in the process of serialization to and deserialization from the dictionary.
3  */
4 @propertyWrapper
5 public final class Field<Type>: AnyFieldInternal {
6   /**
7    The wrapped value.
8    */
9   public var wrappedValue: Type
10 
11   private let fieldType: AnyDynamicType = ~Type.self
12 
13   /**
14    Field's key in the dictionary, which by default is a label of the wrapped property.
15    Sadly, property wrappers don't receive properties' label, so we must wait until it's assigned by `Record`.
16    */
17   internal var key: String? {
18     return options.first { $0.rawValue == FieldOption.keyed("").rawValue }?.key
19   }
20 
21   /**
22    Additional options of the field, such is if providing the value is required (`FieldOption.required`).
23    */
24   internal var options: Set<FieldOption> = Set()
25 
26   /**
27    Whether the generic field type accepts `nil` values.
28    */
29   internal var isOptional: Bool {
30     return fieldType is DynamicOptionalType
31   }
32 
33   internal var isRequired: Bool {
34     options.contains(.required)
35   }
36 
37   /**
38    Initializes the field with given value and customized key.
39    */
40   public init(wrappedValue: Type, _ options: FieldOption...) {
41     self.wrappedValue = wrappedValue
42     self.options = Set(options)
43   }
44 
45   /**
46    Alternative default initializer implementation. It's not possible yet to pass an array
47    as variadic arguments, so we also need to pass an array as a single argument.
48    */
49   public init(wrappedValue: Type, _ options: [FieldOption]) {
50     self.wrappedValue = wrappedValue
51     self.options = Set(options)
52   }
53 
54   /**
55    A hacky way to accept optionals without explicit assignment to `nil`. Normally, we would have to do
56    `@Field var s: String? = nil` but this init with generic constraint allows us to just do `@Field var s: String?`.
57    */
58   public init(wrappedValue: Type = nil) where Type: ExpressibleByNilLiteral {
59     self.wrappedValue = wrappedValue
60   }
61 
62   public init(wrappedValue: Type = nil, _ options: FieldOption...) where Type: ExpressibleByNilLiteral {
63     self.wrappedValue = wrappedValue
64     self.options = Set(options)
65   }
66 
67   /**
68    Returns wrapped value as `Any?` to conform to type-erased `AnyField` protocol.
69    */
getnull70   public func get() -> Any {
71     return wrappedValue
72   }
73 
74   /**
75    Sets the wrapped value with a value of `Any` type.
76    */
setnull77   internal func set(_ newValue: Any?) throws {
78     if newValue == nil && (!isOptional || isRequired) {
79       throw FieldRequiredException(key!)
80     }
81     do {
82       if let value = try fieldType.cast(newValue) as? Type {
83         wrappedValue = value
84       }
85     } catch {
86       throw FieldInvalidTypeException((fieldKey: key!, value: newValue, desiredType: Type.self)).causedBy(error)
87     }
88   }
89 }
90 
91 internal class FieldRequiredException: GenericException<String> {
92   override var reason: String {
93     "Value for field '\(param)' is required, got nil"
94   }
95 }
96 
97 internal class FieldInvalidTypeException: GenericException<(fieldKey: String, value: Any?, desiredType: Any.Type)> {
98   override var reason: String {
99     let value = String(describing: param.value ?? "null")
100     let desiredType = String(describing: param.desiredType)
101 
102     return "Cannot cast '\(value)' for field '\(param.fieldKey)' of type \(desiredType)"
103   }
104 }
105