Skip to content

Commit b97572b

Browse files
committed
Use a struct for URITemplate.Error
1 parent e7efe39 commit b97572b

6 files changed

Lines changed: 42 additions & 38 deletions

File tree

.swiftlint.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,6 @@ trailing_comma:
1515

1616
vertical_whitespace:
1717
max_empty_lines: 2
18+
19+
nesting:
20+
type_level: 2

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,9 @@ 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 URITemplate.Error.malformedTemplate(let position, let reason) {
39-
// reason = "Empty Variable Name"
40-
// position = 29th character
38+
} catch let error as URITemplate.Error {
39+
// error.reason = "Empty Variable Name"
40+
// error.position = 29th character
4141
}
4242
```
4343

Sources/ScreamURITemplate/Internal/Components.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ struct LiteralComponent: Component {
3636
func expand(variables _: TypedVariableProvider) throws -> String {
3737
let expansion = String(literal)
3838
guard let encodedExpansion = expansion.addingPercentEncoding(withAllowedCharacters: reservedAndUnreservedCharacterSet) else {
39-
throw URITemplate.Error.expansionFailure(position: literal.startIndex, reason: "Percent Encoding Failed")
39+
throw URITemplate.Error(type: .expansionFailure, position: literal.startIndex, reason: "Percent Encoding Failed")
4040
}
4141
return encodedExpansion
4242
}
@@ -73,7 +73,7 @@ struct ExpressionComponent: Component {
7373
do {
7474
return try value.formatForTemplateExpansion(variableSpec: variableSpec, expansionConfiguration: configuration)
7575
} catch let error as FormatError {
76-
throw URITemplate.Error.expansionFailure(position: templatePosition, reason: "Failed expanding variable \"\(variableSpec.name)\": \(error.reason)")
76+
throw URITemplate.Error(type: .expansionFailure, position: templatePosition, reason: "Failed expanding variable \"\(variableSpec.name)\": \(error.reason)")
7777
}
7878
}
7979

Sources/ScreamURITemplate/Internal/Scanner.swift

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ struct Scanner {
4444
case literalCharacterSet:
4545
return try scanLiteralComponent()
4646
default:
47-
throw URITemplate.Error.malformedTemplate(position: currentIndex, reason: "Unexpected character")
47+
throw URITemplate.Error(type: .malformedTemplate, position: currentIndex, reason: "Unexpected character")
4848
}
4949
}
5050

@@ -63,7 +63,7 @@ struct Scanner {
6363
let expressionOperator: ExpressionOperator
6464
if expressionOperatorCharacterSet.contains(unicodeScalars[currentIndex]) {
6565
guard let `operator` = ExpressionOperator(rawValue: unicodeScalars[currentIndex]) else {
66-
throw URITemplate.Error.malformedTemplate(position: currentIndex, reason: "Unsupported Operator")
66+
throw URITemplate.Error(type: .malformedTemplate, position: currentIndex, reason: "Unsupported Operator")
6767
}
6868
expressionOperator = `operator`
6969
currentIndex = unicodeScalars.index(after: currentIndex)
@@ -81,13 +81,13 @@ struct Scanner {
8181
let variableName = try scanVariableName()
8282

8383
if currentIndex == unicodeScalars.endIndex {
84-
throw URITemplate.Error.malformedTemplate(position: currentIndex, reason: "Unterminated Expression")
84+
throw URITemplate.Error(type: .malformedTemplate, position: currentIndex, reason: "Unterminated Expression")
8585
}
8686

8787
let modifier = try scanVariableModifier()
8888

8989
if currentIndex == unicodeScalars.endIndex {
90-
throw URITemplate.Error.malformedTemplate(position: currentIndex, reason: "Unterminated Expression")
90+
throw URITemplate.Error(type: .malformedTemplate, position: currentIndex, reason: "Unterminated Expression")
9191
}
9292

9393
variableList.append(VariableSpec(name: variableName, modifier: modifier))
@@ -99,7 +99,7 @@ struct Scanner {
9999
currentIndex = unicodeScalars.index(after: currentIndex)
100100
complete = true
101101
default:
102-
throw URITemplate.Error.malformedTemplate(position: currentIndex, reason: "Unexpected Character in Expression")
102+
throw URITemplate.Error(type: .malformedTemplate, position: currentIndex, reason: "Unexpected Character in Expression")
103103
}
104104
}
105105

@@ -110,17 +110,17 @@ struct Scanner {
110110
let endIndex = scanUpTo(characterSet: invertedVarnameCharacterSet)
111111
let variableName = string[currentIndex..<endIndex]
112112
if variableName.isEmpty {
113-
throw URITemplate.Error.malformedTemplate(position: currentIndex, reason: "Empty Variable Name")
113+
throw URITemplate.Error(type: .malformedTemplate, position: currentIndex, reason: "Empty Variable Name")
114114
} else if variableName.starts(with: ".") {
115-
throw URITemplate.Error.malformedTemplate(position: currentIndex, reason: "Variable Name Cannot Begin With '.'")
115+
throw URITemplate.Error(type: .malformedTemplate, position: currentIndex, reason: "Variable Name Cannot Begin With '.'")
116116
}
117117
var remainingVariableName = variableName
118118
while let index = remainingVariableName.firstIndex(of: "%") {
119119
let secondIndex = remainingVariableName.index(after: index)
120120
let thirdIndex = remainingVariableName.index(after: secondIndex)
121121
if !hexCharacterSet.contains(unicodeScalars[secondIndex]) ||
122122
!hexCharacterSet.contains(unicodeScalars[thirdIndex]) {
123-
throw URITemplate.Error.malformedTemplate(position: currentIndex, reason: "% must be percent-encoded in variable name")
123+
throw URITemplate.Error(type: .malformedTemplate, position: currentIndex, reason: "% must be percent-encoded in variable name")
124124
}
125125
let nextIndex = remainingVariableName.index(after: thirdIndex)
126126
remainingVariableName = remainingVariableName[nextIndex...]
@@ -139,16 +139,16 @@ struct Scanner {
139139
let endIndex = scanUpTo(characterSet: invertedDecimalDigitsCharacterSet)
140140
let lengthString = string[currentIndex..<endIndex]
141141
if lengthString.isEmpty {
142-
throw URITemplate.Error.malformedTemplate(position: currentIndex, reason: "Prefix length not specified")
142+
throw URITemplate.Error(type: .malformedTemplate, position: currentIndex, reason: "Prefix length not specified")
143143
}
144144
if lengthString.first == "0" {
145-
throw URITemplate.Error.malformedTemplate(position: currentIndex, reason: "Prefix length cannot begin with 0")
145+
throw URITemplate.Error(type: .malformedTemplate, position: currentIndex, reason: "Prefix length cannot begin with 0")
146146
}
147147
if lengthString.count > 4 {
148-
throw URITemplate.Error.malformedTemplate(position: currentIndex, reason: "Prefix modifier length too large")
148+
throw URITemplate.Error(type: .malformedTemplate, position: currentIndex, reason: "Prefix modifier length too large")
149149
}
150150
guard let length = Int(lengthString) else {
151-
throw URITemplate.Error.malformedTemplate(position: currentIndex, reason: "Cannot parse prefix modifier length")
151+
throw URITemplate.Error(type: .malformedTemplate, position: currentIndex, reason: "Cannot parse prefix modifier length")
152152
}
153153
currentIndex = endIndex
154154
return .prefix(length: length)
@@ -175,7 +175,7 @@ struct Scanner {
175175

176176
if !hexCharacterSet.contains(unicodeScalars[secondIndex]) ||
177177
!hexCharacterSet.contains(unicodeScalars[thirdIndex]) {
178-
throw URITemplate.Error.malformedTemplate(position: currentIndex, reason: "% must be percent-encoded in literal")
178+
throw URITemplate.Error(type: .malformedTemplate, position: currentIndex, reason: "% must be percent-encoded in literal")
179179
}
180180

181181
currentIndex = unicodeScalars.index(after: thirdIndex)

Sources/ScreamURITemplate/URITemplate.swift

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,20 @@ import Foundation
1717
/// An [RFC6570](https://tools.ietf.org/html/rfc6570) URI Template
1818
public struct URITemplate {
1919
/// An error that may be thrown when parsing or processing a template
20-
public enum Error: Swift.Error {
21-
/// Represents an error parsing a string into a URI Template
22-
case malformedTemplate(position: String.Index, reason: String)
23-
/// Represents an error processing a template
24-
case expansionFailure(position: String.Index, reason: String)
20+
public struct Error: Swift.Error {
21+
public enum ErrorType: Sendable {
22+
/// Represents an error parsing a string into a URI Template
23+
case malformedTemplate
24+
/// Represents an error processing a template
25+
case expansionFailure
26+
}
27+
28+
/// The type of the error
29+
public let type: ErrorType
30+
/// The position in the template that the error occurred
31+
public let position: String.Index
32+
/// The reason for the error
33+
public let reason: String
2534
}
2635

2736
private let string: String
@@ -30,7 +39,7 @@ public struct URITemplate {
3039
/// Initializes a URITemplate from a string
3140
/// - Parameter string: the string representation of the URI Template
3241
///
33-
/// - Throws: `URITemplate.Error.malformedTemplate` if the string is not a valid URI Template
42+
/// - Throws: `URITemplate.Error` with `type = .malformedTemplate` if the string is not a valid URI Template
3443
public init(string: String) throws {
3544
var components: [Component] = []
3645
var scanner = Scanner(string: string)
@@ -46,7 +55,7 @@ public struct URITemplate {
4655
///
4756
/// - Returns: The result of processing the template
4857
///
49-
/// - Throws: `URITemplate.Error.expansionFailure` if an error occurs processing the template
58+
/// - Throws: `URITemplate.Error` with `type = .expansionFailure` if an error occurs processing the template
5059
public func process(variables: TypedVariableProvider) throws -> String {
5160
var result = ""
5261
for component in components {
@@ -63,7 +72,7 @@ public struct URITemplate {
6372
///
6473
/// - Returns: The result of processing the template
6574
///
66-
/// - Throws: `URITemplate.Error.expansionFailure` if an error occurs processing the template
75+
/// - Throws: `URITemplate.Error` with `type = .expansionFailure` if an error occurs processing the template
6776
public func process(variables: VariableProvider) throws -> String {
6877
struct TypedVariableProviderWrapper: TypedVariableProvider {
6978
let variables: VariableProvider
@@ -84,7 +93,7 @@ public struct URITemplate {
8493
///
8594
/// - Returns: The result of processing the template
8695
///
87-
/// - Throws: `URITemplate.Error.expansionFailure` if an error occurs processing the template
96+
/// - Throws: `URITemplate.Error` with `type = .expansionFailure` if an error occurs processing the template
8897
public func process(variables: [String: String]) throws -> String {
8998
return try process(variables: variables as VariableDictionary)
9099
}

Tests/ScreamURITemplateTests/TestFileTests.swift

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,20 +37,12 @@ class TestFileTests: XCTestCase {
3737
let template = try URITemplate(string: templateString)
3838
_ = try template.process(variables: variables)
3939
XCTFail("Did not throw")
40-
} catch let URITemplate.Error.malformedTemplate(position, reason) {
40+
} catch let error as URITemplate.Error {
4141
if failReason != nil {
42-
XCTAssertEqual(failReason, reason)
42+
XCTAssertEqual(failReason, error.reason)
4343
}
4444
if failPosition != nil {
45-
let characters = templateString[..<position].count
46-
XCTAssertEqual(failPosition, characters)
47-
}
48-
} catch let URITemplate.Error.expansionFailure(position, reason) {
49-
if failReason != nil {
50-
XCTAssertEqual(failReason, reason)
51-
}
52-
if failPosition != nil {
53-
let characters = templateString[..<position].count
45+
let characters = templateString[..<error.position].count
5446
XCTAssertEqual(failPosition, characters)
5547
}
5648
} catch {

0 commit comments

Comments
 (0)