Hi,
I'm trying to implement a router that calls different handler functions based on a context object. I took Axum's router as inspiration. I already found a very good explanation of how Axum extracts different parts of a request with so called "extractors" (GitHub - alexpusch/rust-magic-function-params: Example for Axum style magic function parameter passing).
I adapted this example a little and now I'm trying to figure out, how to store handler functions in a HashMap
but struggle with finding a correct data type that allows me to store functions with a different amount of parameters.
This is my code at the moment:
Code
// Context and FromContext trait ///////////////////////////////////////////////
#[derive(Clone)]
pub struct Context {
path: String,
id: String,
}
impl Context {
pub fn default() -> Self {
Context {
path: "/path/to/hello_world".to_string(),
id : "123456789".to_string()
}
}
}
pub trait FromContext {
fn from_context(ctx: &Context) -> Self;
}
// Extractors //////////////////////////////////////////////////////////////////
pub struct Path(String);
impl FromContext for Path {
fn from_context(ctx: &Context) -> Self {
Path(ctx.path.to_string())
}
}
pub struct Id(String);
impl FromContext for Id {
fn from_context(ctx: &Context) -> Self {
Id(ctx.id.to_string())
}
}
// Handler Trait and Implementation ////////////////////////////////////////////
pub trait Handler<T> {
fn call(self, ctx: Context);
}
impl<F, T> Handler<T> for F
where
F: Fn(T),
T: FromContext,
{
fn call(self, ctx: Context) {
(self)(T::from_context(&ctx));
}
}
impl<F, T1, T2> Handler<(T1, T2)> for F
where
F: Fn(T1, T2),
T1: FromContext,
T2: FromContext,
{
fn call(self, ctx: Context) {
(self)(T1::from_context(&ctx), T2::from_context(&ctx));
}
}
// Trigger Function ////////////////////////////////////////////////////////////
pub fn trigger<T, H>(ctx: Context, handler: H)
where
H: Handler<T>,
{
handler.call(ctx);
}
// Handler /////////////////////////////////////////////////////////////////////
fn handler_1(Path(p): Path, Id(id): Id) {
println!("Handler 1: path={}; id={}", p, id);
}
fn handler_2(Path(p): Path) {
println!("Handler 2: path={}", p);
}
// Main ////////////////////////////////////////////////////////////////////////
fn main() {
let ctx = Context::default();
trigger(ctx.clone(), handler_1);
trigger(ctx.clone(), handler_2);
let h1: Box<dyn Handler<(Path, Id)>> = Box::new(handler_1);
let h2: Box<dyn Handler<Path>> = Box::new(handler_2);
// How to store h1 and h2 in the same hash map?
}
And if you want to run it here's a lilnk to Rust playground.
As you can see I tried boxing the Handler<T>
but the generic type parameter T
remains a problem.
I hope someone of you has an idea of how to solve this. Thanks a lot in advance