use std::collections::{HashMap, HashSet}; use proc_macro2::{Group, TokenStream}; use quote::quote; use syn::{parenthesized, parse::Parse, Attribute, Error, Ident, LitInt, Token}; struct Collection { pub id: Ident, pub count: usize, } impl Parse for Collection { fn parse(input: syn::parse::ParseStream) -> syn::Result { let inner; parenthesized!(inner in input); let id: Ident = inner.parse()?; let _: Token![,] = inner.parse()?; let count_lit: LitInt = inner.parse()?; let count: usize = count_lit.base10_parse()?; if count < 1 { return Err(Error::new( count_lit.span(), "number of type parameters must be >= 1", )); } Ok(Self { id, count }) } } #[derive(Default)] pub struct TraitAttrs { pub blanket_impl_attrs: Vec, pub dyn_trait_attrs: Vec, pub collections: HashMap, pub dynamized_supertraits: HashSet, } struct Dynamized { ident: Ident, } impl Parse for Dynamized { fn parse(input: syn::parse::ParseStream) -> syn::Result { let inner; parenthesized!(inner in input); Ok(Self { ident: inner.parse()?, }) } } impl TraitAttrs { pub fn parse(attrs: &mut Vec) -> Result { let mut parsed = TraitAttrs::default(); // FUTURE: use Vec::drain_filter once it's stable let mut i = 0; while i < attrs.len() { if attrs[i].path.is_ident("blanket_impl_attr") { let attr = attrs.remove(i); let group: Group = match syn::parse2(attr.tokens) { Ok(g) => g, Err(err) => { return Err(Error::new( err.span(), "expected parenthesis: #[blanket_impl_attr(...)]", )) } }; let tokens = group.stream(); parsed.blanket_impl_attrs.push(quote! {#[#tokens]}); } else if attrs[i].path.is_ident("dyn_trait_attr") { let attr = attrs.remove(i); let group: Group = match syn::parse2(attr.tokens) { Ok(g) => g, Err(err) => { return Err(Error::new( err.span(), "expected parenthesis: #[dyn_trait_attr(...)]", )) } }; let tokens = group.stream(); parsed.dyn_trait_attrs.push(quote! {#[#tokens]}); } else if attrs[i].path.is_ident("collection") { let attr = attrs.remove(i); let coll: Collection = syn::parse2(attr.tokens)?; if parsed .collections .insert(coll.id.clone(), coll.count) .is_some() { return Err(Error::new( coll.id.span(), format_args!( "collection `{}` is defined multiple times for this trait", coll.id ), )); } } else if attrs[i].path.is_ident("dynamized") { let attr = attrs.remove(i); let dynamized: Dynamized = syn::parse2(attr.tokens)?; let span = dynamized.ident.span(); if !parsed.dynamized_supertraits.insert(dynamized.ident) { // FUTURE: relax to warning once proc_macro::Diagnostic is stable return Err(Error::new( span, format_args!("dynamized attribute is defined multiple times"), )); } } else { i += 1; } } Ok(parsed) } }