use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, ItemStruct};
pub fn claims_impl(_attr: TokenStream, item: TokenStream) -> TokenStream {
let mut input = parse_macro_input!(item as ItemStruct);
let struct_name = input.ident.clone();
match &mut input.fields {
syn::Fields::Named(_) => { }
syn::Fields::Unit => {
input.fields = syn::Fields::Named(syn::FieldsNamed {
brace_token: syn::token::Brace::default(),
named: syn::punctuated::Punctuated::new(),
});
}
syn::Fields::Unnamed(_) => {
return syn::Error::new_spanned(
&input,
"#[claims] requires a struct with named fields; \
tuple structs are not supported",
)
.to_compile_error()
.into();
}
}
let fields_named = match &mut input.fields {
syn::Fields::Named(named) => named,
_ => unreachable!("normalized to named fields above"),
};
let derives_deserialize = input.attrs.iter().any(|attr| {
if !attr.path().is_ident("derive") {
return false;
}
let nested: syn::Result<syn::MetaList> = attr.meta.require_list().cloned();
match nested {
Ok(list) => list
.parse_args_with(syn::punctuated::Punctuated::<syn::Path, syn::Token![,]>::parse_terminated)
.map(|paths| {
paths
.iter()
.any(|p| p.is_ident("Deserialize") || p.is_ident("serde::Deserialize"))
})
.unwrap_or(false),
Err(_) => false,
}
});
let injected_field: syn::Field = if derives_deserialize {
syn::parse_quote! {
#[serde(skip)]
pub claims: ::std::option::Option<::std::boxed::Box<dyn ::rust_webx::IClaims>>
}
} else {
syn::parse_quote! {
pub claims: ::std::option::Option<::std::boxed::Box<dyn ::rust_webx::IClaims>>
}
};
fields_named.named.push(injected_field);
let expanded = quote! {
#input
impl #struct_name {
pub fn set_claims(&mut self, claims: ::std::option::Option<::std::boxed::Box<dyn ::rust_webx::IClaims>>) {
self.claims = claims;
}
}
};
expanded.into()
}