|  |  |  | // Copyright © 2022 Rangeproof Pty Ltd. All rights reserved. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import Foundation | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | public enum Updatable<Wrapped>: ExpressibleByNilLiteral { | 
					
						
							|  |  |  |     /// A cleared value. | 
					
						
							|  |  |  |     /// | 
					
						
							|  |  |  |     /// In code, the cleared of a value is typically written using the `nil` | 
					
						
							|  |  |  |     /// literal rather than the explicit `.remove` enumeration case. | 
					
						
							|  |  |  |     case remove | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     /// The existing value, this will leave whatever value is currently available. | 
					
						
							|  |  |  |     case existing | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /// An updated value, stored as `Wrapped`. | 
					
						
							|  |  |  |     case update(Wrapped) | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     // MARK: - ExpressibleByNilLiteral | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     public init(nilLiteral: ()) { | 
					
						
							|  |  |  |         self = .remove | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     public static func updateIf(_ maybeValue: Wrapped?) -> Updatable<Wrapped> { | 
					
						
							|  |  |  |         switch maybeValue { | 
					
						
							|  |  |  |             case .some(let value): return .update(value) | 
					
						
							|  |  |  |             default: return .existing | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     public static func updateTo(_ maybeValue: Wrapped?) -> Updatable<Wrapped> { | 
					
						
							|  |  |  |         switch maybeValue { | 
					
						
							|  |  |  |             case .some(let value): return .update(value) | 
					
						
							|  |  |  |             default: return .remove | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     // MARK: - Functions | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     public func value(existing: Wrapped) -> Wrapped? { | 
					
						
							|  |  |  |         switch self { | 
					
						
							|  |  |  |             case .remove: return nil | 
					
						
							|  |  |  |             case .existing: return existing | 
					
						
							|  |  |  |             case .update(let newValue): return newValue | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     public func value(existing: Wrapped) -> Wrapped { | 
					
						
							|  |  |  |         switch self { | 
					
						
							|  |  |  |             case .remove: fatalError("Attempted to assign a 'removed' value to a non-null") | 
					
						
							|  |  |  |             case .existing: return existing | 
					
						
							|  |  |  |             case .update(let newValue): return newValue | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // MARK: - Coalesing-nil operator | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | public func ?? <T>(updatable: Updatable<T>, existingValue: @autoclosure () throws -> T) rethrows -> T { | 
					
						
							|  |  |  |     switch updatable { | 
					
						
							|  |  |  |         case .remove: fatalError("Attempted to assign a 'removed' value to a non-null") | 
					
						
							|  |  |  |         case .existing: return try existingValue() | 
					
						
							|  |  |  |         case .update(let newValue): return newValue | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | public func ?? <T>(updatable: Updatable<T>, existingValue: @autoclosure () throws -> T?) rethrows -> T? { | 
					
						
							|  |  |  |     switch updatable { | 
					
						
							|  |  |  |         case .remove: return nil | 
					
						
							|  |  |  |         case .existing: return try existingValue() | 
					
						
							|  |  |  |         case .update(let newValue): return newValue | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | public func ?? <T>(updatable: Updatable<Optional<T>>, existingValue: @autoclosure () throws -> T?) rethrows -> T? { | 
					
						
							|  |  |  |     switch updatable { | 
					
						
							|  |  |  |         case .remove: return nil | 
					
						
							|  |  |  |         case .existing: return try existingValue() | 
					
						
							|  |  |  |         case .update(let newValue): return newValue | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // MARK: - ExpressibleBy Conformance | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | extension Updatable { | 
					
						
							|  |  |  |     public init(_ value: Wrapped) { | 
					
						
							|  |  |  |         self = .update(value) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | extension Updatable: ExpressibleByUnicodeScalarLiteral, ExpressibleByExtendedGraphemeClusterLiteral, ExpressibleByStringLiteral where Wrapped == String { | 
					
						
							|  |  |  |     public init(stringLiteral value: Wrapped) { | 
					
						
							|  |  |  |         self = .update(value) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     public init(extendedGraphemeClusterLiteral value: Wrapped) { | 
					
						
							|  |  |  |         self = .update(value) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     public init(unicodeScalarLiteral value: Wrapped) { | 
					
						
							|  |  |  |         self = .update(value) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | extension Updatable: ExpressibleByIntegerLiteral where Wrapped == Int { | 
					
						
							|  |  |  |     public init(integerLiteral value: Int) { | 
					
						
							|  |  |  |         self = .update(value) | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | extension Updatable: ExpressibleByFloatLiteral where Wrapped == Double { | 
					
						
							|  |  |  |     public init(floatLiteral value: Double) { | 
					
						
							|  |  |  |         self = .update(value) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | extension Updatable: ExpressibleByBooleanLiteral where Wrapped == Bool { | 
					
						
							|  |  |  |     public init(booleanLiteral value: Bool) { | 
					
						
							|  |  |  |         self = .update(value) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } |