diff --git a/Sources/Branches/Branch.swift b/Sources/Branches/Branch.swift index f1902761..44918180 100644 --- a/Sources/Branches/Branch.swift +++ b/Sources/Branches/Branch.swift @@ -1,27 +1,6 @@ import Core import Node -//extension Branch { -// /** -// It is not uncommon to place slugs along our branches representing keys that will -// match for the path given. When this happens, the path can be laid across here to extract -// slug values efficiently. -// -// Branches: `path/to/:name` -// Given Path: `path/to/joe` -// -// let slugs = branch.slugs(for: givenPath) // ["name": "joe"] -// */ -// public func slugs(for path: [String]) -> Node { -// var slugs: [String: Node] = [:] -// slugIndexes.forEach { key, index in -// guard let val = path[safe: index].flatMap({ $0.removingPercentEncoding }) else { return } -// slugs[key] = Node.string(val) -// } -// return Node.object(slugs) -// } -//} - /** When routing requests, different branches will be established, in a linked list style stemming from their host and request method. diff --git a/Sources/Routing/Parameters.swift b/Sources/Routing/Parameters.swift index fc540d0c..f41e4551 100644 --- a/Sources/Routing/Parameters.swift +++ b/Sources/Routing/Parameters.swift @@ -19,12 +19,18 @@ public struct Parameters: StructuredDataWrapper { } extension Parameters { - public func next(_ p: P.Type = P.self) throws -> P { - let param = self[P.uniqueSlug] - guard let next = param?.array?.first?.string ?? param?.string else { - throw ParametersError.noMoreParametersFound(forKey: P.uniqueSlug) - } - return try P.make(for: next) + public mutating func next(_ p: P.Type = P.self) throws -> P { + let error = ParametersError.noMoreParametersFound(forKey: P.uniqueSlug) + guard let param = self[P.uniqueSlug] else { throw error } + + var array = param.array ?? [param] + guard !array.isEmpty else { throw error } + + let rawValue = array.remove(at: 0) + guard let value = rawValue.string else { throw error } + + self[P.uniqueSlug] = .array(array) + return try P.make(for: value) } } diff --git a/Tests/BranchesTests/BranchTests.swift b/Tests/BranchesTests/BranchTests.swift index 0ea56006..8d94233b 100644 --- a/Tests/BranchesTests/BranchTests.swift +++ b/Tests/BranchesTests/BranchTests.swift @@ -6,8 +6,6 @@ class BranchTests: XCTestCase { ("testSimple", testSimple), ("testWildcard", testWildcard), ("testWildcardTrailing", testWildcardTrailing), - ("testParams", testParams), - ("testOutOfBoundsParams", testOutOfBoundsParams), ("testLeadingPath", testLeadingPath), ("testEmpty", testEmpty) ] @@ -37,38 +35,6 @@ class BranchTests: XCTestCase { XCTAssert(result.output == "abc") } - func testParams() { - let base = Branch(name: "[base]", output: nil) - base.extend([":a", ":b", ":c", "*"], output: "abc") - let path = ["zero", "one", "two", "d", "e", "f"] - guard let result = base.fetch(path) else { - XCTFail("invalid wildcard fetch") - return - } - -// let params = result.slugs(for: path) -// XCTAssert(params["a"] == "zero") -// XCTAssert(params["b"] == "one") -// XCTAssert(params["c"] == "two") -// XCTAssert(result.output == "abc") - } - - func testOutOfBoundsParams() { - let base = Branch(name: "[base]", output: nil) - base.extend([":a", ":b", ":c", "*"], output: "abc") - let path = ["zero", "one", "two", "d", "e", "f"] - guard let result = base.fetch(path) else { - XCTFail("invalid wildcard fetch") - return - } - -// let params = result.slugs(for: ["zero", "one"]) -// XCTAssert(params["a"] == "zero") -// XCTAssert(params["b"] == "one") -// XCTAssert(params["c"] == nil) -// XCTAssert(result.output == "abc") - } - func testLeadingPath() { let base = Branch(name: "[base]", output: nil) let subBranch = base.extend([":a", ":b", ":c", "*"], output: "abc") diff --git a/Tests/RoutingTests/RouterTests.swift b/Tests/RoutingTests/RouterTests.swift index e0e92fb5..fa4692e7 100644 --- a/Tests/RoutingTests/RouterTests.swift +++ b/Tests/RoutingTests/RouterTests.swift @@ -20,6 +20,9 @@ class RouterTests: XCTestCase { ("testRouterDualSlugRoutes", testRouterDualSlugRoutes), ("testRouteLogs", testRouteLogs), ("testRouterThrows", testRouterThrows), + ("testParams", testParams), + ("testOutOfBoundsParams", testOutOfBoundsParams), + ("testParamsDuplicateKey", testParamsDuplicateKey), ] func testRouter() throws { @@ -206,4 +209,81 @@ class RouterTests: XCTestCase { print(error) } } + + + func testParams() { + let base = Branch(name: "[base]", output: nil) + base.extend([":a", ":b", ":c", "*"], output: "abc") + let path = ["zero", "one", "two", "d", "e", "f"] + guard let result = base.fetch(path) else { + XCTFail("invalid wildcard fetch") + return + } + + let params = result.slugs(for: path) + XCTAssert(params["a"] == "zero") + XCTAssert(params["b"] == "one") + XCTAssert(params["c"] == "two") + XCTAssert(result.output == "abc") + } + + func testOutOfBoundsParams() { + let base = Branch(name: "[base]", output: nil) + base.extend([":a", ":b", ":c", "*"], output: "abc") + let path = ["zero", "one", "two", "d", "e", "f"] + guard let result = base.fetch(path) else { + XCTFail("invalid wildcard fetch") + return + } + + let params = result.slugs(for: ["zero", "one"]) + XCTAssert(params["a"] == "zero") + XCTAssert(params["b"] == "one") + XCTAssert(params["c"] == nil) + XCTAssert(result.output == "abc") + } + + func testParamsDuplicateKey() { + let base = Branch(name: "[base]", output: nil) + base.extend([":a", ":a", ":a", "*"], output: "abc") + let path = ["zero", "one", "two", "d", "e", "f"] + guard let result = base.fetch(path) else { + XCTFail("invalid wildcard fetch") + return + } + + let params = result.slugs(for: ["zero", "one"]) + XCTAssert(params["a.0"] == "zero") + XCTAssert(params["a.1"] == "one") + XCTAssert(params["a.2"] == nil) + XCTAssert(result.output == "abc") + } + + func testParameterizable() throws { + let base = Branch(name: "[base]", output: nil) + base.extend([Foo.parameter, Foo.parameter, Foo.parameter, "*"], output: "abc") + let path = ["zero", "one", "two", "d", "e", "f"] + guard let result = base.fetch(path) else { + XCTFail("invalid wildcard fetch") + return + } + + var params = result.slugs(for: ["zero", "one"]) + let one = try params.next(Foo.self) + let two = try params.next(Foo.self) + XCTAssert(one.id == "zero") + XCTAssert(two.id == "one") + } +} + +struct Foo { + let id: String +} + +extension Foo: Parameterizable { + static let uniqueSlug = "foo-slug" + + static func make(for parameter: String) throws -> Foo { + return .init(id: parameter) + } }