I did this with a playground:
Given an enum
public struct Person {
var name: String
var lastName: String
}
public enum Foo {
case bar(Person)
}
Let's define the class CasePattern
. Please ignore current implementation. It was needed to make it compile.
public class CasePattern<Root, Value> {
public enum PatternError: Error {
case patternMismatch(String)
}
internal var get: (Root) throws -> Value
internal init(get: @escaping (Root) throws -> Value) {
self.get = get
}
func appending<AppendedValue>(path: KeyPath<Value, AppendedValue>) -> CasePattern<Root, AppendedValue> {
return CasePattern<Root, AppendedValue> { root throws -> AppendedValue in
(try self.get(root))[keyPath: path]
}
}
}
Now we extend the enum to expose CasePattern
s. Again, ignore the implementation. It was needed just to make it compile.
public extension CasePattern where Root == Foo {
static var bar: CasePattern<Root, Person> {
CasePattern<Root, Person> {
guard case let .bar(value) = $0 else {
throw PatternError.patternMismatch("\($0) does not match pattern .bar")
}
return value
}
}
}
Now this is possible:
extension Foo {
/// Not possible until https://github.com/apple/swift/pull/22749
// subscript<Value>(case pattern: CasePattern<Self, Value>)throws -> Value {
// try pattern.get(self)
// }
// Just to make it compile
subscript<Value>(case pattern: CasePattern<Self, Value>) -> () throws -> Value {
{ try pattern.get(self) }
}
}
let e = Foo.bar(Person(name: "David", lastName: "Bowie"))
let kp: KeyPath<Person, String> = \.name // regular keyPath for Person to name property
let cp: CasePattern<Foo, Person> = .bar // with the help of the compiler should be \.bar
let cpKp = cp.appending(path: kp) // With the help of the compiler should be \Foo.bar.name
try e[case: cpKp]() // prints "David"
CasePattern
and KeyPath
can be combined.