Skip to content

Commit

Permalink
CasePath.modify (#50)
Browse files Browse the repository at this point in the history
  • Loading branch information
stephencelis authored Jul 29, 2021
1 parent 40072a9 commit d226d16
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 3 deletions.
16 changes: 15 additions & 1 deletion Sources/CasePaths/CasePath.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,23 @@ public struct CasePath<Root, Value> {
/// Attempts to extract a value from a root.
///
/// - Parameter root: A root to extract from.
/// - Returns: A value iff it can be extracted from the given root, otherwise `nil`.
/// - Returns: A value if it can be extracted from the given root, otherwise `nil`.
public func extract(from root: Root) -> Value? {
self._extract(root)
}

/// Attempts to modify a value in a root.
///
/// - Parameters:
/// - root: A root to modify if the case path matches.
/// - update: A closure that can mutate the case's associated value. If the closure throws, the
/// root will be left unmodified.
public func modify(_ root: inout Root, _ update: (inout Value) throws -> Void) throws {
guard var value = self.extract(from: root) else { throw ExtractionFailed() }
try update(&value)
root = self.embed(value)
}

/// Returns a new case path created by appending the given case path to this one.
///
/// Use this method to extend this case path to the value type of another case path.
Expand All @@ -53,3 +65,5 @@ extension CasePath: CustomStringConvertible {
"CasePath<\(Root.self), \(Value.self)>"
}
}

struct ExtractionFailed: Error {}
4 changes: 2 additions & 2 deletions Sources/CasePaths/EnumReflection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ extension CasePath where Value == Void {
/// - Parameters:
/// - embed: An enum case initializer.
/// - root: A root enum value.
/// - Returns: Values iff they can be extracted from the given enum case initializer and root enum,
/// - Returns: Values if they can be extracted from the given enum case initializer and root enum,
/// otherwise `nil`.
@available(
*, deprecated,
Expand All @@ -70,7 +70,7 @@ public func extract<Root, Value>(case embed: @escaping (Value) -> Root, from roo
/// - Parameters:
/// - embed: An enum case initializer.
/// - root: A root enum value.
/// - Returns: Values iff they can be extracted from the given enum case initializer and root enum,
/// - Returns: Values if they can be extracted from the given enum case initializer and root enum,
/// otherwise `nil`.
@available(
*, deprecated,
Expand Down
7 changes: 7 additions & 0 deletions Tests/CasePathsTests/CasePathsTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -910,6 +910,13 @@ final class CasePathsTests: XCTestCase {

XCTAssertNil((/Action.child1).extract(from: .child2(.b)))
}

func testModify() throws {
enum Foo: Equatable { case bar(Int) }
var foo = Foo.bar(42)
try (/Foo.bar).modify(&foo) { $0 *= 2 }
XCTAssertEqual(foo, .bar(84))
}
}

private class TestObject: Equatable {
Expand Down

0 comments on commit d226d16

Please sign in to comment.