use super::{common::*, *};
pub fn anon(tokens: proc_macro::TokenStream) -> proc_macro::TokenStream {
let anon = parse_macro_input!(tokens as Anon);
anon.expand().into()
}
pub struct Anon {
pub attrs: Vec<syn::Attribute>,
pub items: Punctuated<SpreadItem, Token![,]>,
}
impl Anon {
pub fn expand(self) -> TokenStream {
let Self { mut attrs, items } = self;
for attr in &mut attrs {
attr.style = syn::AttrStyle::Outer;
}
let let_sources = items.iter().filter_map(|item| match item {
SpreadItem::SpreadList(SpreadList {
source,
source_ident,
..
}) => Some(quote! { let #source_ident = #source; }),
_ => None,
});
let fields_expansions = items.iter().map(SpreadItem::field_expansion);
let mut fields_name = vec![];
for item in items.iter() {
match item {
SpreadItem::Field(Field { name, .. }) => {
fields_name.push(name.clone());
}
SpreadItem::SpreadList(SpreadList { fields_list, .. }) => {
for Field { name, .. } in fields_list.iter() {
fields_name.push(name.clone());
}
}
SpreadItem::FinalSpread(_, _) => {
unreachable!("FinalSpread is not allowed in anon!")
}
}
}
let fields_type: Vec<_> = fields_name
.iter()
.enumerate()
.map(|(i, _)| syn::Ident::new(&format!("T{i}"), Span::call_site()))
.collect();
quote! {
{
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#(#attrs)*
struct Anon < #( #fields_type ),* > {
#(
#fields_name: #fields_type
),*
}
#( #let_sources )*
Anon {
#( #fields_expansions ),*
}
}
}
}
}
impl Parse for Anon {
fn parse(input: ParseStream) -> syn::Result<Self> {
let attrs = input.call(syn::Attribute::parse_inner)?;
let items = Punctuated::<SpreadItem, Token![,]>::parse_terminated(input)?;
if items.is_empty() {
return Err(syn::Error::new(
Span::call_site(),
"Anon struct must have at least one field",
));
}
for item in items.iter() {
if let SpreadItem::FinalSpread(dotdot, _) = item {
return Err(syn::Error::new(
dotdot.span(),
"`..remaining` is not allowed in this macro",
));
}
}
for item in items.iter() {
match item {
SpreadItem::Field(Field {
is_mut: Some(token_mut),
..
}) => {
return Err(syn::Error::new(
token_mut.span(),
"`mut` prefix is not allowed in this macro",
))
}
SpreadItem::SpreadList(list) => {
for field in list.fields_list.iter() {
if let Some(token_mut) = field.is_mut {
return Err(syn::Error::new(
token_mut.span(),
"`mut` prefix is not allowed in this macro",
));
}
}
}
_ => (),
}
}
Ok(Self { attrs, items })
}
}