Last active
May 15, 2022 08:36
-
-
Save maximkrouk/eede7171952e044492c1fa57291bcf94 to your computer and use it in GitHub Desktop.
Functional generic builder
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// NOTE: Depends on Modifications.swift | |
// NOTE: Depends on BuildBlocks.swift | |
// NOTE: Depends on FunctionalKeyPath https://gist.github.com/maximkrouk/6287fb56321a21e8180d5fe044e642e4 | |
import Foundation | |
@dynamicMemberLookup | |
public struct Builder<Object> { | |
private var _build: () -> Object | |
public func build() -> Object { _build() } | |
public init(_ initialValue: Object) { self._build = { initialValue } } | |
@inlinable | |
public func setIf<Value>(_ condition: Bool, _ keyPath: WritableKeyPath<Object, Value>, _ value: @autoclosure () -> Value) -> Self { | |
if condition { return self.set(keyPath, value()) } | |
else { return self } | |
} | |
@inlinable | |
public func setIf(_ condition: Bool, _ transform: @escaping (inout Object) -> Void) -> Self { | |
if condition { return self.set(transform) } | |
else { return self } | |
} | |
@inlinable | |
public func set<Value>(_ keyPath: WritableKeyPath<Object, Value>, _ value: Value) -> Self { | |
self.set { object in | |
object[keyPath: keyPath] = value | |
} | |
} | |
public func set(_ transform: @escaping (inout Object) -> Void) -> Self { | |
modification(of: self) { _self in | |
_self._build = { | |
modification(of: build(), with: transform) | |
} | |
} | |
} | |
public subscript<T>(dynamicMember keyPath: WritableKeyPath<Object, T>) -> CallableBuildBlock<T> { | |
CallableBuildBlock( | |
base: BuildBlock( | |
builder: self, | |
keyPath: .init(keyPath) | |
) | |
) | |
} | |
public subscript<T>(dynamicMember keyPath: ReferenceWritableKeyPath<Object, T>) -> CallableBuildBlock<T> | |
where T: AnyObject { | |
CallableBuildBlock( | |
base: BuildBlock( | |
builder: self, | |
keyPath: .init(keyPath) | |
) | |
) | |
} | |
public subscript<T>(dynamicMember keyPath: KeyPath<Object, T>) -> BuildBlock<T> | |
where T: AnyObject { | |
BuildBlock<T>( | |
builder: self, | |
keyPath: FunctionalKeyPath( | |
embed: { value, root in root }, | |
extract: { root in root[keyPath: keyPath] } | |
) | |
) | |
} | |
} | |
public protocol BuilderProvider {} | |
extension BuilderProvider { | |
public var builder: Builder<Self> { .init(self) } | |
} | |
extension NSObject: BuilderProvider {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// NOTE: Depends on Modifications.swift | |
// NOTE: Depends on FunctionalKeyPath https://gist.github.com/maximkrouk/6287fb56321a21e8180d5fe044e642e4 | |
extension Builder { | |
@dynamicMemberLookup | |
public struct CallableBuildBlock<Value> { | |
var base: BuildBlock<Value> | |
public func callAsFunction(_ value: Value) -> Builder<Object> { | |
base.builder.set { object in | |
object = base.keyPath.embed(value, in: object) | |
} | |
} | |
public subscript<T>(dynamicMember keyPath: ReferenceWritableKeyPath<Value, T>) -> CallableBuildBlock<T> | |
where Value: AnyObject { base[dynamicMember: keyPath] } | |
public subscript<T>(dynamicMember keyPath: WritableKeyPath<Value, T>) -> CallableBuildBlock<T> { | |
base[dynamicMember: keyPath] | |
} | |
public subscript<U, T>(dynamicMember keyPath: WritableKeyPath<U, T>) -> CallableBuildBlock<T?> | |
where Value == Optional<U> { base[dynamicMember: keyPath] } | |
public subscript<T>(dynamicMember keyPath: KeyPath<Object, T>) -> BuildBlock<T> | |
where T: AnyObject { base[dynamicMember: keyPath] } | |
} | |
@dynamicMemberLookup | |
public struct BuildBlock<Value> { | |
internal init(builder: Builder<Object>, keyPath: FunctionalKeyPath<Object, Value>) { | |
self.builder = builder | |
self.keyPath = keyPath | |
} | |
var builder: Builder<Object> | |
var keyPath: FunctionalKeyPath<Object, Value> | |
public subscript<T>(dynamicMember keyPath: ReferenceWritableKeyPath<Value, T>) -> CallableBuildBlock<T> | |
where Value: AnyObject { | |
CallableBuildBlock<T>( | |
base: BuildBlock<T>( | |
builder: builder, | |
keyPath: self.keyPath.appending( | |
path: FunctionalKeyPath<Value, T>(keyPath) | |
) | |
) | |
) | |
} | |
public subscript<T>(dynamicMember keyPath: WritableKeyPath<Value, T>) -> CallableBuildBlock<T> { | |
CallableBuildBlock<T>( | |
base: BuildBlock<T>( | |
builder: builder, | |
keyPath: self.keyPath.appending( | |
path: FunctionalKeyPath<Value, T>(keyPath) | |
) | |
) | |
) | |
} | |
public subscript<U, T>(dynamicMember keyPath: WritableKeyPath<U, T>) -> CallableBuildBlock<T?> | |
where Value == Optional<U> { | |
CallableBuildBlock<T?>( | |
base: BuildBlock<T?>( | |
builder: builder, | |
keyPath: self.keyPath.appending( | |
path: FunctionalKeyPath<U?, T?>( | |
embed: { value, root in | |
modification(of: root) { root in | |
value.map { root?[keyPath: keyPath] = $0 } | |
} | |
}, | |
extract: { root in | |
root?[keyPath: keyPath] | |
} | |
) | |
) | |
) | |
) | |
} | |
public subscript<T>(dynamicMember keyPath: KeyPath<Object, T>) -> BuildBlock<T> | |
where T: AnyObject { | |
BuildBlock<T>( | |
builder: builder, | |
keyPath: .init( | |
embed: { value, root in root }, | |
extract: { root in root[keyPath: keyPath] } | |
) | |
) | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@inlinable | |
public func modification<Object>( | |
of object: Object, | |
transform: (inout Object) throws -> Void | |
) rethrows -> Object { | |
var object = object | |
try transform(&object) | |
return object | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
https://github.com/makeupstudio/swift-declarative-configuration
Dependencies
Usage
Back to index