Created
August 20, 2020 19:33
-
-
Save KaQuMiQ/49ca4c268e87b004a3364d8a1dfe0ce6 to your computer and use it in GitHub Desktop.
Aegithalos 2.0
This file contains hidden or 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
| public struct Modifier<Subject, Failure: Error> { | |
| @usableFromInline internal let modifier: (Subject, ParameterStore) -> Result<Subject, Failure> | |
| } | |
| // MARK: - init | |
| public extension Modifier { | |
| init(_ modifier: @escaping (Subject, ParameterStore) -> Result<Subject, Failure>) { | |
| self.modifier = modifier | |
| } | |
| init(_ modifier: @escaping (Subject, ParameterStore) -> Subject) { | |
| self.modifier = { subject, parameters in .success(modifier(subject, parameters)) } | |
| } | |
| init(_ modifier: @escaping (Subject) -> Result<Subject, Failure>) { | |
| self.modifier = { subject, _ in modifier(subject) } | |
| } | |
| init(_ modifier: @escaping (Subject) -> Subject) { | |
| self.modifier = { subject, _ in .success(modifier(subject)) } | |
| } | |
| init(_ modifiers: Modifier...) { | |
| self.modifier = { subject, parameters in | |
| var subject = subject | |
| for modifier in modifiers { | |
| switch modifier.modifier(subject, parameters) { | |
| case let .success(modifiedSubject): | |
| subject = modifiedSubject | |
| case let .failure(error): | |
| return .failure(error) | |
| } | |
| } | |
| return .success(subject) | |
| } | |
| } | |
| } | |
| public extension Modifier where Subject: AnyObject { | |
| init(_ modifier: @escaping (Subject, ParameterStore) -> Void) { | |
| self.modifier = { subject, parameters in | |
| modifier(subject, parameters) | |
| return .success(subject) | |
| } | |
| } | |
| init(_ modifier: @escaping (Subject) -> Void) { | |
| self.modifier = { subject, _ in | |
| modifier(subject) | |
| return .success(subject) | |
| } | |
| } | |
| } | |
| public extension Modifier where Failure == Error { | |
| init(_ modifier: @escaping (Subject, ParameterStore) throws -> Subject) { | |
| self.modifier = { subject, parameters in | |
| do { | |
| return try .success(modifier(subject, parameters)) | |
| } catch { | |
| return .failure(error) | |
| } | |
| } | |
| } | |
| init(_ modifier: @escaping (Subject) throws -> Subject) { | |
| self.modifier = { subject, _ in | |
| do { | |
| return try .success(modifier(subject)) | |
| } catch { | |
| return .failure(error) | |
| } | |
| } | |
| } | |
| } | |
| // MARK: - apply | |
| public extension Modifier { | |
| @inlinable func apply( | |
| on subject: Subject, | |
| using parameters: ParameterStore = Parameters() | |
| ) -> Result<Subject, Failure> { | |
| modifier(subject, parameters) | |
| } | |
| } | |
| public extension Modifier where Subject: AnyObject { | |
| @inlinable func apply( | |
| on subject: Subject, | |
| using parameters: ParameterStore = Parameters() | |
| ) { | |
| _ = modifier(subject, parameters) | |
| } | |
| } | |
| public extension Modifier where Failure == Never { | |
| @inlinable func apply( | |
| on subject: Subject, | |
| using parameters: ParameterStore = Parameters() | |
| ) -> Subject { | |
| switch modifier(subject, parameters) { | |
| case let .success(subject): | |
| return subject | |
| case let .failure(never): | |
| switch never {} // it never fails since Failure type is Never | |
| } | |
| } | |
| } | |
| // MARK: - combine | |
| public extension Modifier { | |
| @inlinable func combine( | |
| _ other: @escaping (Subject, ParameterStore) -> Result<Subject, Failure> | |
| ) -> Self { | |
| Self(self, Self(other)) | |
| } | |
| @inlinable func combine( | |
| _ other: @escaping (Subject) -> Result<Subject, Failure> | |
| ) -> Self { | |
| Self(self, Self(other)) | |
| } | |
| @inlinable func combine( | |
| _ other: @escaping (Subject, ParameterStore) -> Subject | |
| ) -> Self { | |
| Self(self, Self(other)) | |
| } | |
| @inlinable func combine(_ other: @escaping (Subject) -> Subject) -> Self { | |
| Self(self, Self(other)) | |
| } | |
| @inlinable func combine(_ other: Self) -> Self { | |
| Self(self, Self(other)) | |
| } | |
| } | |
| public extension Modifier where Subject: AnyObject { | |
| @inlinable func combine(_ other: @escaping (Subject, ParameterStore) -> Void) -> Self { | |
| Self(self, Self(other)) | |
| } | |
| @inlinable func combine(_ other: @escaping (Subject) -> Void) -> Self { | |
| Self(self, Self(other)) | |
| } | |
| } | |
| public extension Modifier where Failure == Error { | |
| @inlinable func combine(_ other: @escaping (Subject, ParameterStore) throws -> Subject) -> Self { | |
| Self(self, Self(other)) | |
| } | |
| @inlinable func combine(_ other: @escaping (Subject) throws -> Subject) -> Self { | |
| Self(self, Self(other)) | |
| } | |
| } | |
| // MARK: - parameters | |
| public extension Modifier { | |
| @inlinable static func parameter<P>( | |
| _ parameter: P.Type, | |
| _ modifier: @escaping (P.Value) -> Modifier | |
| ) -> Modifier where P: Parameter { | |
| Self({ (subject: Subject, parameters: ParameterStore) -> Result<Subject, Failure> in | |
| modifier(parameters[parameter]).modifier(subject, parameters) | |
| }) | |
| } | |
| @inlinable func parameter<P>( | |
| _ parameter: P.Type, | |
| _ modifier: @escaping (P.Value) -> Self | |
| ) -> Self where P: Parameter { | |
| Self(self, .parameter(parameter, modifier)) | |
| } | |
| } | |
| // MARK: - transform | |
| public extension Modifier { | |
| @inlinable func contramap<OtherSubject>( | |
| _ keyPath: WritableKeyPath<OtherSubject, Subject> | |
| ) -> Modifier<OtherSubject, Failure> { | |
| Modifier<OtherSubject, Failure>({ otherSubject, parameters in | |
| self.modifier(otherSubject[keyPath: keyPath], parameters) | |
| .map { subject in | |
| var other = otherSubject | |
| other[keyPath: keyPath] = subject | |
| return other | |
| } | |
| }) | |
| } | |
| @inlinable func mapFailure<OtherFailure: Error>( | |
| _ transform: @escaping (Failure) -> OtherFailure | |
| ) -> Modifier<Subject, OtherFailure> { | |
| Modifier<Subject, OtherFailure>({ otherSubject, parameters in | |
| self.modifier(otherSubject, parameters) | |
| .mapError(transform) | |
| }) | |
| } | |
| @inlinable func mapFailure<OtherFailure: Error>( | |
| _ transform: @escaping (Failure, ParameterStore) -> OtherFailure | |
| ) -> Modifier<Subject, OtherFailure> { | |
| Modifier<Subject, OtherFailure>({ otherSubject, parameters in | |
| self.modifier(otherSubject, parameters) | |
| .mapError { error in | |
| transform(error, parameters) | |
| } | |
| }) | |
| } | |
| } | |
| public extension Modifier where Subject: AnyObject { | |
| @inlinable func contramap<OtherSubject>( | |
| _ transform: @escaping (OtherSubject) -> Subject | |
| ) -> Modifier<OtherSubject, Failure> { | |
| Modifier<OtherSubject, Failure>({ (otherSubject: OtherSubject, parameters: ParameterStore) -> Result<OtherSubject, Failure> in | |
| self.modifier(transform(otherSubject), parameters) | |
| .map { _ in otherSubject } | |
| }) | |
| } | |
| } | |
| // MARK: - replace | |
| public extension Modifier { | |
| @inlinable static func replace(with subject: Subject) -> Self { | |
| Self({ _, _ in .success(subject) }) | |
| } | |
| @inlinable static func replace(with failure: Failure) -> Self { | |
| Self({ _, _ in .failure(failure) }) | |
| } | |
| @inlinable func replaceFailure(with subject: Subject) -> Modifier<Subject, Never> { | |
| Modifier<Subject, Never>({ otherSubject, parameters in | |
| self.modifier(otherSubject, parameters) | |
| .flatMapError { _ in .success(subject) } | |
| }) | |
| } | |
| } |
This file contains hidden or 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
| public protocol Parameter { | |
| associatedtype Value | |
| static var defaultValue: Value { get } | |
| } | |
| internal extension Parameter { | |
| static var identifier: ObjectIdentifier { ObjectIdentifier(Self.Type.self) } | |
| } |
This file contains hidden or 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
| public struct Parameters { | |
| internal var parameters: Dictionary<ObjectIdentifier, Any> = [:] | |
| public init() {} | |
| } | |
| extension Parameters: ParameterStore { | |
| public subscript<P: Parameter>(parameter: P.Type) -> P.Value { | |
| get { parameters[parameter.identifier] as? P.Value ?? P.defaultValue } | |
| set { parameters[parameter.identifier] = newValue } | |
| } | |
| } |
This file contains hidden or 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
| public protocol ParameterStore { | |
| subscript<P: Parameter>(parameter: P.Type) -> P.Value { get set } | |
| } | |
| public extension ParameterStore { | |
| mutating func reset<P: Parameter>(parameter: P.Type) { | |
| self[parameter] = parameter.defaultValue | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment