dynamic traits are not supported yet

Hello. Nooby question. I am working on some middleware for the lightbug project and want to build a chain-of-responsibility design pattern to handle the different aspects of processing a request / response:
## Middleware is an interface for processing HTTP requests.
## Each middleware in the chain can modify the request and response.
trait Middleware:
fn set_next(self, next: Middleware):
...
fn call(self, context: Context) -> HTTPResponse:
...

## MiddlewareChain is a chain of middleware that processes the request.
## The chain is a linked list of middleware objects.
struct MiddlewareChain(HTTPService):
var root: Middleware

fn add(self, middleware: Middleware):
if self.root == nil:
self.root = middleware
else:
var current = self.root
while current.next != nil:
current = current.next
current.set_next(middleware)

fn call(self, request: HTTPRequest) raises -> HTTPResponse:
var context = Context(request)
return self.root.call(context)
## Middleware is an interface for processing HTTP requests.
## Each middleware in the chain can modify the request and response.
trait Middleware:
fn set_next(self, next: Middleware):
...
fn call(self, context: Context) -> HTTPResponse:
...

## MiddlewareChain is a chain of middleware that processes the request.
## The chain is a linked list of middleware objects.
struct MiddlewareChain(HTTPService):
var root: Middleware

fn add(self, middleware: Middleware):
if self.root == nil:
self.root = middleware
else:
var current = self.root
while current.next != nil:
current = current.next
current.set_next(middleware)

fn call(self, request: HTTPRequest) raises -> HTTPResponse:
var context = Context(request)
return self.root.call(context)
This is how we would build the MiddlewareChain:
var middleware = MiddlewareChain()
middleware.add(CompressionMiddleware())
middleware.add(ErrorMiddleware())
middleware.add(LoggerMiddleware())
middleware.add(CorsMiddleware(allows_origin = "*"))
middleware.add(BasicAuthMiddleware("admin", "password"))
middleware.add(StaticMiddleware("static"))
middleware.add(router)
middleware.add(NotFoundMiddleware())
var middleware = MiddlewareChain()
middleware.add(CompressionMiddleware())
middleware.add(ErrorMiddleware())
middleware.add(LoggerMiddleware())
middleware.add(CorsMiddleware(allows_origin = "*"))
middleware.add(BasicAuthMiddleware("admin", "password"))
middleware.add(StaticMiddleware("static"))
middleware.add(router)
middleware.add(NotFoundMiddleware())
I'm getting the TODO: Dynamic traits are not supported yet. please use a compile time generic instead. I have tried using a Pointer[Middleware]` but you need the type to cast and make the call. I also tried passing in a concrete middleware type as a parameter at compile time but it requires some really ugly code to chain them together. Are there any other options? Let me know if anyone has a solution that doesn't look completely crazy.
3 Replies
tkeitt
tkeitt3mo ago
Dunno. But I sure could use generic traits already.
Ryulord
Ryulord3mo ago
Currently you need to explicitly do runtime type checking yourself using the variant type like so:
if middleware.isa[CompressionMiddleware]():
middleware[CompressionMiddleware].set_next(next)
if middleware.isa[CompressionMiddleware]():
middleware[CompressionMiddleware].set_next(next)
If you're going to be doing lots of these often you could make a helper struct like the following:
@value
struct DynamicMiddleware:
var _middleware: Variant[CompressionMiddleware, ErrorMiddleware]

fn set_next(inout self, owned next: DynamicMiddleware):
if self._middleware.isa[CompressionMiddleware]():
self._middleware[CompressionMiddleware].set_next(next)
elif self._middleware.isa[ErrorMiddleware]():
self._middleware[ErrorMiddleware].set_next(next)
@value
struct DynamicMiddleware:
var _middleware: Variant[CompressionMiddleware, ErrorMiddleware]

fn set_next(inout self, owned next: DynamicMiddleware):
if self._middleware.isa[CompressionMiddleware]():
self._middleware[CompressionMiddleware].set_next(next)
elif self._middleware.isa[ErrorMiddleware]():
self._middleware[ErrorMiddleware].set_next(next)
Then you can just use DynamicMiddleware any place you have some kind of Middleware but don't know which one until runtime. It can also be useful to create an __init__ on DynamicMiddleware method for each type in the variant. This would let you write code like middleware.add(ErrorMiddleware()) instead of stuff like middleware.add(DynamicMiddleware(ErrorMiddleware()))
a2svior
a2svior3mo ago
@Ryulord thanks! This is helpful!
Want results from more Discord servers?
Add your server