Closed connor-ricks closed 2 months ago
I guess you could extend your controller to conform to MiddlewareProtocol and in the handle function pass it onto a version of endpoints created internally.
protocol Controller<Context> {
var endpoints: RouterMiddleware<Context> { get }
}
extension Controller: MiddlewareProtocol {
func handle(request:Input, context: Context, next...) async throw -> Output {
try await endpoints.handle(request, context, next)
}
I'm not sure how performant that'd be as endpoints would be called every call to handle.
We can probably add something to the result builder so we cache the endpoints variable.
Would the caching part not also be a concern in the existing approach too?
Playing around with this a bit more, I'm stuck here trying to mimic View
's body.
public protocol Controller: MiddlewareProtocol where Context: RouterRequestContext {
associatedtype Body
@RouteBuilder<Context>
var body: Body { get }
}
extension Controller where Body: RouterMiddleware<Context>, Body.Input == Input, Body.Output == Output, Body.Context == Context {
public func handle(_ input: Input, context: Context, next: (Input, Context) async throws -> Output) async throws -> Output {
try await body.handle(input, context: context, next: next)
}
}
struct FooController: Controller {
typealias Context = BasicRouterRequestContext
var body: some RouterMiddleware<Context> {
RouteGroup("foo") {
Get("bar") { _, _ in
return "Hello world!"
}
}
}
}
Sadly this has a couple compile errors...
I feel like there's just a small piece I'm missing here to get these conformances to work together. The context generic definitely increases the complexity a bit more than what I had tackled with Vapor
's approach.
I also realize that I haven't 100% wrapped my head around the nuance and structure of Hummingbird yet, so my contributions to this conversation could be a little out of left field...
My aim was simply to try and tackle a solution that would be a bit more familiar for developers who have used result builders elsewhere in the Swift ecosystem. The composability is a major plus for organization, and the Body
style syntax you see in things like SwiftUI's View
and PointFree's Parser
/Reducer
feels much more ergonomic and natural.
Would the caching part not also be a concern in the existing approach too?
No it wouldn't as the endpoint variable is only referenced when the full result builder is built.
Regarding your code I can't tell as I'm on holiday and don't have a laptop with me.
Although maybe requiring Body to conform to RouterMiddleware will help
You're on holiday?! Go away and enjoy it! Let's talk again when you're back!
I managed to get this to compile this morning... Caching is still a potential issue.
protocol Controller: RouterMiddleware {
associatedtype Body: RouterMiddleware
var body: Body { get }
}
extension Controller where Body.Input == Input, Body.Context == Context, Body.Output == Output {
func handle(_ input: Input, context: Context, next: (Input, Context) async throws -> Output) async throws -> Output {
try await body.handle(input, context: context, next: next)
}
}
struct UserController: Controller {
typealias Context = BasicRouterRequestContext
var body: some RouterMiddleware<Context> {
RouteGroup("user") {
Get { _,_ in
"User"
}
}
}
}
func foo() {
let router = RouterBuilder {
UserController()
}
let app = Application(responder: router)
}
Closing this issue to migrate the conversation to the actual Hummingbird repo.
Hello,
I was experimenting with Hummingbird today and saw the
HummingbirdRouter
target was added for result builder style syntax.While I managed to use the result builder syntax to get some routes created, I was wondering if at this point in time, it was possible to make the content feel more composable.
Most of the examples build route collections using a property called
endpoints
but then when they add them, they explicitly call.endpoints
.I was hoping to achieve a syntax more like this...
Just some rough syntax, but hopefully you get the idea.
Is this possible at the moment? Perusing the implementation, it seems maybe not quite, but perhaps it is pretty close.
I achieved a similar syntax with
Vapor
. You can take a peek here.