Skip to content

Commit

Permalink
Remove unnecessary mutating behavior from Parameters.getCatchall() (
Browse files Browse the repository at this point in the history
#130)

* Bump min Swift version, general package/API docs updates, update CI
* Make `getCatchall()` non-mutating by removing percent encoding in setCatchall(), the same way it's done in set(). Also use the same fallback for percent decoding in set() that we do in the catchall.
  • Loading branch information
gwynne authored May 5, 2024
1 parent 2a92a7e commit 8c9a227
Show file tree
Hide file tree
Showing 8 changed files with 97 additions and 69 deletions.
9 changes: 0 additions & 9 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -1,14 +1,5 @@
version: 2
enable-beta-ecosystems: true
updates:
- package-ecosystem: "swift"
directory: "/"
schedule:
interval: "daily"
groups:
dependencies:
patterns:
- "*"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ env:
SWIFT_DETERMINISTIC_HASHING: 1

jobs:

unit-tests:
uses: vapor/ci/.github/workflows/run-unit-tests.yml@main
secrets: inherit

upstream-check:
runs-on: ubuntu-latest
container: swift:5.9-jammy
container: swift:5.10-jammy
steps:
- name: Check out self
uses: actions/checkout@v4
Expand Down
16 changes: 11 additions & 5 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// swift-tools-version:5.7
// swift-tools-version:5.8
import PackageDescription

let package = Package(
Expand All @@ -16,9 +16,15 @@ let package = Package(
.package(url: "https://github.com/apple/swift-log.git", from: "1.5.3")
],
targets: [
.target(name: "RoutingKit", dependencies: [
.product(name: "Logging", package: "swift-log"),
]),
.testTarget(name: "RoutingKitTests", dependencies: ["RoutingKit"]),
.target(
name: "RoutingKit",
dependencies: [
.product(name: "Logging", package: "swift-log"),
]
),
.testTarget(
name: "RoutingKitTests",
dependencies: ["RoutingKit"]
),
]
)
2 changes: 2 additions & 0 deletions Package@swift-5.9.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@ let package = Package(
.product(name: "Logging", package: "swift-log"),
],
swiftSettings: [
.enableUpcomingFeature("ExistentialAny"),
.enableExperimentalFeature("StrictConcurrency=complete"),
]),
.testTarget(
name: "RoutingKitTests",
dependencies: ["RoutingKit"],
swiftSettings: [
.enableUpcomingFeature("ExistentialAny"),
.enableExperimentalFeature("StrictConcurrency=complete"),
]
),
Expand Down
27 changes: 14 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
<p align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/1130717/260613479-e5838c91-74ae-4f8f-87ce-db37b518fc13.png">
<source media="(prefers-color-scheme: light)" srcset="https://user-images.githubusercontent.com/1130717/260613528-2ba056bc-19d0-468b-b971-88d333ad70d6.png">
<img src="https://user-images.githubusercontent.com/1130717/260613528-2ba056bc-19d0-468b-b971-88d333ad70d6.png" height="96" alt="RoutingKit">
</picture>
<br>
<br>
<a href="https://docs.vapor.codes/4.0/"><img src="https://img.shields.io/badge/read_the-docs-2196f3.svg?logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCAxNiAxNiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cmVjdCB4PSIxLjQiIHk9IjIuMzUiIHdpZHRoPSIxMy4yIiBoZWlnaHQ9IjkuOCIgc3R5bGU9ImZpbGw6ICNkZmRiZjM7IG9wYWNpdHk6IDAuMzMzMzM7Ii8%2BPHBhdGggc3R5bGU9ImZpbGw6ICNmZmY7IiBkPSJNMCwxLjc1YzAsLTAuNCAwLjM1LC0wLjg1IDAuNzUsLTAuODVjMi43LDAgNS4yNSwtMC42IDcuMjUsMS40YzIsLTIgNC4yNSwtMS40IDcuMjUsLTEuNGMwLjQsMCAwLjc1LDAuNCAwLjc1LDAuODV2MTAuNWMwLDAuNCAtMC4zNSwwLjc1IC0wLjc1LDAuNzVjLTIuNSwwIC00LjgsLTAuNiAtNi43NSwxLjNjLTAuMjUsMC4yNSAtMC43NSwwLjI1IC0xLDBjLTEuNzUsLTEuNyAtNC40NSwtMS4zIC02Ljc1LC0xLjNjLTAuNCwwIC0wLjc1LC0wLjM1IC0wLjc1LC0wLjc1em03LjI1LDEwLjI1di03LjI1Yy0wLjA4LC0yLjk1IC0zLjYsLTIuMjUgLTUuNzUsLTIuMjV2OWMxLjk1LDAgMy45NSwtMC4zIDUuNzUsMC41em0xLjUsLTcuMjV2Ny4yNWMxLjc1LC0wLjg1IDMuODUsLTAuNSA1Ljc1LC0wLjV2LTljLTIuMjUsMCAtNS43NSwtMC43IC01Ljc1LDIuMjV6Ii8%2BPC9zdmc%2BCg%3D%3D&labelColor=gray" alt="Documentation"></a>
<a href="https://discord.gg/vapor"><img src="https://img.shields.io/discord/431917998102675485?logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyBmaWxsPSIjOGY4ZmZmIiB2aWV3Qm94PSIwIDAgMjQgMjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI%2BPHBhdGggZD0iTTIwLjMxNyA0LjM2OThhMTkuNzkxMyAxOS43OTEzIDAgMDAtNC44ODUxLTEuNTE1Mi4wNzQxLjA3NDEgMCAwMC0uMDc4NS4wMzcxYy0uMjExLjM3NTMtLjQ0NDcuODY0OC0uNjA4MyAxLjI0OTUtMS44NDQ3LS4yNzYyLTMuNjgtLjI3NjItNS40ODY4IDAtLjE2MzYtLjM5MzMtLjQwNTgtLjg3NDItLjYxNzctMS4yNDk1YS4wNzcuMDc3IDAgMDAtLjA3ODUtLjAzNyAxOS43MzYzIDE5LjczNjMgMCAwMC00Ljg4NTIgMS41MTUuMDY5OS4wNjk5IDAgMDAtLjAzMjEuMDI3N0MuNTMzNCA5LjA0NTgtLjMxOSAxMy41Nzk5LjA5OTIgMTguMDU3OGEuMDgyNC4wODI0IDAgMDAuMDMxMi4wNTYxYzIuMDUyOCAxLjUwNzYgNC4wNDEzIDIuNDIyOCA1Ljk5MjkgMy4wMjk0YS4wNzc3LjA3NzcgMCAwMC4wODQyLS4wMjc2Yy40NjE2LS42MzA0Ljg3MzEtMS4yOTUyIDEuMjI2LTEuOTk0MmEuMDc2LjA3NiAwIDAwLS4wNDE2LS4xMDU3Yy0uNjUyOC0uMjQ3Ni0xLjI3NDMtLjU0OTUtMS44NzIyLS44OTIzYS4wNzcuMDc3IDAgMDEtLjAwNzYtLjEyNzdjLjEyNTgtLjA5NDMuMjUxNy0uMTkyMy4zNzE4LS4yOTE0YS4wNzQzLjA3NDMgMCAwMS4wNzc2LS4wMTA1YzMuOTI3OCAxLjc5MzMgOC4xOCAxLjc5MzMgMTIuMDYxNCAwYS4wNzM5LjA3MzkgMCAwMS4wNzg1LjAwOTVjLjEyMDIuMDk5LjI0Ni4xOTgxLjM3MjguMjkyNGEuMDc3LjA3NyAwIDAxLS4wMDY2LjEyNzYgMTIuMjk4NiAxMi4yOTg2IDAgMDEtMS44NzMuODkxNC4wNzY2LjA3NjYgMCAwMC0uMDQwNy4xMDY3Yy4zNjA0LjY5OC43NzE5IDEuMzYyOCAxLjIyNSAxLjk5MzJhLjA3Ni4wNzYgMCAwMC4wODQyLjAyODZjMS45NjEtLjYwNjcgMy45NDk1LTEuNTIxOSA2LjAwMjMtMy4wMjk0YS4wNzcuMDc3IDAgMDAuMDMxMy0uMDU1MmMuNTAwNC01LjE3Ny0uODM4Mi05LjY3MzktMy41NDg1LTEzLjY2MDRhLjA2MS4wNjEgMCAwMC0uMDMxMi0uMDI4NnpNOC4wMiAxNS4zMzEyYy0xLjE4MjUgMC0yLjE1NjktMS4wODU3LTIuMTU2OS0yLjQxOSAwLTEuMzMzMi45NTU1LTIuNDE4OSAyLjE1Ny0yLjQxODkgMS4yMTA4IDAgMi4xNzU3IDEuMDk1MiAyLjE1NjggMi40MTkgMCAxLjMzMzItLjk1NTUgMi40MTg5LTIuMTU2OSAyLjQxODl6bTcuOTc0OCAwYy0xLjE4MjUgMC0yLjE1NjktMS4wODU3LTIuMTU2OS0yLjQxOSAwLTEuMzMzMi45NTU0LTIuNDE4OSAyLjE1NjktMi40MTg5IDEuMjEwOCAwIDIuMTc1NyAxLjA5NTIgMi4xNTY4IDIuNDE5IDAgMS4zMzMyLS45NDYgMi40MTg5LTIuMTU2OCAyLjQxODlaIi8%2BPC9zdmc%2B&color=%238f8fff" alt="Team Chat"></a>
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-skyblue?style=flat&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyB2aWV3Qm94PSIwIDAgMTI4IDEyOCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBmaWxsPSIjZmZmIiBkPSJNNzAuNTQsMTEuNTJjLS4xLTMuODktMTEuOS0zLjg5LTEyLDB2MTBjLTUuMjcuMTgtMjAuMzYsNy44NS0yNC40Niw4aC0xOC4zOGMtMS42LDAtNS43NTcsMi40NS01LjY5LDYuMDhjLjEsNC41NCw0LjcxLDYuNjUsOS4xOSw1LjkyYzAsMC0xNC45NTcsMzMuMTgtMTYuODgsMzcuNTJjLTMuMzcyLDcuNjEsMTIuNDcsMTQuOTcsMjIuOSwxNC43MmMxMS44LS42LDI2LjEyLTcuMzEsMjIuODYtMTMuOTJjLTIuNS01LjA3LTE2LjY4LTM4LjMyLTE2LjY4LTM4LjMyYzUuNzEsMCwxOC4zNy03Ljg1LDI3LjE0LTh2NzYuNzhoLTIwYy0zLjkxLDAtMy45MSwxMiwwLDEyaDUyYzMuOTEsMCwzLjkxLTEyLDAtMTJoLTIwdi03Ni43OGM4LjctLjEsMjEuMTQsNy45NywyNy4zNCw4YzAsMC0xNC40MSwzMi44NC0xNi44OCwzOC4zMmMtMi43MSw2LDExLjYxLDEzLjkyLDIyLjksMTMuOTJjMTEuOCwwLDI2LjEtNi4zNywyMi45LTEzLjkyYy0yLjItNS4zMS0xNi45LTM4LjMyLTE2LjktMzguMzJjNC44LjQ1LDkuNi0xLjU3LDkuNi01LjkyYy0uMS00LjYtNC02LjA5LTYuMS02LjA4YzAsMC0yMi43OS0uMi0xOC40LDBjMCwwLTE4Ljk4LTcuOTQtMjQuNDYtOHptMzIuODYsNDQuNjQsMTAuNCwyNGMtMy44LDEuNzMtMTguNTksMS4xMi0yMC44NCwwem0tNzcuNywwLDEwLjQ0LDI0Yy04LjAyLDMuMjgtMTUuMDEsMi45MS0yMC44NCwwYzAsMCwxMC4zNi0yMy45NiwxMC40LTI0eiIvPjwvc3ZnPgo%3D" alt="MIT License"></a>
<a href="https://github.com/vapor/routing-kit/actions/workflows/test.yml"><img src="https://github.com/vapor/routing-kit/actions/workflows/test.yml/badge.svg" alt="Continuous Integration"></a>
<a href="https://codecov.io/github/vapor/routing-kit"><img src="https://codecov.io/github/vapor/routing-kit/branch/main/graph/badge.svg?token=yDzzHja8lt"></a>
<a href="https://swift.org"><img src="https://img.shields.io/badge/swift-5.7%2b-white?style=flat&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI%2BPHBhdGggZD0iTSA2LDI0YyAtMywwIC02LC0zIC02LC02diAtMTJjIDAsLTMgMywtNiA2LC02aCAxMmMgMywwIDYsMyA2LDZ2IDEyYyAwLDMgLTMsNiAtNiw2eiIgZmlsbD0iI2YwNzE1OCIvPjxwYXRoIGQ9Ik0gMTMuNTUsMy40YyA0LjE1LDIuMzkgNi4zLDcuNTMgNS4zLDExLjUgMS45NSwyLjggMS42NSw1LjE3IDEuMzgsNC42NiAtMS4yLC0yLjMzIC0zLjMzLC0xLjQyIC00LjM3LC0wLjcxIC0zLjksMS44MSAtMTAuMTYsMC4xOCAtMTMuNDYsLTUuMDMgMi45OCwyLjIgNy4yLDMuMTUgMTAuMywxLjI1IC00LjYsLTMuNTcgLTguNSwtOS4xNyAtOC41LC05LjI4IDIuMjgsMi4xNSA1Ljk4LDQuODQgNy4zLDUuNzEgLTIuOCwtMy4xIC01LjMsLTYuNjUgLTUuMiwtNi42NSAyLjczLDIuNjggNS42Niw1LjIgOC45LDcuMiAwLjM3LC0wLjc5IDEuNDMsLTQuNDcgLTEuNjUsLTguNjV6IiBmaWxsPSJ3aGl0ZSIvPjwvc3ZnPg%3D%3D&logoColor=%23f07158&labelColor=gray&color=%23f07158" alt="Swift 5.7 - 5.9"></a>
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://github.com/vapor/routing-kit/assets/1130717/1b2a3781-dacd-4534-8cbb-48ff67c09f87">
<source media="(prefers-color-scheme: light)" srcset="https://github.com/vapor/routing-kit/assets/1130717/7053e66f-ea9f-4f3e-b3db-7bbf9fac1590">
<img src="https://github.com/vapor/routing-kit/assets/1130717/7053e66f-ea9f-4f3e-b3db-7bbf9fac1590" height="96" alt="RoutingKit">
</picture>
<br>
<br>
<a href="https://docs.vapor.codes/4.0/"><img src="https://design.vapor.codes/images/readthedocs.svg" alt="Documentation"></a>
<a href="https://discord.gg/vapor"><img src="https://design.vapor.codes/images/discordchat.svg" alt="Team Chat"></a>
<a href="LICENSE"><img src="https://design.vapor.codes/images/mitlicense.svg" alt="MIT License"></a>
<a href="https://github.com/vapor/routing-kit/actions/workflows/test.yml"><img src="https://img.shields.io/github/actions/workflow/status/vapor/routing-kit/test.yml?event=push&style=plastic&logo=github&label=tests&logoColor=%23ccc" alt="Continuous Integration"></a>
<a href="https://codecov.io/github/vapor/routing-kit"><img src="https://img.shields.io/codecov/c/github/vapor/routing-kit?style=plastic&logo=codecov&label=codecov"></a>
<a href="https://swift.org"><img src="https://design.vapor.codes/images/swift58up.svg" alt="Swift 5.8+"></a>
</p>

<br>
21 changes: 21 additions & 0 deletions Sources/RoutingKit/Docs.docc/Resources/vapor-routingkit-logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 21 additions & 0 deletions Sources/RoutingKit/Docs.docc/theme-settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"theme": {
"aside": { "border-radius": "16px", "border-style": "double", "border-width": "3px" },
"border-radius": "0",
"button": { "border-radius": "16px", "border-width": "1px", "border-style": "solid" },
"code": { "border-radius": "16px", "border-width": "1px", "border-style": "solid" },
"color": {
"routingkit": "#f00",
"documentation-intro-fill": "radial-gradient(circle at top, var(--color-routingkit) 30%, #000 100%)",
"documentation-intro-accent": "var(--color-routingkit)",
"logo-base": { "dark": "#fff", "light": "#000" },
"logo-shape": { "dark": "#000", "light": "#fff" },
"fill": { "dark": "#000", "light": "#fff" }
},
"icons": { "technology": "/routingkit/images/vapor-routingkit-logo.svg" }
},
"features": {
"quickNavigation": { "enable": true },
"i18n": { "enable": true }
}
}
66 changes: 26 additions & 40 deletions Sources/RoutingKit/Parameters.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,41 +4,42 @@ import Logging
/// Holds dynamic path components that were discovered while routing.
///
/// After this struct has been filled with parameter values, you can fetch
/// them out by name using `get(_:)`.
/// them out by name using ``get(_:)`` or ``get(_:as:)``.
///
/// let postID = parameters.get("post_id")
///
public struct Parameters: Sendable {
/// Internal storage.
private var values: [String: String]
private var catchall: Catchall
private var catchall: [String]

/// The configured logger.
public let logger: Logger

/// Return a list of all parameter names which were captured. Does not include values listed in the catchall.
public var allNames: Set<String> { .init(self.values.keys) }

/// Creates a new `Parameters`.
/// Create a new `Parameters`.
///
/// Pass this into the `Router.route(path:parameters:)` method to fill with values.
/// Pass this to ``Router/route(path:parameters:)`` to fill with values.
public init() {
self.values = [:]
self.catchall = Catchall()
self.logger = Logger(label: "codes.vapor.routingkit")
self.init(nil)
}

/// Creates a new `Parameters`.
/// Pass this into the `Router.route(path:parameters:)` method to fill with values.
/// - Parameters:
/// - logger: The logger to be used. If none is provided, a default one will be created.
/// Create a new `Parameters`.
///
/// Pass this to ``Router/route(path:parameters:)`` to fill with values.
///
/// - Parameter logger: The logger to be used. If none is provided, a default one will be created.
public init(_ logger: Logger?) {
self.values = [:]
self.catchall = Catchall()
self.logger = logger ?? Logger(label: "codes.vapor.routingkit")
self.catchall = []
self.logger = logger ?? .init(label: "codes.vapor.routingkit")
}

/// Grabs the named parameter from the parameter bag.
///
/// For example GET /posts/:post_id/comments/:comment_id
/// For example `GET /posts/:post_id/comments/:comment_id`
/// would be fetched using:
///
/// let postID = parameters.get("post_id")
Expand All @@ -51,27 +52,25 @@ public struct Parameters: Sendable {
/// Grabs the named parameter from the parameter bag, casting it to
/// a `LosslessStringConvertible` type.
///
/// For example GET /posts/:post_id/comments/:comment_id
/// For example `GET /posts/:post_id/comments/:comment_id`
/// would be fetched using:
///
/// let postID = parameters.get("post_id", as: Int.self)
/// let commentID = parameters.get("comment_id", as: Int.self)
///
public func get<T>(_ name: String, as type: T.Type = T.self) -> T?
where T: LosslessStringConvertible
{
public func get<T: LosslessStringConvertible>(_ name: String, as type: T.Type = T.self) -> T? {
self.get(name).flatMap(T.init)
}

/// Adds a new parameter value to the bag.
///
/// - note: The value will be percent-decoded.
/// > Note: The value will be percent-decoded.
///
/// - parameters:
/// - Parameters:
/// - name: Unique parameter name
/// - value: Value (percent-encoded if necessary)
public mutating func set(_ name: String, to value: String?) {
self.values[name] = value?.removingPercentEncoding
self.values[name] = value.map { $0.removingPercentEncoding ?? $0 }
}

/// Fetches the components matched by `catchall` (`**`).
Expand All @@ -85,30 +84,17 @@ public struct Parameters: Sendable {
/// // not hit
/// }
///
/// - note: The value will be percent-decoded.
/// > Note: The value will be percent-decoded.
///
/// - returns: The path components matched
public mutating func getCatchall() -> [String] {
if self.catchall.isPercentEncoded {
self.catchall.values = self.catchall.values.map { $0.removingPercentEncoding ?? $0 }
self.catchall.isPercentEncoded = false
}
return self.catchall.values
/// - Returns: The path components matched.
public func getCatchall() -> [String] {
self.catchall
}

/// Stores the components matched by `catchall` (`**`).
///
/// - parameters:
/// - matched: The subpaths matched (percent-encoded if necessary)
/// - Parameter matched: The subpaths matched (percent-encoded if necessary)
public mutating func setCatchall(matched: [String]) {
self.catchall = Catchall(values: matched)
}

/// Holds path components that were matched by `catchall` (`**`).
///
/// Used internally.
private struct Catchall {
var values: [String] = []
var isPercentEncoded: Bool = true
self.catchall = matched.map { $0.removingPercentEncoding ?? $0 }
}
}

0 comments on commit 8c9a227

Please sign in to comment.