125 lines
3.8 KiB
Rust
125 lines
3.8 KiB
Rust
extern crate proc_macro2;
|
|
extern crate quote;
|
|
extern crate syn;
|
|
|
|
extern crate proc_macro;
|
|
|
|
use parse::{Parse, ParseStream};
|
|
use proc_macro::{Span, TokenStream};
|
|
use punctuated::Punctuated;
|
|
// 用于处理宏输入
|
|
use quote::quote;
|
|
use spanned::Spanned;
|
|
// 用于生成代码
|
|
use syn::*;
|
|
use syn::{parse_macro_input, ItemFn};
|
|
|
|
struct RouteArgs {
|
|
path: String,
|
|
methods: Vec<String>,
|
|
}
|
|
|
|
// 实现 Parse trait 以支持解析参数
|
|
impl Parse for RouteArgs {
|
|
fn parse(input: ParseStream) -> Result<Self> {
|
|
let args = Punctuated::<Expr, Token![,]>::parse_terminated(input)?;
|
|
let mut path = None;
|
|
let mut methods = Vec::new();
|
|
|
|
for expr in args {
|
|
match expr {
|
|
Expr::Lit(ExprLit {
|
|
lit: Lit::Str(lit_str),
|
|
..
|
|
}) => {
|
|
let path_str = lit_str.value();
|
|
// 验证路由路径
|
|
validate_route_path(&path_str)?;
|
|
path = Some(path_str);
|
|
}
|
|
Expr::Assign(assign) => {
|
|
if let Expr::Path(path) = *assign.left {
|
|
if path.path.is_ident("method") {
|
|
if let Expr::Array(ExprArray { elems, .. }) = *assign.right {
|
|
for elem in elems {
|
|
if let Expr::Lit(ExprLit {
|
|
lit: Lit::Str(lit_str),
|
|
..
|
|
}) = elem
|
|
{
|
|
methods.push(lit_str.value());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
|
|
let path = path.ok_or_else(|| Error::new(
|
|
Span::call_site().into(),
|
|
"路由路径参数不能为空",
|
|
))?;
|
|
|
|
Ok(RouteArgs { path, methods })
|
|
}
|
|
}
|
|
|
|
// 添加路由参数验证
|
|
fn validate_route_path(path: &str) -> Result<()> {
|
|
if !path.starts_with('/') {
|
|
return Err(Error::new(
|
|
Span::call_site().into(),
|
|
"路由路径必须以'/'开头",
|
|
));
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
pub fn gen_route(attr: TokenStream, item: TokenStream, method: &str) -> TokenStream {
|
|
let func = parse_macro_input!(item as ItemFn);
|
|
// 解析宏的参数
|
|
let RouteArgs { path, methods } = parse_macro_input!(attr as RouteArgs);
|
|
|
|
// 将 method 转换为小写并生成标识符
|
|
// let vis = func.vis.clone();
|
|
let ident = func.sig.ident.clone();
|
|
|
|
let mut method_routers = Vec::new();
|
|
if methods.is_empty() {
|
|
let method_name = Ident::new(&method.to_lowercase(), method.span());
|
|
method_routers.push(quote! {
|
|
(#path, axum::routing::#method_name(#ident::#ident))
|
|
});
|
|
} else {
|
|
for method in methods {
|
|
let method_name = Ident::new(&method.to_lowercase(), method.span());
|
|
method_routers.push(quote! {
|
|
(#path, axum::routing::#method_name(#ident::#ident))
|
|
});
|
|
}
|
|
}
|
|
|
|
let generated = quote! {
|
|
#[allow(non_camel_case_types)]
|
|
struct #ident;
|
|
impl #ident {
|
|
#func
|
|
}
|
|
impl library::typed_router::RouteMethod for #ident {
|
|
fn ge_router(&self, mut router: axum::Router) -> axum::Router {
|
|
let methods = vec![#(#method_routers),*];
|
|
for (path, method_router) in methods {
|
|
router = router.route(path, method_router);
|
|
}
|
|
router
|
|
}
|
|
}
|
|
::library::submit_router_method!(#ident);
|
|
};
|
|
|
|
TokenStream::from(generated)
|
|
}
|