From 6e32969629c1fdbf0abadaf1d98cd145259e42d2 Mon Sep 17 00:00:00 2001 From: Kazumasa Shimomura Date: Mon, 20 Mar 2023 13:24:15 +0900 Subject: [PATCH 1/2] Update deployment target to 11.0 for ios --- Matcha.xcodeproj/project.pbxproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Matcha.xcodeproj/project.pbxproj b/Matcha.xcodeproj/project.pbxproj index e79cde5..ecab3f9 100644 --- a/Matcha.xcodeproj/project.pbxproj +++ b/Matcha.xcodeproj/project.pbxproj @@ -378,6 +378,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Matcha/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -403,6 +404,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Matcha/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", From 9649f445775baba5bdd78a171a80062d2377d344 Mon Sep 17 00:00:00 2001 From: Kazumasa Shimomura Date: Tue, 23 Jul 2024 02:55:47 +0900 Subject: [PATCH 2/2] Add: Implement URL pattern matching with parameter support in Matcha 1. Introduced support for dynamic URL pattern matching using parameters enclosed in `{}`. 2. Updated `Matcha` initializer to parse patterns and extract parameter values from matched URLs. 3. Enhanced test cases to validate the new parameterized matching functionality. --- Matcha.xcodeproj/project.pbxproj | 2 ++ Matcha/Matcha.swift | 61 +++++++++++++++++++++----------- MatchaTests/MatchaTests.swift | 6 +++- 3 files changed, 48 insertions(+), 21 deletions(-) diff --git a/Matcha.xcodeproj/project.pbxproj b/Matcha.xcodeproj/project.pbxproj index ecab3f9..96ed5ef 100644 --- a/Matcha.xcodeproj/project.pbxproj +++ b/Matcha.xcodeproj/project.pbxproj @@ -423,6 +423,7 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CODE_SIGN_STYLE = Automatic; INFOPLIST_FILE = MatchaTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -441,6 +442,7 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CODE_SIGN_STYLE = Automatic; INFOPLIST_FILE = MatchaTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", diff --git a/Matcha/Matcha.swift b/Matcha/Matcha.swift index a319a19..9b17be4 100644 --- a/Matcha/Matcha.swift +++ b/Matcha/Matcha.swift @@ -67,38 +67,59 @@ public struct Matcha: Equatable { /// Matcha(url: url, pattern: "/{A}/{B}/{C}/")?.value(at: 1) // is `to` /// Matcha(url: url, pattern: "/{A}/{B}/{C}/")?.value(of: "C") // is `glory` public init?(url: URL, pattern: String) { - let isPath = pattern.first == "/" - let patternUrl = isPath ? "dummy\(pattern)" : pattern + guard let url = url.trailingSlashed else { return nil } + let isPatternPath = pattern.first == "/" - guard let schemeRegex = try? NSRegularExpression(pattern: "[^https?:\\/\\/].+") else { return nil } - guard let matched = schemeRegex.firstMatch(in: patternUrl) else { return nil } + guard let patternComponents = URLComponents(string: pattern) else { return nil } - let paths = (patternUrl as NSString).substring(with: matched.range(at: 0)).split(separator: "/").map(String.init) - - guard (url.host == paths.first || isPath) - && (url.pathComponents.count == paths.count || (url.pathComponents.isEmpty && paths.count == 1)) else { - return nil + guard url.host == patternComponents.host || isPatternPath else { + return nil } - var values: [String: String] = [:] - var list: [String] = [] - - for (pattern, path) in zip(paths, url.pathComponents) { - if pattern == path || path == "/" { - continue - } + let pathComponents = patternComponents.url?.pathComponents.dropFirst() ?? [] - if pattern.first == "{" && pattern.last == "}" { - values[String(pattern.dropFirst().dropLast())] = path - list.append(path) + var regexComponents: [String] = [] + var parameterNames: [String] = [] + for pathComponent in pathComponents { + if pathComponent.first == "{" && pathComponent.last == "}" { + let parameterName = String(pathComponent.dropFirst().dropLast()) + regexComponents.append("(?<\(parameterName)>.+)") + parameterNames.append(parameterName) } else { - return nil + regexComponents.append(pathComponent) } } + let regexPattern = regexComponents.joined(separator: "/") + let urlPath = url.path + + guard let regex = try? NSRegularExpression(pattern: "/\(regexPattern)$"), + let match = regex.firstMatch(in: urlPath) else { return nil } + + var values: [String: String] = [:] + var list: [String] = [] + for parameterName in parameterNames { + guard let range = match.range(withName: parameterName, in: urlPath) else { continue } + let value = String(urlPath[range]) + values[parameterName] = value + list.append(value) + } + self.url = url self.values = values self.list = list } } + +private extension NSTextCheckingResult { + func range(withName name: String, in string: String) -> Range? { + Range(range(withName: name), in: string) + } +} + +private extension URL { + var trailingSlashed: URL? { + absoluteString.hasSuffix("/") ? self : URL(string: absoluteString + "/") + } +} diff --git a/MatchaTests/MatchaTests.swift b/MatchaTests/MatchaTests.swift index 66c9222..8108f57 100644 --- a/MatchaTests/MatchaTests.swift +++ b/MatchaTests/MatchaTests.swift @@ -32,13 +32,17 @@ class MatchaTests: XCTestCase { XCTAssertNil(matcha.matched("")) XCTAssertNil(Matcha(url: URL(string: "https://example.com/")!).matched("/path")) XCTAssertNil(Matcha(url: URL(string: "https://example.com/")!).matched("")) - XCTAssertNotNil(matcha.matched("https://example.com/path/to/glory/")) XCTAssertNotNil(matcha.matched("https://example.com/path/to/glory")) XCTAssertNotNil(matcha.matched("/path/to/glory/")) XCTAssertNotNil(matcha.matched("/path/to/glory")) XCTAssertNotNil(Matcha(url: URL(string: "https://example.com/")!).matched("/")) XCTAssertNotNil(Matcha(url: URL(string: "https://example.com")!).matched("/")) + XCTAssertNotNil(matcha.matched("/path/{A}")) + XCTAssertEqual(matcha.matched("/path/{A}")?.value(of: "A"), "to/glory") + XCTAssertEqual(matcha.matched("/{A}")?.value(of: "A"), "path/to/glory") + XCTAssertEqual(matcha.matched("/{A}/{B}")?.value(of: "A"), "path/to") + XCTAssertEqual(matcha.matched("/{A}/{B}")?.value(of: "B"), "glory") let urlPattern = "https://example.com/{A}/{B}/{C}/" let urlPatternMatched = matcha.matched(urlPattern)