use std::{ collections::{HashMap, HashSet}, rc::Rc, }; use proc_macro2::{Group, TokenStream}; use quote::quote; use syn::{ braced, parenthesized, parse::Parse, spanned::Spanned, Attribute, Error, Ident, LitInt, Token, Type, }; 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 dynamized_supertraits: HashSet, pub collections: HashMap, pub type_conversions: HashMap>, } 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("convert") { let attr = attrs.remove(i); let convert: Convert = syn::parse2(attr.tokens)?; let span = convert.original_type.span(); if parsed .type_conversions .insert(convert.original_type.clone(), Rc::new(convert)) .is_some() { return Err(Error::new( span, format_args!("conversion is defined multiple times for this type"), )); } } 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) } } #[derive(Debug, Clone)] pub struct Convert { pub ident: Ident, pub original_type: Type, pub dest_type: Type, pub block: TokenStream, } impl Parse for Convert { fn parse(input: syn::parse::ParseStream) -> syn::Result { let _: Token![=] = input.parse()?; let _: Token![|] = input.parse()?; let param: Ident = input.parse()?; let _: Token![:] = input.parse()?; let original_type: Type = input.parse()?; let _: Token![|] = input.parse()?; let _: Token![->] = input.parse()?; let dest_type: Type = input.parse()?; let inner; braced!(inner in input); Ok(Self { ident: param, original_type, dest_type, block: inner.parse()?, }) } }