Skip to content

Commit f77f0db

Browse files
committed
Leverage Typed Throws
1 parent b97572b commit f77f0db

File tree

6 files changed

+53
-48
lines changed

6 files changed

+53
-48
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ The error cases contain associated values specifying a string reason for the err
3535
```swift
3636
do {
3737
_ = try URITemplate(string: "https://api.github.com/repos/{}/{repository}")
38-
} catch let error as URITemplate.Error {
38+
} catch {
3939
// error.reason = "Empty Variable Name"
4040
// error.position = 29th character
4141
}

Sources/ScreamURITemplate/Internal/Components.swift

Lines changed: 28 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import Foundation
1717
typealias ComponentBase = Sendable
1818

1919
protocol Component: ComponentBase {
20-
func expand(variables: TypedVariableProvider) throws -> String
20+
func expand(variables: TypedVariableProvider) throws(URITemplate.Error) -> String
2121
var variableNames: [String] { get }
2222
}
2323

@@ -33,7 +33,7 @@ struct LiteralComponent: Component {
3333
literal = string
3434
}
3535

36-
func expand(variables _: TypedVariableProvider) throws -> String {
36+
func expand(variables _: TypedVariableProvider) throws(URITemplate.Error) -> String {
3737
let expansion = String(literal)
3838
guard let encodedExpansion = expansion.addingPercentEncoding(withAllowedCharacters: reservedAndUnreservedCharacterSet) else {
3939
throw URITemplate.Error(type: .expansionFailure, position: literal.startIndex, reason: "Percent Encoding Failed")
@@ -48,7 +48,7 @@ struct LiteralPercentEncodedTripletComponent: Component {
4848
literal = string
4949
}
5050

51-
func expand(variables _: TypedVariableProvider) throws -> String {
51+
func expand(variables _: TypedVariableProvider) throws(URITemplate.Error) -> String {
5252
return String(literal)
5353
}
5454
}
@@ -64,28 +64,35 @@ struct ExpressionComponent: Component {
6464
self.templatePosition = templatePosition
6565
}
6666

67-
func expand(variables: TypedVariableProvider) throws -> String {
67+
func expand(variables: TypedVariableProvider) throws(URITemplate.Error) -> String {
6868
let configuration = expressionOperator.expansionConfiguration()
69-
let expansions = try variableList.compactMap { variableSpec -> String? in
70-
guard let value = variables[String(variableSpec.name)] else {
71-
return nil
69+
do {
70+
let expansions = try variableList.compactMap { variableSpec throws(URITemplate.Error) -> String? in
71+
guard let value = variables[String(variableSpec.name)] else {
72+
return nil
73+
}
74+
do throws(FormatError) {
75+
return try value.formatForTemplateExpansion(variableSpec: variableSpec, expansionConfiguration: configuration)
76+
} catch {
77+
throw URITemplate.Error(type: .expansionFailure, position: templatePosition, reason: "Failed expanding variable \"\(variableSpec.name)\": \(error.reason)")
78+
}
7279
}
73-
do {
74-
return try value.formatForTemplateExpansion(variableSpec: variableSpec, expansionConfiguration: configuration)
75-
} catch let error as FormatError {
76-
throw URITemplate.Error(type: .expansionFailure, position: templatePosition, reason: "Failed expanding variable \"\(variableSpec.name)\": \(error.reason)")
80+
81+
if expansions.count == 0 {
82+
return ""
7783
}
84+
85+
let joinedExpansions = expansions.joined(separator: configuration.separator)
86+
if let prefix = configuration.prefix {
87+
return prefix + joinedExpansions
88+
}
89+
return joinedExpansions
90+
} catch let error as URITemplate.Error {
91+
throw error
92+
} catch {
93+
// compactMap is not marked up for Typed Throws, the compiler therefore does not know that this is not possible
94+
throw URITemplate.Error(type: .expansionFailure, position: templatePosition, reason: "Failed expanding variable: \(error.localizedDescription)")
7895
}
79-
80-
if expansions.count == 0 {
81-
return ""
82-
}
83-
84-
let joinedExpansions = expansions.joined(separator: configuration.separator)
85-
if let prefix = configuration.prefix {
86-
return prefix + joinedExpansions
87-
}
88-
return joinedExpansions
8996
}
9097

9198
var variableNames: [String] {

Sources/ScreamURITemplate/Internal/Scanner.swift

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ struct Scanner {
3333
return currentIndex >= unicodeScalars.endIndex
3434
}
3535

36-
mutating func scanComponent() throws -> Component {
36+
mutating func scanComponent() throws(URITemplate.Error) -> Component {
3737
let nextScalar = unicodeScalars[currentIndex]
3838

3939
switch nextScalar {
@@ -48,7 +48,7 @@ struct Scanner {
4848
}
4949
}
5050

51-
private mutating func scanExpressionComponent() throws -> Component {
51+
private mutating func scanExpressionComponent() throws(URITemplate.Error) -> Component {
5252
assert(unicodeScalars[currentIndex] == "{")
5353
let expressionStartIndex = currentIndex
5454
currentIndex = unicodeScalars.index(after: currentIndex)
@@ -59,7 +59,7 @@ struct Scanner {
5959
return ExpressionComponent(expressionOperator: expressionOperator, variableList: variableList, templatePosition: expressionStartIndex)
6060
}
6161

62-
private mutating func scanExpressionOperator() throws -> ExpressionOperator {
62+
private mutating func scanExpressionOperator() throws(URITemplate.Error) -> ExpressionOperator {
6363
let expressionOperator: ExpressionOperator
6464
if expressionOperatorCharacterSet.contains(unicodeScalars[currentIndex]) {
6565
guard let `operator` = ExpressionOperator(rawValue: unicodeScalars[currentIndex]) else {
@@ -73,7 +73,7 @@ struct Scanner {
7373
return expressionOperator
7474
}
7575

76-
private mutating func scanVariableList() throws -> [VariableSpec] {
76+
private mutating func scanVariableList() throws(URITemplate.Error) -> [VariableSpec] {
7777
var variableList: [VariableSpec] = []
7878

7979
var complete = false
@@ -106,7 +106,7 @@ struct Scanner {
106106
return variableList
107107
}
108108

109-
private mutating func scanVariableName() throws -> Substring {
109+
private mutating func scanVariableName() throws(URITemplate.Error) -> Substring {
110110
let endIndex = scanUpTo(characterSet: invertedVarnameCharacterSet)
111111
let variableName = string[currentIndex..<endIndex]
112112
if variableName.isEmpty {
@@ -129,7 +129,7 @@ struct Scanner {
129129
return variableName
130130
}
131131

132-
private mutating func scanVariableModifier() throws -> VariableSpec.Modifier {
132+
private mutating func scanVariableModifier() throws(URITemplate.Error) -> VariableSpec.Modifier {
133133
switch unicodeScalars[currentIndex] {
134134
case "*":
135135
currentIndex = unicodeScalars.index(after: currentIndex)
@@ -157,7 +157,7 @@ struct Scanner {
157157
}
158158
}
159159

160-
private mutating func scanLiteralComponent() throws -> Component {
160+
private mutating func scanLiteralComponent() throws(URITemplate.Error) -> Component {
161161
assert(literalCharacterSet.contains(unicodeScalars[currentIndex]))
162162

163163
let startIndex = currentIndex
@@ -166,7 +166,7 @@ struct Scanner {
166166
return LiteralComponent(string[startIndex..<endIndex])
167167
}
168168

169-
private mutating func scanPercentEncodingComponent() throws -> Component {
169+
private mutating func scanPercentEncodingComponent() throws(URITemplate.Error) -> Component {
170170
assert(unicodeScalars[currentIndex] == "%")
171171

172172
let startIndex = currentIndex

Sources/ScreamURITemplate/Internal/ValueFormatting.swift

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ struct FormatError: Error {
1919
}
2020

2121
extension TypedVariableValue {
22-
func formatForTemplateExpansion(variableSpec: VariableSpec, expansionConfiguration configuration: ExpansionConfiguration) throws -> String? {
22+
func formatForTemplateExpansion(variableSpec: VariableSpec, expansionConfiguration configuration: ExpansionConfiguration) throws(FormatError) -> String? {
2323
switch self {
2424
case let .string(plainValue):
2525
return try plainValue.formatForTemplateExpansion(variableSpec: variableSpec, expansionConfiguration: configuration)
@@ -45,7 +45,7 @@ extension TypedVariableValue {
4545
}
4646
}
4747

48-
private func percentEncode(string: String, withAllowedCharacters allowedCharacterSet: CharacterSet, allowPercentEncodedTriplets: Bool) throws -> String {
48+
private func percentEncode(string: String, withAllowedCharacters allowedCharacterSet: CharacterSet, allowPercentEncodedTriplets: Bool) throws(FormatError) -> String {
4949
guard var encoded = string.addingPercentEncoding(withAllowedCharacters: allowedCharacterSet) else {
5050
throw FormatError(reason: "Percent Encoding Failed")
5151
}
@@ -73,7 +73,7 @@ private func percentEncode(string: String, withAllowedCharacters allowedCharacte
7373
}
7474

7575
private extension StringProtocol {
76-
func formatForTemplateExpansion(variableSpec: VariableSpec, expansionConfiguration: ExpansionConfiguration) throws -> String {
76+
func formatForTemplateExpansion(variableSpec: VariableSpec, expansionConfiguration: ExpansionConfiguration) throws(FormatError) -> String {
7777
let modifiedValue = if let prefixLength = variableSpec.prefixLength() {
7878
String(prefix(prefixLength))
7979
} else {
@@ -91,9 +91,9 @@ private extension StringProtocol {
9191
}
9292

9393
private extension Array where Element: StringProtocol {
94-
func formatForTemplateExpansion(variableSpec: VariableSpec, expansionConfiguration: ExpansionConfiguration) throws -> String? {
94+
func formatForTemplateExpansion(variableSpec: VariableSpec, expansionConfiguration: ExpansionConfiguration) throws(FormatError) -> String? {
9595
let separator = ","
96-
let encodedExpansions = try map { element -> String in
96+
let encodedExpansions = try map { element throws(FormatError) -> String in
9797
return try percentEncode(string: String(element), withAllowedCharacters: expansionConfiguration.percentEncodingAllowedCharacterSet, allowPercentEncodedTriplets: expansionConfiguration.allowPercentEncodedTriplets)
9898
}
9999
if encodedExpansions.count == 0 {
@@ -109,9 +109,9 @@ private extension Array where Element: StringProtocol {
109109
return expansion
110110
}
111111

112-
func explodeForTemplateExpansion(variableSpec: VariableSpec, expansionConfiguration: ExpansionConfiguration) throws -> String? {
112+
func explodeForTemplateExpansion(variableSpec: VariableSpec, expansionConfiguration: ExpansionConfiguration) throws(FormatError) -> String? {
113113
let separator = expansionConfiguration.separator
114-
let encodedExpansions = try map { element -> String in
114+
let encodedExpansions = try map { element throws(FormatError) -> String in
115115
let encodedElement = try percentEncode(string: String(element), withAllowedCharacters: expansionConfiguration.percentEncodingAllowedCharacterSet, allowPercentEncodedTriplets: expansionConfiguration.allowPercentEncodedTriplets)
116116
if expansionConfiguration.named {
117117
if encodedElement.isEmpty && expansionConfiguration.omitOrphanedEquals {
@@ -129,8 +129,8 @@ private extension Array where Element: StringProtocol {
129129
}
130130

131131
private extension [(key: String, value: String)] {
132-
func formatForTemplateExpansion(variableSpec: VariableSpec, expansionConfiguration: ExpansionConfiguration) throws -> String? {
133-
let encodedExpansions = try map { key, value -> String in
132+
func formatForTemplateExpansion(variableSpec: VariableSpec, expansionConfiguration: ExpansionConfiguration) throws(FormatError) -> String? {
133+
let encodedExpansions = try map { key, value throws(FormatError) -> String in
134134
let encodedKey = try percentEncode(string: String(key), withAllowedCharacters: expansionConfiguration.percentEncodingAllowedCharacterSet, allowPercentEncodedTriplets: expansionConfiguration.allowPercentEncodedTriplets)
135135
let encodedValue = try percentEncode(string: String(value), withAllowedCharacters: expansionConfiguration.percentEncodingAllowedCharacterSet, allowPercentEncodedTriplets: expansionConfiguration.allowPercentEncodedTriplets)
136136
return "\(encodedKey),\(encodedValue)"
@@ -145,9 +145,9 @@ private extension [(key: String, value: String)] {
145145
return expansion
146146
}
147147

148-
func explodeForTemplateExpansion(variableSpec: VariableSpec, expansionConfiguration: ExpansionConfiguration) throws -> String? {
148+
func explodeForTemplateExpansion(variableSpec: VariableSpec, expansionConfiguration: ExpansionConfiguration) throws(FormatError) -> String? {
149149
let separator = expansionConfiguration.separator
150-
let encodedExpansions = try map { key, value -> String in
150+
let encodedExpansions = try map { key, value throws(FormatError) -> String in
151151
let encodedKey = try percentEncode(string: String(key), withAllowedCharacters: expansionConfiguration.percentEncodingAllowedCharacterSet, allowPercentEncodedTriplets: expansionConfiguration.allowPercentEncodedTriplets)
152152
let encodedValue = try percentEncode(string: String(value), withAllowedCharacters: expansionConfiguration.percentEncodingAllowedCharacterSet, allowPercentEncodedTriplets: expansionConfiguration.allowPercentEncodedTriplets)
153153
if expansionConfiguration.named && encodedValue.isEmpty && expansionConfiguration.omitOrphanedEquals {

Sources/ScreamURITemplate/URITemplate.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public struct URITemplate {
4040
/// - Parameter string: the string representation of the URI Template
4141
///
4242
/// - Throws: `URITemplate.Error` with `type = .malformedTemplate` if the string is not a valid URI Template
43-
public init(string: String) throws {
43+
public init(string: String) throws(URITemplate.Error) {
4444
var components: [Component] = []
4545
var scanner = Scanner(string: string)
4646
while !scanner.isComplete {
@@ -56,7 +56,7 @@ public struct URITemplate {
5656
/// - Returns: The result of processing the template
5757
///
5858
/// - Throws: `URITemplate.Error` with `type = .expansionFailure` if an error occurs processing the template
59-
public func process(variables: TypedVariableProvider) throws -> String {
59+
public func process(variables: TypedVariableProvider) throws(URITemplate.Error) -> String {
6060
var result = ""
6161
for component in components {
6262
result += try component.expand(variables: variables)
@@ -73,7 +73,7 @@ public struct URITemplate {
7373
/// - Returns: The result of processing the template
7474
///
7575
/// - Throws: `URITemplate.Error` with `type = .expansionFailure` if an error occurs processing the template
76-
public func process(variables: VariableProvider) throws -> String {
76+
public func process(variables: VariableProvider) throws(URITemplate.Error) -> String {
7777
struct TypedVariableProviderWrapper: TypedVariableProvider {
7878
let variables: VariableProvider
7979

@@ -94,7 +94,7 @@ public struct URITemplate {
9494
/// - Returns: The result of processing the template
9595
///
9696
/// - Throws: `URITemplate.Error` with `type = .expansionFailure` if an error occurs processing the template
97-
public func process(variables: [String: String]) throws -> String {
97+
public func process(variables: [String: String]) throws(URITemplate.Error) -> String {
9898
return try process(variables: variables as VariableDictionary)
9999
}
100100

Tests/ScreamURITemplateTests/TestFileTests.swift

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,16 +37,14 @@ class TestFileTests: XCTestCase {
3737
let template = try URITemplate(string: templateString)
3838
_ = try template.process(variables: variables)
3939
XCTFail("Did not throw")
40-
} catch let error as URITemplate.Error {
40+
} catch {
4141
if failReason != nil {
4242
XCTAssertEqual(failReason, error.reason)
4343
}
4444
if failPosition != nil {
4545
let characters = templateString[..<error.position].count
4646
XCTAssertEqual(failPosition, characters)
4747
}
48-
} catch {
49-
XCTFail("Threw an unexpected error")
5048
}
5149
}
5250

0 commit comments

Comments
 (0)