From f038ca2d0f4cb7553669ed7726375c7637a1be4b Mon Sep 17 00:00:00 2001 From: Alex Deem Date: Thu, 13 Jun 2024 09:53:54 +1000 Subject: [PATCH] Support processing by directly providing TypedVariableValue --- .../Internal/Components.swift | 10 ++--- Sources/ScreamURITemplate/URITemplate.swift | 14 ++++++- .../ScreamURITemplate/VariableProvider.swift | 8 ++++ Tests/ScreamURITemplateTests/Tests.swift | 39 +++++++++++++++++++ 4 files changed, 65 insertions(+), 6 deletions(-) diff --git a/Sources/ScreamURITemplate/Internal/Components.swift b/Sources/ScreamURITemplate/Internal/Components.swift index 9e63fbc..a8eae7e 100644 --- a/Sources/ScreamURITemplate/Internal/Components.swift +++ b/Sources/ScreamURITemplate/Internal/Components.swift @@ -17,7 +17,7 @@ import Foundation typealias ComponentBase = Sendable protocol Component: ComponentBase { - func expand(variables: VariableProvider) throws -> String + func expand(variables: TypedVariableProvider) throws -> String var variableNames: [String] { get } } @@ -33,7 +33,7 @@ struct LiteralComponent: Component { literal = string } - func expand(variables _: VariableProvider) throws -> String { + func expand(variables _: TypedVariableProvider) throws -> String { let expansion = String(literal) guard let encodedExpansion = expansion.addingPercentEncoding(withAllowedCharacters: reservedAndUnreservedCharacterSet) else { throw URITemplate.Error.expansionFailure(position: literal.startIndex, reason: "Percent Encoding Failed") @@ -48,7 +48,7 @@ struct LiteralPercentEncodedTripletComponent: Component { literal = string } - func expand(variables _: VariableProvider) throws -> String { + func expand(variables _: TypedVariableProvider) throws -> String { return String(literal) } } @@ -64,10 +64,10 @@ struct ExpressionComponent: Component { self.templatePosition = templatePosition } - func expand(variables: VariableProvider) throws -> String { + func expand(variables: TypedVariableProvider) throws -> String { let configuration = expressionOperator.expansionConfiguration() let expansions = try variableList.compactMap { variableSpec -> String? in - guard let value = variables[String(variableSpec.name)]?.asTypedVariableValue() else { + guard let value = variables[String(variableSpec.name)] else { return nil } do { diff --git a/Sources/ScreamURITemplate/URITemplate.swift b/Sources/ScreamURITemplate/URITemplate.swift index 70175c5..770f348 100644 --- a/Sources/ScreamURITemplate/URITemplate.swift +++ b/Sources/ScreamURITemplate/URITemplate.swift @@ -33,7 +33,7 @@ public struct URITemplate { self.components = components } - public func process(variables: VariableProvider) throws -> String { + public func process(variables: TypedVariableProvider) throws -> String { var result = "" for component in components { result += try component.expand(variables: variables) @@ -41,6 +41,18 @@ public struct URITemplate { return result } + public func process(variables: VariableProvider) throws -> String { + struct TypedVariableProviderWrapper: TypedVariableProvider { + let variables: VariableProvider + + subscript(_ key: String) -> TypedVariableValue? { + return variables[key]?.asTypedVariableValue() + } + } + + return try process(variables: TypedVariableProviderWrapper(variables: variables)) + } + public func process(variables: [String: String]) throws -> String { return try process(variables: variables as VariableDictionary) } diff --git a/Sources/ScreamURITemplate/VariableProvider.swift b/Sources/ScreamURITemplate/VariableProvider.swift index 2467e9c..17c4cf6 100644 --- a/Sources/ScreamURITemplate/VariableProvider.swift +++ b/Sources/ScreamURITemplate/VariableProvider.swift @@ -17,10 +17,18 @@ public protocol VariableProvider { subscript(_: String) -> VariableValue? { get } } +public protocol TypedVariableProvider { + subscript(_: String) -> TypedVariableValue? { get } +} + public typealias VariableDictionary = [String: VariableValue] extension VariableDictionary: VariableProvider {} +public typealias TypedVariableDictionary = [String: TypedVariableValue] + +extension TypedVariableDictionary: TypedVariableProvider {} + public struct SequenceVariableProvider: VariableProvider, ExpressibleByArrayLiteral { let sequence: any Sequence diff --git a/Tests/ScreamURITemplateTests/Tests.swift b/Tests/ScreamURITemplateTests/Tests.swift index f093174..281ab24 100644 --- a/Tests/ScreamURITemplateTests/Tests.swift +++ b/Tests/ScreamURITemplateTests/Tests.swift @@ -100,6 +100,45 @@ class Tests: XCTestCase { XCTAssertEqual(urlString, "https://api.example.com/1740A1A9-B3AD-4AE9-954B-918CEDE95285") } + func testVariableDictionaryVariousTypes() throws { + let template: URITemplate = "https://api.example.com{/string,int,bool,list}{?unordered*,ordered*}" + let variables: VariableDictionary = [ + "string": "SwiftScream", + "int": 42, + "bool": true, + "list": ["SwiftScream", 42, true], + "unordered": [ + "b": 42, + "a": "A", + "c": true, + ], + "ordered": [ + "b2": 42, + "a2": "A", + "c2": true, + ] as KeyValuePairs, + ] + let urlString = try template.process(variables: variables) + XCTAssertEqual("https://api.example.com/SwiftScream/42/true/SwiftScream,42,true?a=A&b=42&c=true&b2=42&a2=A&c2=true", urlString) + } + + func testTypedVariableDictionaryVariousTypes() throws { + let template: URITemplate = "https://api.example.com{/string,int,bool,list}{?unordered*,ordered*}" + let variables: TypedVariableDictionary = [ + "string": .string("SwiftScream"), + "int": .string("42"), + "bool": .string("true"), + "list": .list(["SwiftScream", "42", "true"]), + "ordered": .associativeArray([ + ("b", "42"), + ("a", "A"), + ("c", "true"), + ]), + ] + let urlString = try template.process(variables: variables) + XCTAssertEqual("https://api.example.com/SwiftScream/42/true/SwiftScream,42,true?b=42&a=A&c=true", urlString) + } + func testSendable() { let template: URITemplate = "https://api.github.com/repos/{owner}/{repo}/collaborators/{username}" let sendable = template as Sendable