nutt_web_macro/
lib.rs

1mod include_addr;
2
3use core::panic;
4use proc_macro::TokenStream;
5use proc_macro2::Ident;
6use std::fmt::Debug;
7use syn::__private::quote::quote;
8use syn::__private::ToTokens;
9use syn::{FnArg, ItemFn, Lit, Pat, PatType, Type, TypePath, TypeReference};
10
11#[derive(Debug)]
12enum ArgType {
13    TypePath(TypePath),
14    TypeRef(TypeReference),
15}
16
17impl ToTokens for ArgType {
18    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
19        match self {
20            ArgType::TypePath(ty) => ty.to_tokens(tokens),
21            ArgType::TypeRef(ty) => ty.to_tokens(tokens),
22        }
23    }
24}
25
26type FnItems = (
27    ItemFn,
28    Ident,
29    String,
30    Vec<Ident>,
31    Vec<ArgType>,
32    Vec<Ident>,
33    Vec<ArgType>,
34    Vec<Ident>,
35    Vec<ArgType>,
36    Vec<Ident>,
37    Vec<ArgType>,
38    Vec<Ident>,
39    Vec<ArgType>,
40);
41
42fn get_fn_and_args_from_stream(attr: TokenStream, item: TokenStream) -> FnItems {
43    let item = syn::parse::<ItemFn>(item.clone()).unwrap();
44    let attr = syn::parse::<Lit>(attr).unwrap();
45    let mut path = String::new();
46    if let Lit::Str(lit) = attr {
47        path = lit.value()
48    } else {
49        panic!("Path should be string")
50    }
51    let ident = item.clone().sig.ident;
52    let args = item.clone().sig.inputs.into_iter().collect::<Vec<FnArg>>();
53    let mut args_ident = vec![];
54    let mut args_ty = vec![];
55    let mut args_ident_state = vec![];
56    let mut args_ident_json = vec![];
57    let mut args_ty_json = vec![];
58    let mut args_ty_state = vec![];
59    let mut args_ident_session = vec![];
60    let mut args_ty_session = vec![];
61    let mut args_ident_cookie = vec![];
62    let mut args_ty_cookie = vec![];
63
64    for arg in &args {
65        if let FnArg::Typed(PatType { pat, ty, .. }) = arg {
66            if let Pat::Ident(ident) = *pat.clone() {
67                args_ident.push(ident.ident.clone());
68            }
69            if let Type::Path(ty) = *ty.clone() {
70                let seg = ty.path.segments.iter().next().unwrap().clone().ident;
71                if &seg.to_string() == "State" {
72                    args_ty_state.push(ArgType::TypePath(ty.clone()));
73                    if let Pat::Ident(ident) = *pat.clone() {
74                        args_ident_state.push(ident.ident.clone());
75                    }
76                } else if &seg.to_string() == "CookieSession" {
77                    args_ty_session.push(ArgType::TypePath(ty.clone()));
78                    if let Pat::Ident(ident) = *pat.clone() {
79                        args_ident_session.push(ident.ident.clone());
80                    }
81                } else if &seg.to_string() == "CookieJar" {
82                    args_ty_cookie.push(ArgType::TypePath(ty.clone()));
83                    if let Pat::Ident(ident) = *pat.clone() {
84                        args_ident_cookie.push(ident.ident.clone());
85                    }
86                } else {
87                    args_ty_json.push(ArgType::TypePath(ty.clone()));
88                    if let Pat::Ident(ident) = *pat.clone() {
89                        args_ident_json.push(ident.ident.clone());
90                    }
91                }
92                args_ty.push(ArgType::TypePath(ty))
93            }
94        }
95    }
96    (
97        item,
98        ident,
99        path,
100        args_ident,
101        args_ty,
102        args_ident_json,
103        args_ty_json,
104        args_ident_state,
105        args_ty_state,
106        args_ident_session,
107        args_ty_session,
108        args_ident_cookie,
109        args_ty_cookie,
110    )
111}
112
113fn get_stream(method: &str, fn_items: FnItems) -> TokenStream {
114    let (
115        item,
116        ident,
117        path,
118        args_ident,
119        _args_ty,
120        args_ident_json,
121        args_ty_json,
122        args_ident_state,
123        args_ty_state,
124        args_ident_session,
125        args_ty_session,
126        args_ident_cookie,
127        args_ty_cookie,
128    ) = fn_items;
129
130    let vis = item.vis.clone();
131
132    let method = match method {
133        "get" => quote! { Method::GET },
134        "post" => quote! {Method::POST},
135        "put" => quote! {Method::PUT},
136        "delete" => quote! {Method::DELETE},
137        _ => panic!("Unhanding method"),
138    };
139    let stream = quote! {
140
141        #vis fn #ident() -> Route {
142            use std::future::Future;
143            use std::pin::Pin;
144            use nutt_web::http::method::Method;
145            use nutt_web::http::request::Request;
146            use nutt_web::modules::session::Session;
147            use nutt_web::http::cookie::CookieJar;
148            let f = |req: Request| -> Pin<Box<dyn Future<Output = Response> + Send + Sync>> {
149                #item
150                #(
151                    let #args_ident_json: #args_ty_json = if let Ok(value) = req.body_json() {
152                        value
153                    } else { panic!("Args parsing error") };
154                )*
155                #(
156                    let #args_ident_state: #args_ty_state = if let Some(value) = req.get_state().get(stringify!(#args_ident_state)) {
157                        if let Some(value) = value.downcast_ref::<#args_ty_state>() {
158                            value.clone()
159                        } else {panic!("Downcast state type error")}
160                    } else { panic!("Args parsing error") };
161                )*
162                #(
163                    let #args_ident_session: #args_ty_session = {
164                        if req.get_session().is_some() {
165                            match req.get_session().as_ref() {
166                                Some(Session::Cookie(session)) => {session.clone()}
167                                None => {panic!("")}
168                            }
169                        } else { panic!("Args parsing error") }
170                    };
171                )*
172                #(
173                    let #args_ident_cookie: #args_ty_cookie = {
174                        req.get_cookie_jar()
175                    };
176                )*
177                Box::pin(#ident(#(#args_ident.clone(),)*))
178            } as fn(Request) -> _;
179
180            return Route::new(#method, #path, f)
181        }
182    };
183
184    stream.into()
185}
186
187#[proc_macro_attribute]
188pub fn get(attr: TokenStream, item: TokenStream) -> TokenStream {
189    let fn_items = get_fn_and_args_from_stream(attr, item);
190    get_stream("get", fn_items)
191}
192
193#[proc_macro_attribute]
194pub fn post(attr: TokenStream, item: TokenStream) -> TokenStream {
195    let fn_items = get_fn_and_args_from_stream(attr, item);
196    get_stream("post", fn_items)
197}
198
199#[proc_macro_attribute]
200pub fn put(attr: TokenStream, item: TokenStream) -> TokenStream {
201    let fn_items = get_fn_and_args_from_stream(attr, item);
202    get_stream("put", fn_items)
203}
204
205#[proc_macro_attribute]
206pub fn delete(attr: TokenStream, item: TokenStream) -> TokenStream {
207    let fn_items = get_fn_and_args_from_stream(attr, item);
208    get_stream("delete", fn_items)
209}
210
211#[proc_macro]
212pub fn include_addr(_input: TokenStream) -> TokenStream {
213    include_addr::include_addr(_input)
214}