Skip to content

Commit

Permalink
Merge pull request #18 from giginet/case-insensitive-pattern
Browse files Browse the repository at this point in the history
Support case insensitive URL pattern
  • Loading branch information
giginet authored Aug 5, 2019
2 parents 945f979 + da8ee56 commit 354c3cd
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 75 deletions.
16 changes: 6 additions & 10 deletions Sources/Crossroad/Context.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ public struct Context<UserInfo> {
throw Error.parsingArgumentFailed
}

public func parameter<T: Extractable>(for key: String, caseInsensitive: Bool = false) -> T? {
if let queryItem = queryItem(from: key, caseInsensitive: caseInsensitive) {
public func parameter<T: Extractable>(for key: String) -> T? {
if let queryItem = queryItem(from: key) {
if let queryValue = queryItem.value,
let value = T.extract(from: queryValue) {
return value
Expand All @@ -49,15 +49,11 @@ public struct Context<UserInfo> {
return nil
}

private func queryItem(from key: String, caseInsensitive: Bool) -> URLQueryItem? {
func isEqual(_ lhs: String, _ rhs: String, caseInsensitive: Bool) -> Bool {
if caseInsensitive {
return lhs.lowercased() == rhs.lowercased()
} else {
return lhs == rhs
}
private func queryItem(from key: String) -> URLQueryItem? {
func isEqual(_ lhs: String, _ rhs: String) -> Bool {
return lhs.lowercased() == rhs.lowercased()
}
return parameters.first { isEqual($0.name, key, caseInsensitive: caseInsensitive) }
return parameters.first { isEqual($0.name, key) }
}

private func queryItem(matchesIn regexp: NSRegularExpression) -> URLQueryItem? {
Expand Down
4 changes: 2 additions & 2 deletions Sources/Crossroad/PatternURL.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ internal struct PatternURL {
private static let pathSeparator = "/"

init?(string: String) {
let firstSplit = string.components(separatedBy: PatternURL.schemeSeparator)
self.patternString = string.lowercased()
let firstSplit = patternString.components(separatedBy: PatternURL.schemeSeparator)
guard let scheme = firstSplit.first, !scheme.isEmpty else {
return nil
}
Expand All @@ -25,7 +26,6 @@ internal struct PatternURL {
}
self.scheme = scheme
self.host = host
self.patternString = string
if components.count > 1 {
let left = components[1 ..< components.count]
// In URL, pathComponents includes the starting "/" so do the same.
Expand Down
21 changes: 3 additions & 18 deletions Sources/Crossroad/Router.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public final class Router<UserInfo> {
private var routes: [Route<UserInfo>] = []

public init(scheme: String) {
prefix = .scheme(scheme)
prefix = .scheme(scheme.lowercased())
}

public init(url: URL) {
Expand All @@ -27,15 +27,6 @@ public final class Router<UserInfo> {
}
}

private func canRespond(to url: URL) -> Bool {
switch prefix {
case .scheme(let scheme):
return scheme == url.scheme
case .url(let prefixURL):
return url.absoluteString.hasPrefix(prefixURL.absoluteString)
}
}

internal func register(_ route: Route<UserInfo>) {
if isValidURLPattern(route.patternURL) {
routes.append(route)
Expand All @@ -46,16 +37,10 @@ public final class Router<UserInfo> {

@discardableResult
public func openIfPossible(_ url: URL, userInfo: UserInfo) -> Bool {
if !canRespond(to: url) {
return false
}
return routes.first { $0.openIfPossible(url, userInfo: userInfo) } != nil
}

public func responds(to url: URL, userInfo: UserInfo) -> Bool {
if !canRespond(to: url) {
return false
}
return routes.first { $0.responds(to: url, userInfo: userInfo) } != nil
}

Expand All @@ -71,13 +56,13 @@ public final class Router<UserInfo> {
let patternURLString: String
switch prefix {
case .scheme(let scheme):
if pattern.hasPrefix("\(scheme)://") {
if pattern.lowercased().hasPrefix("\(scheme)://") {
patternURLString = canonicalizePattern(pattern)
} else {
patternURLString = "\(scheme)://\(canonicalizePattern(pattern))"
}
case .url(let url):
if pattern.hasPrefix(url.absoluteString) {
if pattern.lowercased().hasPrefix(url.absoluteString) {
patternURLString = canonicalizePattern(pattern)
} else {
patternURLString = url.appendingPathComponent(canonicalizePattern(pattern)).absoluteString
Expand Down
8 changes: 4 additions & 4 deletions Sources/Crossroad/URLParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,23 @@ public struct URLParser<UserInfo> {
guard let scheme = url.scheme, let host = url.host else {
return nil
}
if scheme != patternURL.scheme || patternURL.pathComponents.count != url.pathComponents.count {
if scheme.lowercased() != patternURL.scheme || patternURL.pathComponents.count != url.pathComponents.count {
return nil
}

var arguments: Arguments = [:]
if patternURL.host.hasPrefix(PatternURL.keywordPrefix) {
let keyword = String(patternURL.host[PatternURL.keywordPrefix.endIndex...])
arguments[keyword] = host
} else if host != patternURL.host {
arguments[keyword] = url.host
} else if host.lowercased() != patternURL.host {
return nil
}

for (patternComponent, component) in zip(patternURL.pathComponents, url.pathComponents) {
if patternComponent.hasPrefix(PatternURL.keywordPrefix) {
let keyword = String(patternComponent[PatternURL.keywordPrefix.endIndex...])
arguments[keyword] = component
} else if patternComponent == component {
} else if patternComponent == component.lowercased() {
continue
} else {
return nil
Expand Down
5 changes: 2 additions & 3 deletions Tests/CrossroadTests/ContextTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,8 @@ final class ContextTests: XCTestCase {
XCTAssertEqual(context.parameter(for: "name"), "Pikachu")
XCTAssertNil(context.parameter(for: "foo") as String?)
XCTAssertEqual(context.parameter(for: "region"), Region.kanto)
XCTAssertNil(context.parameter(for: "NaMe") as String?)
XCTAssertEqual(context.parameter(for: "NaMe", caseInsensitive: true), "Pikachu")
XCTAssertEqual(context.parameter(for: "NAME2", caseInsensitive: true), "Mewtwo")
XCTAssertEqual(context.parameter(for: "NaMe"), "Pikachu")
XCTAssertEqual(context.parameter(for: "NAME2"), "Mewtwo")
}

func testParametersByRegexp() {
Expand Down
9 changes: 9 additions & 0 deletions Tests/CrossroadTests/PatternURLTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,15 @@ final class PatternURLTests: XCTestCase {
XCTAssertEqual(patternURL.pathComponents, [])
}

func testCapitalCase() {
let subject = PatternURL(string: "FOOBAR://FOO/BAR")!
XCTAssertEqual(subject.patternString,
"foobar://foo/bar")
XCTAssertEqual(subject.scheme, "foobar")
XCTAssertEqual(subject.host, "foo")
XCTAssertEqual(subject.pathComponents, ["/", "bar"])
}

func testParseWithKeyword() {
let url0 = PatternURL(string: "foobar://search/:keyword")
XCTAssertEqual(url0?.scheme, "foobar")
Expand Down
Loading

0 comments on commit 354c3cd

Please sign in to comment.