diff options
Diffstat (limited to 'src/parse_attrs.rs')
-rw-r--r-- | src/parse_attrs.rs | 93 |
1 files changed, 93 insertions, 0 deletions
diff --git a/src/parse_attrs.rs b/src/parse_attrs.rs new file mode 100644 index 0000000..03b5377 --- /dev/null +++ b/src/parse_attrs.rs @@ -0,0 +1,93 @@ +use std::collections::HashMap; + +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<Self> { + 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<TokenStream>, + pub dyn_trait_attrs: Vec<TokenStream>, + pub collections: HashMap<Ident, usize>, +} + +impl TraitAttrs { + pub fn parse(attrs: &mut Vec<Attribute>) -> Result<Self, Error> { + 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 { + i += 1; + } + } + Ok(parsed) + } +} |