Rework API to attribute macro
This commit is contained in:
parent
a8864786aa
commit
d05df1bb4d
4 changed files with 63 additions and 23 deletions
25
CHANGELOG.md
25
CHANGELOG.md
|
@ -1,3 +1,28 @@
|
|||
# Unreleased
|
||||
|
||||
## Breaking
|
||||
### Rework into attribute macro.
|
||||
|
||||
Instead of using it like this
|
||||
|
||||
```rust
|
||||
// ...
|
||||
folder_router!("./examples/simple/api", AppState);
|
||||
// ...
|
||||
let folder_router: Router<AppState> = folder_router();
|
||||
```
|
||||
|
||||
It now works like this:
|
||||
```rust
|
||||
// ...
|
||||
#[folder_router("./examples/simple/api", AppState)]
|
||||
struct MyFolderRouter
|
||||
// ...
|
||||
let folder_router: Router<AppState> = MyFolderRouter::into_router();
|
||||
```
|
||||
|
||||
This is a bit cleaner & it allows you to have multiple separate folder-based Routers.
|
||||
|
||||
# 0.2.3
|
||||
- Refactored the detection of which methods exist,
|
||||
we actually parse the file now instead of just checking that it contains `pub async #method_name`
|
||||
|
|
|
@ -7,7 +7,8 @@ struct AppState {
|
|||
}
|
||||
|
||||
// Imports route.rs files & generates an init fn
|
||||
folder_router!("examples/advanced/api", AppState);
|
||||
#[folder_router("examples/advanced/api", AppState)]
|
||||
struct MyFolderRouter();
|
||||
|
||||
pub async fn server() -> anyhow::Result<()> {
|
||||
// Create app state
|
||||
|
@ -16,7 +17,7 @@ pub async fn server() -> anyhow::Result<()> {
|
|||
};
|
||||
|
||||
// Use the init fn generated above
|
||||
let folder_router: Router<AppState> = folder_router();
|
||||
let folder_router: Router<AppState> = MyFolderRouter::into_router();
|
||||
|
||||
// Build the router and provide the state
|
||||
let app: Router<()> = folder_router.with_state(app_state);
|
||||
|
|
|
@ -7,7 +7,8 @@ struct AppState {
|
|||
}
|
||||
|
||||
// Imports route.rs files & generates an init fn
|
||||
folder_router!("./examples/simple/api", AppState);
|
||||
#[folder_router("./examples/simple/api", AppState)]
|
||||
struct MyFolderRouter();
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
|
@ -17,7 +18,7 @@ async fn main() -> anyhow::Result<()> {
|
|||
};
|
||||
|
||||
// Use the init fn generated above
|
||||
let folder_router: Router<AppState> = folder_router();
|
||||
let folder_router: Router<AppState> = MyFolderRouter::into_router();
|
||||
|
||||
// Build the router and provide the state
|
||||
let app: Router<()> = folder_router.with_state(app_state);
|
||||
|
|
51
src/lib.rs
51
src/lib.rs
|
@ -177,7 +177,6 @@ impl ModuleDir {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates an Axum router module tree & creation function
|
||||
/// by scanning a directory for `route.rs` files.
|
||||
///
|
||||
|
@ -191,9 +190,12 @@ impl ModuleDir {
|
|||
/// This will scan all `route.rs` files in the `./src/api` directory and its
|
||||
/// subdirectories, automatically mapping their path structure to URL routes
|
||||
/// with the specified state type.
|
||||
#[proc_macro]
|
||||
pub fn folder_router(input: TokenStream) -> TokenStream {
|
||||
let args = parse_macro_input!(input as FolderRouterArgs);
|
||||
#[proc_macro_attribute]
|
||||
pub fn folder_router(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let args = parse_macro_input!(attr as FolderRouterArgs);
|
||||
let input_item = parse_macro_input!(item as syn::ItemStruct);
|
||||
let struct_name = &input_item.ident;
|
||||
|
||||
let base_path = args.path;
|
||||
let state_type = args.state_type;
|
||||
|
||||
|
@ -214,8 +216,15 @@ pub fn folder_router(input: TokenStream) -> TokenStream {
|
|||
});
|
||||
}
|
||||
|
||||
fn replace_special_chars(input: &str) -> String {
|
||||
input
|
||||
.chars()
|
||||
.map(|c| if c.is_alphanumeric() { c } else { '_' })
|
||||
.collect()
|
||||
}
|
||||
|
||||
// Build module tree
|
||||
let mut root = ModuleDir::new("__folder_router");
|
||||
let mut root = ModuleDir::new(&format!("__folder_router_{}", replace_special_chars(&base_path)));
|
||||
for (route_path, rel_path) in &routes {
|
||||
add_to_module_tree(&mut root, rel_path, route_path);
|
||||
}
|
||||
|
@ -268,16 +277,20 @@ pub fn folder_router(input: TokenStream) -> TokenStream {
|
|||
|
||||
// Generate the final code
|
||||
let expanded = quote! {
|
||||
#[path = #base_path_lit]
|
||||
mod #root_mod_ident {
|
||||
#mod_hierarchy
|
||||
}
|
||||
#[path = #base_path_lit]
|
||||
mod #root_mod_ident {
|
||||
#mod_hierarchy
|
||||
}
|
||||
|
||||
pub fn folder_router() -> axum::Router<#state_type> {
|
||||
let mut router = axum::Router::new();
|
||||
#(#route_registrations)*
|
||||
router
|
||||
}
|
||||
#input_item
|
||||
|
||||
impl #struct_name {
|
||||
pub fn into_router() -> axum::Router<#state_type> {
|
||||
let mut router = axum::Router::new();
|
||||
#(#route_registrations)*
|
||||
router
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
expanded.into()
|
||||
|
@ -288,11 +301,11 @@ pub fn folder_router(input: TokenStream) -> TokenStream {
|
|||
/// e.g. for the file
|
||||
///
|
||||
/// ```rust
|
||||
/// pub async fn get() {} # ✅ => "get" be added to vec
|
||||
/// pub fn post() {} # not async
|
||||
/// async fn delete() {} # not pub
|
||||
/// fn patch() {} # not pub nor async
|
||||
/// pub fn non_verb() {} # not a http verb
|
||||
/// pub async fn get() {} // ✅ => "get" be added to vec
|
||||
/// pub fn post() {} // not async
|
||||
/// async fn delete() {} // not pub
|
||||
/// fn patch() {} // not pub nor async
|
||||
/// pub fn non_verb() {} // not a http verb
|
||||
/// ```
|
||||
///
|
||||
/// it returns: `vec!["get"]`
|
||||
|
|
Loading…
Add table
Reference in a new issue