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 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
mod error;
mod route;
mod service;
mod state;
use proc_macro::TokenStream;
use syn::{spanned::Spanned, Error, ImplItem, ImplItemFn};
#[proc_macro_derive(State, attributes(borrow))]
pub fn state_impl(item: TokenStream) -> TokenStream {
let item = syn::parse_macro_input!(item);
state::state(item).unwrap_or_else(|e| e.to_compile_error().into())
}
/// attribute macro for `xitca-web` application.
///
/// # Pattern
/// ```plain
/// #[route("path", method = <method>[, attributes])]
/// ```
///
/// # Attributes
/// - `"path"`: string literal represent path register to http router.
/// `"/foo"` for example.
/// - `method = <method>`: function path of http method register to http router.
/// `method = get` for example.
/// - `enclosed = <type>`: typed middleware applied to route.
/// - `enclosed_fn = <async function>`: async function as middleware applied to route
///
/// # Example
/// ```rust(no_run)
/// # use xitca_web::{codegen::route, handler::handler_service, service::Service, App, WebContext};
/// #[route("/", method = get, enclosed_fn = middleware_fn)]
/// async fn index() -> &'static str {
/// ""
/// }
///
/// async fn middleware_fn<S, C, B, Res, Err>(service: &S, ctx: WebContext<'_, C, B>) -> Result<Res, Err>
/// where
/// S: for<'r> Service<WebContext<'r, C, B>, Response = Res, Error = Err>
/// {
/// service.call(ctx).await
/// }
///
/// App::new()
/// // add generated index typed route to application.
/// .at_typed(index)
/// # .at("/nah", handler_service(nah));
///
/// # async fn nah(_: &WebContext<'_>) -> &'static str {
/// # // needed to infer the body type of request
/// # ""
/// # }
/// ```
#[proc_macro_attribute]
pub fn route(attr: TokenStream, item: TokenStream) -> TokenStream {
let attr = syn::parse_macro_input!(attr);
let item = syn::parse_macro_input!(item);
route::route(attr, item).unwrap_or_else(|e| e.to_compile_error().into())
}
#[proc_macro_attribute]
pub fn error_impl(attr: TokenStream, item: TokenStream) -> TokenStream {
let item = syn::parse_macro_input!(item);
error::error(attr, item).unwrap_or_else(|e| e.to_compile_error().into())
}
#[proc_macro_attribute]
pub fn middleware_impl(attr: TokenStream, item: TokenStream) -> TokenStream {
let item = syn::parse_macro_input!(item);
service::middleware(attr, item).unwrap_or_else(|e| e.to_compile_error().into())
}
#[proc_macro_attribute]
pub fn service_impl(attr: TokenStream, item: TokenStream) -> TokenStream {
let item = syn::parse_macro_input!(item);
service::service(attr, item).unwrap_or_else(|e| e.to_compile_error().into())
}
fn find_async_method<'a>(items: &'a [ImplItem], ident_str: &str) -> Option<Result<&'a ImplItemFn, Error>> {
for item in items.iter() {
if let ImplItem::Fn(func) = item {
if func.sig.ident.to_string().as_str() == ident_str {
if func.sig.asyncness.is_none() {
return Some(Err(Error::new(
func.span(),
format!("{ident_str} method must be async fn"),
)));
}
return Some(Ok(func));
}
}
}
None
}