diff --git a/Sources/KeyPath.swift b/Sources/KeyPath.swift index 698d07b..1208aad 100644 --- a/Sources/KeyPath.swift +++ b/Sources/KeyPath.swift @@ -4,7 +4,7 @@ import Foundation /// Simple struct used to represent multiple segments of a string. /// This is a utility used to recursively access values in nested dictionaries. -public struct KeyPath { +public struct KeyPath: Hashable { public var segments: [String] public var isEmpty: Bool { return segments.isEmpty } @@ -41,6 +41,18 @@ extension KeyPath: ExpressibleByStringLiteral { } } +extension KeyPath: CustomStringConvertible { + public var description: String { + return segments.joined(separator: ".") + } +} + +public extension KeyPath { + static func + (lhs: KeyPath, rhs: KeyPath) -> KeyPath { + return KeyPath(lhs.description + "." + rhs.description) + } +} + public extension Dictionary where Key == String { subscript(keyPath keyPath: KeyPath) -> Any? { get { diff --git a/Tests/KeyPathTests.swift b/Tests/KeyPathTests.swift index 739a660..b5ab53b 100644 --- a/Tests/KeyPathTests.swift +++ b/Tests/KeyPathTests.swift @@ -74,4 +74,22 @@ final class KeyPathTests : XCTestCase { let keyPath = KeyPath(path) XCTAssertEqual(keyPath.path, path) } + + func test_description_whenSegmentIsEmpty_shouldReturnEmptyString() { + XCTAssertEqual(KeyPath(""), "") + } + + func test_description_whenSegmentHasOneElement_shouldReturnThatElement() { + XCTAssertEqual(KeyPath("element"), "element") + } + + func test_description_whenSegmentHasTwoElements_shouldReturnStringSeparatedByPeriod() { + XCTAssertEqual(KeyPath("my.name"), "my.name") + } + + func test_pathAppending_whenPathIsValid_shouldReturnAppendedPathsUsingPeriod() { + let lhs = "this.is.path.start" + let rhs = "this.is.path.end" + XCTAssertEqual(KeyPath(lhs) + KeyPath(rhs), "this.is.path.start.this.is.path.end") + } }