titan_router_derive/
lib.rs

1use proc_macro::TokenStream as TokenStream1;
2use proc_macro2::TokenStream as TokenStream2;
3use syn::{
4  braced,
5  parse::{Parse, ParseStream},
6  parse_macro_input, Expr, LitStr, Token,
7};
8
9// Define a struct to represent a single "/path" => ident entry
10struct PathMapping {
11  path: LitStr,
12  _arrow: Token![=>],
13  ident: Expr,
14}
15
16// Define a struct to represent the entire block
17struct PathMappings {
18  mappings: Vec<PathMapping>,
19}
20
21impl Parse for PathMapping {
22  fn parse(input: ParseStream) -> syn::Result<Self> {
23    let path = input.parse()?; // Parse the string literal ("/path")
24    let _arrow = input.parse()?; // Parse the => token
25    let ident = input.parse()?; // Parse the single identifier
26    Ok(PathMapping { path, _arrow, ident })
27  }
28}
29
30impl Parse for PathMappings {
31  fn parse(input: ParseStream) -> syn::Result<Self> {
32    let content;
33    let _braces = braced!(content in input); // Parse the enclosing {}
34
35    let mut mappings = Vec::new();
36    while !content.is_empty() {
37      mappings.push(content.parse()?); // Parse each "/path" => ident entry
38      if content.peek(Token![,]) {
39        content.parse::<Token![,]>()?; // Consume the comma, if present
40      }
41    }
42
43    Ok(PathMappings { mappings })
44  }
45}
46
47#[proc_macro]
48pub fn define_routes(input: TokenStream1) -> TokenStream1 {
49  let path_mappings = parse_macro_input!(input as PathMappings);
50
51  impl_define_routes(path_mappings).into()
52}
53
54fn impl_define_routes(mappings: PathMappings) -> TokenStream2 {
55  let mut base = Vec::from_iter([quote::quote! {
56      let mut router = Router::default();
57  }]);
58
59  for mapping in mappings.mappings {
60    let key = mapping.path;
61    let value = mapping.ident;
62    base.push(quote::quote! {
63        router.at(#key, #value);
64    });
65  }
66
67  quote::quote! {{
68     #(#base)*
69
70     router
71  }}
72}