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
|
# 0.2.3
|
||||||
- Refactored the detection of which methods exist,
|
- Refactored the detection of which methods exist,
|
||||||
we actually parse the file now instead of just checking that it contains `pub async #method_name`
|
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
|
// 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<()> {
|
pub async fn server() -> anyhow::Result<()> {
|
||||||
// Create app state
|
// Create app state
|
||||||
|
@ -16,7 +17,7 @@ pub async fn server() -> anyhow::Result<()> {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Use the init fn generated above
|
// 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
|
// Build the router and provide the state
|
||||||
let app: Router<()> = folder_router.with_state(app_state);
|
let app: Router<()> = folder_router.with_state(app_state);
|
||||||
|
|
|
@ -7,7 +7,8 @@ struct AppState {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Imports route.rs files & generates an init fn
|
// Imports route.rs files & generates an init fn
|
||||||
folder_router!("./examples/simple/api", AppState);
|
#[folder_router("./examples/simple/api", AppState)]
|
||||||
|
struct MyFolderRouter();
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> anyhow::Result<()> {
|
async fn main() -> anyhow::Result<()> {
|
||||||
|
@ -17,7 +18,7 @@ async fn main() -> anyhow::Result<()> {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Use the init fn generated above
|
// 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
|
// Build the router and provide the state
|
||||||
let app: Router<()> = folder_router.with_state(app_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
|
/// Creates an Axum router module tree & creation function
|
||||||
/// by scanning a directory for `route.rs` files.
|
/// 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
|
/// This will scan all `route.rs` files in the `./src/api` directory and its
|
||||||
/// subdirectories, automatically mapping their path structure to URL routes
|
/// subdirectories, automatically mapping their path structure to URL routes
|
||||||
/// with the specified state type.
|
/// with the specified state type.
|
||||||
#[proc_macro]
|
#[proc_macro_attribute]
|
||||||
pub fn folder_router(input: TokenStream) -> TokenStream {
|
pub fn folder_router(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||||
let args = parse_macro_input!(input as FolderRouterArgs);
|
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 base_path = args.path;
|
||||||
let state_type = args.state_type;
|
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
|
// 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 {
|
for (route_path, rel_path) in &routes {
|
||||||
add_to_module_tree(&mut root, rel_path, route_path);
|
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
|
// Generate the final code
|
||||||
let expanded = quote! {
|
let expanded = quote! {
|
||||||
#[path = #base_path_lit]
|
#[path = #base_path_lit]
|
||||||
mod #root_mod_ident {
|
mod #root_mod_ident {
|
||||||
#mod_hierarchy
|
#mod_hierarchy
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn folder_router() -> axum::Router<#state_type> {
|
#input_item
|
||||||
let mut router = axum::Router::new();
|
|
||||||
#(#route_registrations)*
|
impl #struct_name {
|
||||||
router
|
pub fn into_router() -> axum::Router<#state_type> {
|
||||||
}
|
let mut router = axum::Router::new();
|
||||||
|
#(#route_registrations)*
|
||||||
|
router
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
expanded.into()
|
expanded.into()
|
||||||
|
@ -288,11 +301,11 @@ pub fn folder_router(input: TokenStream) -> TokenStream {
|
||||||
/// e.g. for the file
|
/// e.g. for the file
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// pub async fn get() {} # ✅ => "get" be added to vec
|
/// pub async fn get() {} // ✅ => "get" be added to vec
|
||||||
/// pub fn post() {} # not async
|
/// pub fn post() {} // not async
|
||||||
/// async fn delete() {} # not pub
|
/// async fn delete() {} // not pub
|
||||||
/// fn patch() {} # not pub nor async
|
/// fn patch() {} // not pub nor async
|
||||||
/// pub fn non_verb() {} # not a http verb
|
/// pub fn non_verb() {} // not a http verb
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// it returns: `vec!["get"]`
|
/// it returns: `vec!["get"]`
|
||||||
|
|
Loading…
Add table
Reference in a new issue