Skip to main content

oxapi_impl/
router.rs

1//! Router generation for axum.
2
3use proc_macro2::TokenStream;
4use quote::{format_ident, quote};
5
6use crate::Generator;
7use crate::openapi::HttpMethod;
8
9/// Generator for axum router code.
10pub struct RouterGenerator<'a> {
11    #[allow(dead_code)]
12    generator: &'a Generator,
13}
14
15impl<'a> RouterGenerator<'a> {
16    pub fn new(generator: &'a Generator) -> Self {
17        Self { generator }
18    }
19
20    /// Generate the body of the map_routes function.
21    ///
22    /// The methods parameter is a list of (method_name, http_method, path) tuples.
23    pub fn generate_map_routes(&self, methods: &[(syn::Ident, HttpMethod, String)]) -> TokenStream {
24        if methods.is_empty() {
25            return quote! { router };
26        }
27
28        let routes = methods.iter().map(|(method_name, http_method, path)| {
29            let axum_path = convert_path_to_axum(path);
30            let method_fn = http_method_to_axum_fn(*http_method);
31
32            quote! {
33                .route(#axum_path, ::axum::routing::#method_fn(Self::#method_name))
34            }
35        });
36
37        quote! {
38            router
39                #(#routes)*
40        }
41    }
42}
43
44/// Convert an OpenAPI path to axum path format.
45///
46/// OpenAPI uses `{param}` and axum 0.8+ also uses `{param}`.
47/// Earlier axum versions used `:param` but 0.8 uses `{param}`.
48fn convert_path_to_axum(path: &str) -> String {
49    // OpenAPI and axum 0.8+ both use {param} format, so just pass through
50    path.to_string()
51}
52
53/// Get the axum routing function for an HTTP method.
54fn http_method_to_axum_fn(method: HttpMethod) -> syn::Ident {
55    format_ident!(
56        "{}",
57        match method {
58            HttpMethod::Get => "get",
59            HttpMethod::Post => "post",
60            HttpMethod::Put => "put",
61            HttpMethod::Delete => "delete",
62            HttpMethod::Patch => "patch",
63            HttpMethod::Head => "head",
64            HttpMethod::Options => "options",
65        }
66    )
67}
68
69#[cfg(test)]
70mod tests {
71    use super::*;
72
73    #[test]
74    fn test_convert_path_simple() {
75        assert_eq!(convert_path_to_axum("/users"), "/users");
76    }
77
78    #[test]
79    fn test_convert_path_with_param() {
80        assert_eq!(convert_path_to_axum("/users/{id}"), "/users/{id}");
81    }
82
83    #[test]
84    fn test_convert_path_multiple_params() {
85        assert_eq!(
86            convert_path_to_axum("/users/{user_id}/posts/{post_id}"),
87            "/users/{user_id}/posts/{post_id}"
88        );
89    }
90}