diff options
author | Martin Fischer <martin@push-f.com> | 2021-11-22 15:57:17 +0100 |
---|---|---|
committer | Martin Fischer <martin@push-f.com> | 2021-11-22 15:57:17 +0100 |
commit | 346113bbebddbd199b61249957c7569514071e89 (patch) | |
tree | 82a1978a60faf93a00b5cc9ed4aa4f182b3d037f /src | |
parent | a0ec23e259359bbbd115d6159193a361c8ce24df (diff) |
support other collections via #[collection(...)]
Diffstat (limited to 'src')
-rw-r--r-- | src/lib.rs | 48 | ||||
-rw-r--r-- | src/transform.rs | 5 |
2 files changed, 52 insertions, 1 deletions
@@ -8,6 +8,8 @@ use quote::format_ident; use quote::quote; use quote::quote_spanned; use quote::ToTokens; +use syn::parenthesized; +use syn::parse::Parse; use syn::parse_macro_input; use syn::spanned::Spanned; use syn::token::Brace; @@ -16,16 +18,20 @@ use syn::token::Lt; use syn::token::Trait; use syn::AngleBracketedGenericArguments; use syn::Block; +use syn::Error; use syn::Expr; use syn::GenericArgument; use syn::GenericParam; +use syn::Ident; use syn::ImplItemMethod; use syn::ItemTrait; +use syn::LitInt; use syn::Path; use syn::PathArguments; use syn::PathSegment; use syn::Signature; use syn::Stmt; +use syn::Token; use syn::TraitBound; use syn::TraitItem; use syn::TraitItemMethod; @@ -53,13 +59,37 @@ mod syn_utils; mod transform; macro_rules! abort { - ($span:expr, $message:literal $(,$args:tt)*) => {{ + ($span:expr, $message:literal $(,$args:expr)*) => {{ let msg = format!($message $(,$args)*); let tokens = quote_spanned! {$span => compile_error!(#msg);}.into(); tokens }}; } +struct Collection { + id: Ident, + 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 }) + } +} + #[proc_macro_attribute] pub fn dynamize(_attr: TokenStream, input: TokenStream) -> TokenStream { let mut original_trait = parse_macro_input!(input as ItemTrait); @@ -100,6 +130,22 @@ pub fn dynamize(_attr: TokenStream, input: TokenStream) -> TokenStream { }; let tokens = group.stream(); dyn_trait_attrs.push(quote! {#[#tokens]}); + } else if original_trait.attrs[i].path.is_ident("collection") { + let attr = original_trait.attrs.remove(i); + let tokens = attr.tokens.into(); + let coll = parse_macro_input!(tokens as Collection); + + if type_converter + .collections + .insert(coll.id.clone(), coll.count) + .is_some() + { + return abort!( + coll.id.span(), + "collection `{}` is defined multiple times for this trait", + coll.id + ); + } } else { i += 1; } diff --git a/src/transform.rs b/src/transform.rs index 05866b6..11a98c2 100644 --- a/src/transform.rs +++ b/src/transform.rs @@ -17,6 +17,7 @@ use crate::{ #[derive(Default)] pub struct TypeConverter<'a> { pub assoc_type_conversions: HashMap<Ident, DestType<'a>>, + pub collections: HashMap<Ident, usize>, } #[derive(Debug)] @@ -39,6 +40,10 @@ impl TypeConverter<'_> { /// ... etc. A return type of None means the type isn't recognized. #[rustfmt::skip] fn get_collection_type_count(&self, ident: &Ident) -> Option<usize> { + if let Some(count) = self.collections.get(ident) { + return Some(*count); + } + // when adding a type here don't forget to document it in the README if ident == "Vec" { return Some(1); } if ident == "VecDeque" { return Some(1); } |