Skip to content

Commit c16e183

Browse files
committed
✨ Add support for route modifiers.
1 parent 0976dce commit c16e183

File tree

13 files changed

+236
-74
lines changed

13 files changed

+236
-74
lines changed

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ Build Vapor routes using a declarative syntax langauge similar to how you would
66

77
- [Confirmed] Add README usage docs.
88
- [Confirmed] Implement Websocket support
9-
- [Confirmed] Implement RouteModifier support
109
- [Maybe] Implement EnvironmentObject style support.
1110
- [Maybe] Implement Service support
1211

Sources/VaporRouteBuilder/RouteComponents/Groups/Group+Middleware.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
import Vapor
2424

2525
extension Group {
26-
/// Creates a group of content under a path.
26+
/// Groups content under the provided middleware.
2727
///
2828
/// See ``Group.init(middleware:content:)`` for more info.
2929
public struct Middleware<Middlewares: Collection<any Vapor.Middleware>>: RouteComponent {

Sources/VaporRouteBuilder/RouteComponents/Groups/Group+Path.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
import Vapor
2424

2525
extension Group {
26-
/// Creates a group of content under a path.
26+
/// Groups content under a provided path.
2727
///
2828
/// See ``Group.init(path:content:)`` for more info.
2929
public struct Path<Path: Collection<PathComponent>>: RouteComponent {

Sources/VaporRouteBuilder/RouteComponents/HTTPMethods/DELETE.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public struct DELETE: RouteComponent {
4444
/// - use: The closure handler for incoming requests.
4545
@inlinable
4646
public init<Path: Collection, Response: ResponseEncodable>(
47-
path: Path,
47+
_ path: Path,
4848
strategy: HTTPBodyStreamStrategy = .collect,
4949
use: @Sendable @escaping (Request) throws -> Response
5050
) where Path.Element == PathComponent {
@@ -59,7 +59,7 @@ public struct DELETE: RouteComponent {
5959
/// - use: The closure handler for incoming requests.
6060
@inlinable
6161
public init<Path: Collection, Response: AsyncResponseEncodable>(
62-
path: Path,
62+
_ path: Path,
6363
strategy: HTTPBodyStreamStrategy = .collect,
6464
use: @Sendable @escaping (Request) async throws -> Response
6565
) where Path.Element == PathComponent {
@@ -74,7 +74,7 @@ public struct DELETE: RouteComponent {
7474
/// - use: The closure handler for incoming requests.
7575
@inlinable
7676
public init<Response: ResponseEncodable>(
77-
path: PathComponent...,
77+
_ path: PathComponent...,
7878
strategy: HTTPBodyStreamStrategy = .collect,
7979
use: @Sendable @escaping (Request) throws -> Response
8080
) {
@@ -89,7 +89,7 @@ public struct DELETE: RouteComponent {
8989
/// - use: The closure handler for incoming requests.
9090
@inlinable
9191
public init<Response: AsyncResponseEncodable>(
92-
path: PathComponent...,
92+
_ path: PathComponent...,
9393
strategy: HTTPBodyStreamStrategy = .collect,
9494
use: @Sendable @escaping (Request) async throws -> Response
9595
) {

Sources/VaporRouteBuilder/RouteComponents/HTTPMethods/GET.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public struct GET: RouteComponent {
4444
/// - use: The closure handler for incoming requests.
4545
@inlinable
4646
public init<Path: Collection, Response: ResponseEncodable>(
47-
path: Path,
47+
_ path: Path,
4848
strategy: HTTPBodyStreamStrategy = .collect,
4949
use: @Sendable @escaping (Request) throws -> Response
5050
) where Path.Element == PathComponent {
@@ -59,7 +59,7 @@ public struct GET: RouteComponent {
5959
/// - use: The closure handler for incoming requests.
6060
@inlinable
6161
public init<Path: Collection, Response: AsyncResponseEncodable>(
62-
path: Path,
62+
_ path: Path,
6363
strategy: HTTPBodyStreamStrategy = .collect,
6464
use: @Sendable @escaping (Request) async throws -> Response
6565
) where Path.Element == PathComponent {
@@ -74,7 +74,7 @@ public struct GET: RouteComponent {
7474
/// - use: The closure handler for incoming requests.
7575
@inlinable
7676
public init<Response: ResponseEncodable>(
77-
path: PathComponent...,
77+
_ path: PathComponent...,
7878
strategy: HTTPBodyStreamStrategy = .collect,
7979
use: @Sendable @escaping (Request) throws -> Response
8080
) {
@@ -89,7 +89,7 @@ public struct GET: RouteComponent {
8989
/// - use: The closure handler for incoming requests.
9090
@inlinable
9191
public init<Response: AsyncResponseEncodable>(
92-
path: PathComponent...,
92+
_ path: PathComponent...,
9393
strategy: HTTPBodyStreamStrategy = .collect,
9494
use: @Sendable @escaping (Request) async throws -> Response
9595
) {

Sources/VaporRouteBuilder/RouteComponents/HTTPMethods/PATCH.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public struct PATCH: RouteComponent {
4444
/// - use: The closure handler for incoming requests.
4545
@inlinable
4646
public init<Path: Collection, Response: ResponseEncodable>(
47-
path: Path,
47+
_ path: Path,
4848
strategy: HTTPBodyStreamStrategy = .collect,
4949
use: @Sendable @escaping (Request) throws -> Response
5050
) where Path.Element == PathComponent {
@@ -59,7 +59,7 @@ public struct PATCH: RouteComponent {
5959
/// - use: The closure handler for incoming requests.
6060
@inlinable
6161
public init<Path: Collection, Response: AsyncResponseEncodable>(
62-
path: Path,
62+
_ path: Path,
6363
strategy: HTTPBodyStreamStrategy = .collect,
6464
use: @Sendable @escaping (Request) async throws -> Response
6565
) where Path.Element == PathComponent {
@@ -74,7 +74,7 @@ public struct PATCH: RouteComponent {
7474
/// - use: The closure handler for incoming requests.
7575
@inlinable
7676
public init<Response: ResponseEncodable>(
77-
path: PathComponent...,
77+
_ path: PathComponent...,
7878
strategy: HTTPBodyStreamStrategy = .collect,
7979
use: @Sendable @escaping (Request) throws -> Response
8080
) {
@@ -89,7 +89,7 @@ public struct PATCH: RouteComponent {
8989
/// - use: The closure handler for incoming requests.
9090
@inlinable
9191
public init<Response: AsyncResponseEncodable>(
92-
path: PathComponent...,
92+
_ path: PathComponent...,
9393
strategy: HTTPBodyStreamStrategy = .collect,
9494
use: @Sendable @escaping (Request) async throws -> Response
9595
) {

Sources/VaporRouteBuilder/RouteComponents/HTTPMethods/POST.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public struct POST: RouteComponent {
4444
/// - use: The closure handler for incoming requests.
4545
@inlinable
4646
public init<Path: Collection, Response: ResponseEncodable>(
47-
path: Path,
47+
_ path: Path,
4848
strategy: HTTPBodyStreamStrategy = .collect,
4949
use: @Sendable @escaping (Request) throws -> Response
5050
) where Path.Element == PathComponent {
@@ -59,7 +59,7 @@ public struct POST: RouteComponent {
5959
/// - use: The closure handler for incoming requests.
6060
@inlinable
6161
public init<Path: Collection, Response: AsyncResponseEncodable>(
62-
path: Path,
62+
_ path: Path,
6363
strategy: HTTPBodyStreamStrategy = .collect,
6464
use: @Sendable @escaping (Request) async throws -> Response
6565
) where Path.Element == PathComponent {
@@ -74,7 +74,7 @@ public struct POST: RouteComponent {
7474
/// - use: The closure handler for incoming requests.
7575
@inlinable
7676
public init<Response: ResponseEncodable>(
77-
path: PathComponent...,
77+
_ path: PathComponent...,
7878
strategy: HTTPBodyStreamStrategy = .collect,
7979
use: @Sendable @escaping (Request) throws -> Response
8080
) {
@@ -89,7 +89,7 @@ public struct POST: RouteComponent {
8989
/// - use: The closure handler for incoming requests.
9090
@inlinable
9191
public init<Response: AsyncResponseEncodable>(
92-
path: PathComponent...,
92+
_ path: PathComponent...,
9393
strategy: HTTPBodyStreamStrategy = .collect,
9494
use: @Sendable @escaping (Request) async throws -> Response
9595
) {

Sources/VaporRouteBuilder/RouteComponents/HTTPMethods/PUT.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public struct PUT: RouteComponent {
4444
/// - use: The closure handler for incoming requests.
4545
@inlinable
4646
public init<Path: Collection, Response: ResponseEncodable>(
47-
path: Path,
47+
_ path: Path,
4848
strategy: HTTPBodyStreamStrategy = .collect,
4949
use: @Sendable @escaping (Request) throws -> Response
5050
) where Path.Element == PathComponent {
@@ -59,7 +59,7 @@ public struct PUT: RouteComponent {
5959
/// - use: The closure handler for incoming requests.
6060
@inlinable
6161
public init<Path: Collection, Response: AsyncResponseEncodable>(
62-
path: Path,
62+
_ path: Path,
6363
strategy: HTTPBodyStreamStrategy = .collect,
6464
use: @Sendable @escaping (Request) async throws -> Response
6565
) where Path.Element == PathComponent {
@@ -74,7 +74,7 @@ public struct PUT: RouteComponent {
7474
/// - use: The closure handler for incoming requests.
7575
@inlinable
7676
public init<Response: ResponseEncodable>(
77-
path: PathComponent...,
77+
_ path: PathComponent...,
7878
strategy: HTTPBodyStreamStrategy = .collect,
7979
use: @Sendable @escaping (Request) throws -> Response
8080
) {
@@ -89,7 +89,7 @@ public struct PUT: RouteComponent {
8989
/// - use: The closure handler for incoming requests.
9090
@inlinable
9191
public init<Response: AsyncResponseEncodable>(
92-
path: PathComponent...,
92+
_ path: PathComponent...,
9393
strategy: HTTPBodyStreamStrategy = .collect,
9494
use: @Sendable @escaping (Request) async throws -> Response
9595
) {

Sources/VaporRouteBuilder/RouteModifier.swift

Lines changed: 47 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -20,50 +20,51 @@
2020
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2121
// SOFTWARE.
2222

23-
// import SwiftUI
24-
//
25-
// public protocol MyViewModifier<Body> {
26-
// associatedtype Body: View
27-
//
28-
// @ViewBuilder
29-
// func body(content: Content) -> Body
30-
//
31-
// typealias Content = ProxyView<Self>
32-
// }
33-
//
34-
// public struct ProxyView<Value> {
35-
// let content: AnyView
36-
// init(_ content: some View) { self.content = AnyView(content) }
37-
// }
38-
//
39-
// struct TestModifier: MyViewModifier {
40-
// func body(content: Content) -> some View {
41-
// VStack {
42-
// content.content
43-
// }
44-
// }
45-
// }
23+
import Vapor
4624

47-
// import Vapor
48-
//
49-
// public protocol MyViewModifier<Body> {
50-
// associatedtype Body: RouteComponent
51-
//
52-
// @RouteBuilder
53-
// func body(content: Content) -> Body
54-
//
55-
// typealias Content = ProxyView<Self>
56-
// }
57-
//
58-
// public struct ProxyView<Value> {
59-
// let content: AnyRouteComponent
60-
// init(_ content: some RouteComponent) { self.content = AnyRouteComponent(content) }
61-
// }
62-
//
63-
// struct TestModifier: MyViewModifier {
64-
// func body(content: Content) -> some RouteComponent {
65-
// Group {
66-
// content.content
67-
// }
68-
// }
69-
// }
25+
// MARK: - RouteModifier
26+
27+
/// A modifier that you apply to a route component or another route modifier, producing a different version of the original value.
28+
///
29+
/// Adopt the `RouteModifier` protocol when you want to create a reusable modifier that you can apply to any route component.
30+
public protocol RouteModifier<Body> {
31+
associatedtype Body: RouteComponent
32+
33+
@RouteBuilder
34+
func body(content: RouteContent) -> Body
35+
36+
typealias RouteContent = RouteModifierContent<Self>
37+
}
38+
39+
// MARK: - RouteModifierContent
40+
41+
public struct RouteModifierContent<Value>: RouteComponent {
42+
43+
// MARK: Properties
44+
45+
@usableFromInline
46+
let content: AnyRouteComponent
47+
48+
// MARK: Initializers
49+
50+
@inlinable
51+
init(_ content: some RouteComponent) {
52+
self.content = AnyRouteComponent(content)
53+
}
54+
55+
// MARK: Body
56+
57+
public var body: some RouteComponent {
58+
content
59+
}
60+
}
61+
62+
// MARK: - RouteComponent + Modifier
63+
64+
extension RouteComponent {
65+
/// Applies a modifier to a route component and returns a new route component.
66+
@inlinable
67+
public func modifier<Modifier: RouteModifier>(_ modifier: Modifier) -> some RouteComponent {
68+
modifier.body(content: RouteModifierContent(self))
69+
}
70+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
//
2+
// MIT License
3+
//
4+
// Copyright (c) 2024 Connor Ricks
5+
//
6+
// Permission is hereby granted, free of charge, to any person obtaining a copy
7+
// of this software and associated documentation files (the "Software"), to deal
8+
// in the Software without restriction, including without limitation the rights
9+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
// copies of the Software, and to permit persons to whom the Software is
11+
// furnished to do so, subject to the following conditions:
12+
//
13+
// The above copyright notice and this permission notice shall be included in all
14+
// copies or substantial portions of the Software.
15+
//
16+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
// SOFTWARE.
23+
24+
import Vapor
25+
26+
// MARK: - MiddlewareModifier
27+
28+
/// A route modifier that wraps its content in the provided middleware.
29+
private struct MiddlewareModifier: RouteModifier {
30+
31+
// MARK: Properties
32+
33+
/// The middleware to add to the content.
34+
let middleware: [Middleware]
35+
36+
// MARK: Body
37+
38+
func body(content: RouteContent) -> some RouteComponent {
39+
Group(middleware: middleware) {
40+
content
41+
}
42+
}
43+
}
44+
45+
// MARK: - RouteComponent + Middleware
46+
47+
extension RouteComponent {
48+
/// Modifies the route component by wrapping it in the provided `Middleware`.
49+
public func middleware(_ middleware: any Middleware...) -> some RouteComponent {
50+
modifier(MiddlewareModifier(middleware: middleware))
51+
}
52+
}

0 commit comments

Comments
 (0)