diff --git a/Sources/ScreamURITemplate/Internal/Scanner.swift b/Sources/ScreamURITemplate/Internal/Scanner.swift index 3db2b59..e93e1f1 100644 --- a/Sources/ScreamURITemplate/Internal/Scanner.swift +++ b/Sources/ScreamURITemplate/Internal/Scanner.swift @@ -48,26 +48,22 @@ struct Scanner { } } - private func checkUnterminated() throws(URITemplate.Error) { - if currentIndex == unicodeScalars.endIndex { - throw URITemplate.Error(type: .malformedTemplate, position: currentIndex, reason: "Unterminated Expression") - } - } - private mutating func scanExpressionComponent() throws(URITemplate.Error) -> Component { assert(unicodeScalars[currentIndex] == "{") let expressionStartIndex = currentIndex currentIndex = unicodeScalars.index(after: currentIndex) - try checkUnterminated() let expressionOperator = try scanExpressionOperator() - try checkUnterminated() let variableList = try scanVariableList() return ExpressionComponent(expressionOperator: expressionOperator, variableList: variableList, templatePosition: expressionStartIndex) } private mutating func scanExpressionOperator() throws(URITemplate.Error) -> ExpressionOperator { + guard currentIndex < unicodeScalars.endIndex else { + return .simple + } + let expressionOperator: ExpressionOperator if expressionOperatorCharacterSet.contains(unicodeScalars[currentIndex]) { guard let `operator` = ExpressionOperator(rawValue: unicodeScalars[currentIndex]) else { @@ -87,15 +83,13 @@ struct Scanner { var complete = false while !complete { let variableName = try scanVariableName() - - try checkUnterminated() - let modifier = try scanVariableModifier() - - try checkUnterminated() - variableList.append(VariableSpec(name: variableName, modifier: modifier)) + guard currentIndex < unicodeScalars.endIndex else { + throw URITemplate.Error(type: .malformedTemplate, position: currentIndex, reason: "Unterminated Expression") + } + switch unicodeScalars[currentIndex] { case ",": currentIndex = unicodeScalars.index(after: currentIndex) @@ -134,6 +128,10 @@ struct Scanner { } private mutating func scanVariableModifier() throws(URITemplate.Error) -> VariableSpec.Modifier { + guard currentIndex < unicodeScalars.endIndex else { + return .none + } + switch unicodeScalars[currentIndex] { case "*": currentIndex = unicodeScalars.index(after: currentIndex) diff --git a/Tests/ScreamURITemplateTests/Tests.swift b/Tests/ScreamURITemplateTests/Tests.swift index 5e3c75f..ae9ec7e 100644 --- a/Tests/ScreamURITemplateTests/Tests.swift +++ b/Tests/ScreamURITemplateTests/Tests.swift @@ -27,9 +27,13 @@ private struct TestVariableProvider: VariableProvider { } class Tests: XCTestCase { - func testUncompleteNotCrashing() throws { - // Use case: the user enters a template. This should not crash. + func testUnterminatedExpression() throws { XCTAssertThrowsError(try URITemplate(string: "https://api.github.com/repos/{")) + XCTAssertThrowsError(try URITemplate(string: "https://api.github.com/repos/{owner")) + XCTAssertThrowsError(try URITemplate(string: "https://api.github.com/repos/{owner,")) + XCTAssertThrowsError(try URITemplate(string: "https://api.github.com/repos/{owner*")) + XCTAssertThrowsError(try URITemplate(string: "https://api.github.com/repos/{owner:")) + XCTAssertThrowsError(try URITemplate(string: "https://api.github.com/repos/{owner:3")) } func testIncompletePercentEncodedTriplet() throws {