framework-proc-web 26.8.151

lingting framework
Documentation
mod arguments;
mod common;
mod options;
mod response;

use crate::arguments::expand_arguments;
use crate::options::{Method, WebApiOptions};
use crate::response::expand_response;
use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use quote::{format_ident, quote};
use syn::{Error, ItemFn, parse2};

#[proc_macro_attribute]
pub fn web_api(args: TokenStream, input: TokenStream) -> TokenStream {
    expand(args.into(), input.into(), None).into()
}

#[proc_macro_attribute]
pub fn web_api_get(args: TokenStream, input: TokenStream) -> TokenStream {
    expand(args.into(), input.into(), Some(Method::Get)).into()
}

#[proc_macro_attribute]
pub fn web_api_post(args: TokenStream, input: TokenStream) -> TokenStream {
    expand(args.into(), input.into(), Some(Method::Post)).into()
}

#[proc_macro_attribute]
pub fn web_api_put(args: TokenStream, input: TokenStream) -> TokenStream {
    expand(args.into(), input.into(), Some(Method::Put)).into()
}

#[proc_macro_attribute]
pub fn web_api_patch(args: TokenStream, input: TokenStream) -> TokenStream {
    expand(args.into(), input.into(), Some(Method::Patch)).into()
}

#[proc_macro_attribute]
pub fn web_api_delete(args: TokenStream, input: TokenStream) -> TokenStream {
    expand(args.into(), input.into(), Some(Method::Delete)).into()
}

fn expand(args: TokenStream2, input: TokenStream2, forced_method: Option<Method>) -> TokenStream2 {
    let options = match WebApiOptions::parse(args, forced_method) {
        Ok(options) => options,
        Err(error) => return error.to_compile_error(),
    };
    let function = match parse2::<ItemFn>(input) {
        Ok(function) => function,
        Err(error) => return error.to_compile_error(),
    };
    if function.sig.asyncness.is_none() {
        return Error::new_spanned(function.sig.fn_token, "web_api 仅支持 async fn")
            .to_compile_error();
    }

    let (conversions, call_arguments) = match expand_arguments(&function, options.method) {
        Ok(result) => result,
        Err(error) => return error.to_compile_error(),
    };
    let function_ident = &function.sig.ident;
    let route_ident = format_ident!("build_{}_route", function_ident);
    let method = options.method.tokens();
    let method_name = options.method.as_str();
    let path = options.path;
    let auth = options.auth.map_or_else(
        || quote!(::framework_web::AuthRule::login()),
        |value| quote!(#value),
    );
    let response = expand_response(&function.sig.output, function_ident, &call_arguments);
    let collect = if cfg!(feature = "collect") {
        quote! {
            ::framework_web::push_web_api!(#route_ident);
        }
    } else {
        TokenStream2::new()
    };

    quote! {
        #[::framework_proc_ts::ts_api(method = #method_name, path = #path)]
        #function

        pub(crate) fn #route_ident() -> ::framework_web::WebRoute {
            ::framework_web::WebRoute {
                method: #method,
                path: #path.to_string(),
                auth: #auth,
                invoke: ::std::sync::Arc::new(|| {
                    ::std::boxed::Box::pin(async move {
                        let context = match ::framework_web::use_web() {
                            Ok(context) => context,
                            Err(error) => return ::framework_web::WebResponse::from_error(error, None),
                        };
                        let panic_request = context
                            .request_arc();
                        let invoke_request = ::std::sync::Arc::clone(&panic_request);
                        match ::framework_web::catch_panic(async move {
                            #(#conversions)*
                            #response
                        })
                        .await
                        {
                            Ok(response) => response,
                            Err(error) => ::framework_web::WebResponse::from_error(
                                error,
                                Some(panic_request.as_ref()),
                            ),
                        }
                    })
                }),
            }
        }

        #collect
    }
}