1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
//! `paths![]` collection macro implementation.
//!
//! Expands `paths![handler_a, handler_b]` into a `pub mod paths { … }` module
//! that re-exports each handler's `__autumn_path_*` companion function under
//! its short name, so callers can write `paths::handler_a(arg)` instead of
//! the internal `__autumn_path_handler_a(arg)`.
//!
//! Module-qualified paths are supported: `paths![posts::show, users::index]`
//! re-exports them as `paths::show` and `paths::index` respectively.
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use syn::{Path, Token, punctuated::Punctuated};
/// Expand `paths![handler_a, module::handler_b]` into a `pub mod paths { … }`.
pub fn paths_macro(input: TokenStream) -> TokenStream {
if input.is_empty() {
return quote! {
pub mod paths {}
};
}
let paths: Punctuated<Path, Token![,]> =
match syn::parse::Parser::parse2(Punctuated::parse_terminated, input) {
Ok(p) => p,
Err(err) => return err.to_compile_error(),
};
let uses: Vec<_> = paths
.iter()
.map(|path| {
// The short alias is the last segment's ident (e.g. `show` from `posts::show`).
let alias = match path.segments.last() {
Some(seg) => seg.ident.clone(),
None => return quote! {},
};
// Build the companion path: prefix the last segment with `__autumn_path_`.
let mut companion = path.clone();
if let Some(last) = companion.segments.last_mut() {
last.ident = format_ident!("__autumn_path_{}", last.ident);
}
// Emit a `pub use` that reaches the companion. Paths with a
// leading `::` or starting with `crate` are truly absolute and
// used verbatim. Simple / module-qualified relative paths (including
// those starting with `super` or `self`) must be reached via an
// additional `super::` from inside the generated `pub mod paths`.
let is_absolute = companion.leading_colon.is_some()
|| companion
.segments
.first()
.is_some_and(|s| s.ident == "crate");
if is_absolute {
quote! { pub use #companion as #alias; }
} else {
quote! { pub use super::#companion as #alias; }
}
})
.collect();
quote! {
pub mod paths {
#(#uses)*
}
}
}