axum_distributed_routing/
lib.rs

1pub use axum_distributed_routing_macros::*;
2pub use inventory;
3
4use axum::routing::Router;
5
6/// A trait for defining a route. All routes must implement this trait
7///
8/// A route is not necessarily an HTTP route, it could be anything that can be
9/// attached to a router (for example, a nested router).
10///
11/// You typically use the `route_group!` macro to define a route struct that
12/// implements this trait, but you can also do it manually.
13/// Be sure to create a function `new` (you can find the signature in the
14/// `route_group!` macro)
15pub trait Route {
16    type State: Clone + Send + Sync + 'static;
17
18    fn attach(&self, router: Router<Self::State>, level: usize) -> Router<Self::State>;
19
20    fn path(&self) -> &'static str;
21}
22
23/// Define a route group
24///
25/// A route group is used to group routes together. It is then used in the
26/// `create_router!` macro. It is effectively a type that implements the
27/// `Route` trait.
28///
29/// You can also use this macro to define a nested route group, simply add the
30/// parent group and the subpath as the third and fourth arguments
31#[macro_export]
32macro_rules! route_group {
33    ($vis:vis $name:ident, $type:ty, $parent:ident, $path:literal) => {
34        $crate::route_group!($vis $name, $type);
35        $crate::inventory::submit!($parent::new($path, |router, level| {
36            router.nest($path, $crate::create_router::<$name>(level + 4))
37        }));
38    };
39    ($vis:vis $name:ident, $type:ty) => {
40        #[derive(Copy, Clone, Debug)]
41        $vis struct $name {
42            path: &'static str,
43            handler: fn(axum::routing::Router<$type>, usize) -> axum::routing::Router<$type>,
44        }
45
46        impl $name {
47            pub const fn new(
48                path: &'static str,
49                handler: fn(axum::routing::Router<$type>, usize) -> axum::routing::Router<$type>,
50            ) -> Self {
51                Self { path, handler }
52            }
53        }
54
55        impl $crate::Route for $name {
56            type State = $type;
57
58            fn attach(
59                &self,
60                router: axum::routing::Router<$type>,
61                level: usize,
62            ) -> axum::routing::Router<$type> {
63                (self.handler)(router, level)
64            }
65
66            fn path(&self) -> &'static str {
67                self.path
68            }
69        }
70
71        $crate::inventory::collect!($name);
72    };
73}
74
75/// Returns an iterator over the routes of the provided group
76#[macro_export]
77macro_rules! routes {
78    ($type:ty) => {
79        $crate::inventory::iter::<$type>
80    };
81}
82
83/// Creates a router from the provided group
84#[macro_export]
85macro_rules! create_router {
86    ($type:ty) => {
87        $crate::create_router::<$type>(0)
88    };
89}
90
91#[doc(hidden)]
92pub fn create_router<T: Route + 'static>(level: usize) -> Router<T::State>
93where
94    inventory::iter<T>: IntoIterator<Item = &'static T>,
95{
96    let mut router = Router::new();
97    for route in inventory::iter::<T> {
98        router = route.attach(router, level);
99    }
100    router
101}
102
103// TODO: tests