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 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
use crate::controller::handler::{HandlerAttrs, HandlerRepr}; use proc_macro2::{Ident, TokenStream}; use syn::{AttributeArgs, Error, ItemImpl, Lit, Meta, MetaNameValue, NestedMeta, Result}; use quote::quote; use syn::export::ToTokens; #[derive(Debug)] pub struct ControllerAttr { pub ident: Ident, pub name: String, pub version: Option<u16>, pub prefix: Option<String>, } impl ControllerAttr { pub fn new(args: AttributeArgs, input: &ItemImpl) -> Result<ControllerAttr> { let mut name = None; let mut version = None; let mut prefix = None; let ident = crate::utils::parse_item_impl_ident(input)?; for m in args.into_iter().filter_map(|a| if let NestedMeta::Meta(m) = a { Some(m) } else { None }) { match m { Meta::Path(p) => { return Err(Error::new_spanned(p, "Unexpected Attribute on controller impl")); } Meta::List(l) => { return Err(Error::new_spanned(l, "Unexpected Attribute on controller impl")); } Meta::NameValue(MetaNameValue { path, lit, .. }) => match (path.segments.first().map(|p| p.ident.to_string()).as_deref(), lit) { (Some("name"), Lit::Str(bp)) => { name = Some(bp.value().trim_matches('/').to_string()); } (Some("version"), Lit::Str(v)) => { version = Some( v.value() .parse::<u16>() .map_err(|_| Error::new_spanned(v, "Invalid version, expected number between 1 & u16::MAX"))?, ); } (Some("version"), Lit::Int(v)) => { version = Some( v.base10_parse::<u16>() .map_err(|_| Error::new_spanned(v, "Invalid version, expected number between 1 & u16::MAX"))?, ); } (Some("prefix"), Lit::Str(p)) => { prefix = Some(p.value().trim_matches('/').to_string()); } _ => { return Err(Error::new_spanned(path, "Unexpected Param in controller macro")); } }, } } let name = name.unwrap_or_else(|| ident.to_string().to_lowercase().trim_end_matches("controller").to_string()); Ok(ControllerAttr { ident, name, version, prefix }) } } pub fn gen_controller_trait_implementation(attrs: &ControllerAttr, handlers: &[HandlerRepr]) -> TokenStream { let controller_base_path = gen_controller_base_path_const(attrs); let controller_handlers_fn = gen_controller_handlers_fn(attrs, handlers); let ident = &attrs.ident; let e = quote! { impl Controller for #ident { #controller_base_path #controller_handlers_fn } }; e } fn gen_controller_base_path_const(attr: &ControllerAttr) -> TokenStream { let mut path = "/".to_string(); if let Some(prefix) = attr.prefix.as_ref() { path.push_str(prefix); path.push('/'); } if let Some(version) = attr.version { path.push('v'); path.push_str(&format!("{}", version)); path.push('/'); } path.push_str(attr.name.as_str()); let e = quote! { const BASE_PATH: &'static str = #path; }; e } fn gen_controller_handlers_fn(attr: &ControllerAttr, handlers: &[HandlerRepr]) -> TokenStream { let mut handler_stream = TokenStream::new(); let ctrl_ident = attr.ident.clone(); for handler in handlers { let HandlerAttrs { methods_paths, guards, .. } = &handler.attrs; let handler_ident = handler.original_method.sig.ident.clone(); for (method, path) in methods_paths { let method = method.as_str(); let handler_name = handler_ident.to_string(); if guards.is_empty() { (quote! { .add_with_name(#handler_name, Method::from_str(#method).expect("Method was validated by the macro expansion"), #path, #ctrl_ident::#handler_ident) }) .to_tokens(&mut handler_stream); } else { let mut guard_stream = TokenStream::new(); for guard_def in guards { (quote! { .apply(#guard_def) }) .to_tokens(&mut guard_stream); } (quote! { .add_with_guards_and_name(#handler_name, Method::from_str(#method).expect("Method was validated the macro expansion"), #path, #ctrl_ident::#handler_ident, |g| { g #guard_stream }) }) .to_tokens(&mut handler_stream); } } } let quoted_h = quote! { fn handlers(&self) -> Vec<ControllerEndpoint<Self>> where Self: Sized { EndpointsBuilder::new() #handler_stream .build() } }; quoted_h }