import Foundation import UIKit public final class Empty: Equatable { static let shared: Empty = Empty() public static func ==(lhs: Empty, rhs: Empty) -> Bool { return true } } public class _Environment { fileprivate var data: [Int: _EnvironmentValue] = [:] var _isUpdated: Bool = false func calculateIsUpdated() -> Bool { if self._isUpdated { return true } for (_, item) in self.data { if let parentEnvironment = item.parentEnvironment, parentEnvironment.calculateIsUpdated() { return true } } return false } fileprivate func set(index: Int, value: EnvironmentValue) { if let current = self.data[index] { self.data[index] = value if current as! EnvironmentValue != value { self._isUpdated = true } } else { self.data[index] = value self._isUpdated = true } } } private enum EnvironmentValueStorage { case direct(T) case reference(_Environment, Int) } public class _EnvironmentValue { fileprivate let parentEnvironment: _Environment? fileprivate init(parentEnvironment: _Environment?) { self.parentEnvironment = parentEnvironment } } @dynamicMemberLookup public final class EnvironmentValue: _EnvironmentValue, Equatable { private var storage: EnvironmentValueStorage public var value: T { switch self.storage { case let .direct(value): return value case let .reference(environment, index): return (environment.data[index] as! EnvironmentValue).value } } fileprivate init(_ value: T) { self.storage = .direct(value) super.init(parentEnvironment: nil) } fileprivate init(environment: _Environment, index: Int) { self.storage = .reference(environment, index) super.init(parentEnvironment: environment) } public static func ==(lhs: EnvironmentValue, rhs: EnvironmentValue) -> Bool { if lhs === rhs { return true } // TODO: follow the reference chain for faster equality checking return lhs.value == rhs.value } public subscript(dynamicMember keyPath: KeyPath) -> V { return self.value[keyPath: keyPath] } } public class Environment: _Environment { private let file: StaticString private let line: Int public init(_ file: StaticString = #file, _ line: Int = #line) { self.file = file self.line = line } } public extension Environment where T == Empty { static let value: Environment = { let result = Environment() result.set(index: 0, value: EnvironmentValue(Empty())) return result }() } public extension Environment { subscript(_ t1: T.Type) -> EnvironmentValue where T: Equatable { return EnvironmentValue(environment: self, index: 0) } subscript(_ t1: T1.Type) -> EnvironmentValue where T == (T1, T2), T1: Equatable, T2: Equatable { return EnvironmentValue(environment: self, index: 0) } subscript(_ t2: T2.Type) -> EnvironmentValue where T == (T1, T2), T1: Equatable, T2: Equatable { return EnvironmentValue(environment: self, index: 1) } subscript(_ t1: T1.Type) -> EnvironmentValue where T == (T1, T2, T3), T1: Equatable, T2: Equatable, T3: Equatable { return EnvironmentValue(environment: self, index: 0) } subscript(_ t2: T2.Type) -> EnvironmentValue where T == (T1, T2, T3), T1: Equatable, T2: Equatable, T3: Equatable { return EnvironmentValue(environment: self, index: 1) } subscript(_ t3: T3.Type) -> EnvironmentValue where T == (T1, T2, T3), T1: Equatable, T2: Equatable, T3: Equatable { return EnvironmentValue(environment: self, index: 2) } subscript(_ t1: T1.Type) -> EnvironmentValue where T == (T1, T2, T3, T4), T1: Equatable, T2: Equatable, T3: Equatable, T4: Equatable { return EnvironmentValue(environment: self, index: 0) } subscript(_ t2: T2.Type) -> EnvironmentValue where T == (T1, T2, T3, T4), T1: Equatable, T2: Equatable, T3: Equatable, T4: Equatable { return EnvironmentValue(environment: self, index: 1) } subscript(_ t3: T3.Type) -> EnvironmentValue where T == (T1, T2, T3, T4), T1: Equatable, T2: Equatable, T3: Equatable, T4: Equatable { return EnvironmentValue(environment: self, index: 2) } subscript(_ t4: T4.Type) -> EnvironmentValue where T == (T1, T2, T3, T4), T1: Equatable, T2: Equatable, T3: Equatable, T4: Equatable { return EnvironmentValue(environment: self, index: 3) } } @resultBuilder public struct EnvironmentBuilder { static var _environment: _Environment? private static func current(_ type: T.Type) -> Environment { return self._environment as! Environment } public struct Partial { fileprivate var value: EnvironmentValue } public static func buildBlock() -> Environment { let result = self.current(Empty.self) result.set(index: 0, value: EnvironmentValue(Empty.shared)) return result } public static func buildExpression(_ expression: T) -> Partial { return Partial(value: EnvironmentValue(expression)) } public static func buildExpression(_ expression: EnvironmentValue) -> Partial { return Partial(value: EnvironmentValue(expression.value)) } public static func buildBlock(_ t1: Partial) -> Environment { let result = self.current(T1.self) result.set(index: 0, value: t1.value) return result } public static func buildBlock(_ t1: Partial, _ t2: Partial) -> Environment<(T1, T2)> { let result = self.current((T1, T2).self) result.set(index: 0, value: t1.value) result.set(index: 1, value: t2.value) return result } public static func buildBlock(_ t1: Partial, _ t2: Partial, _ t3: Partial) -> Environment<(T1, T2, T3)> { let result = self.current((T1, T2, T3).self) result.set(index: 0, value: t1.value) result.set(index: 1, value: t2.value) result.set(index: 2, value: t3.value) return result } public static func buildBlock(_ t1: Partial, _ t2: Partial, _ t3: Partial, _ t4: Partial) -> Environment<(T1, T2, T3, T4)> { let result = self.current((T1, T2, T3, T4).self) result.set(index: 0, value: t1.value) result.set(index: 1, value: t2.value) result.set(index: 2, value: t3.value) result.set(index: 3, value: t4.value) return result } } @propertyWrapper public struct ZeroEquatable: Equatable { public var wrappedValue: T public init(_ wrappedValue: T) { self.wrappedValue = wrappedValue } public static func ==(lhs: ZeroEquatable, rhs: ZeroEquatable) -> Bool { return true } }