diff options
| author | Laurenz <laurmaedje@gmail.com> | 2022-02-08 16:39:37 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2022-02-09 12:34:19 +0100 |
| commit | e089b6ea40015e012302dc55ac5d6cb42ca4876e (patch) | |
| tree | dbb66237cb996bc880560dfd94ac9b682e1ac985 /macros/src | |
| parent | 68503b9a07b00bce3f4d377bcfe945452de815ea (diff) | |
Set rules for everything
Diffstat (limited to 'macros/src')
| -rw-r--r-- | macros/src/lib.rs | 92 |
1 files changed, 71 insertions, 21 deletions
diff --git a/macros/src/lib.rs b/macros/src/lib.rs index efaf8be8..0757a201 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -5,7 +5,7 @@ use proc_macro2::TokenStream as TokenStream2; use quote::quote; use syn::parse_quote; use syn::spanned::Spanned; -use syn::{Error, Result}; +use syn::{Error, Ident, Result}; /// Turn a node into a class. #[proc_macro_attribute] @@ -24,15 +24,17 @@ fn expand(mut impl_block: syn::ItemImpl) -> Result<TokenStream2> { let module = quote::format_ident!("{}_types", self_name); let mut key_modules = vec![]; + let mut properties = vec![]; let mut construct = None; let mut set = None; for item in std::mem::take(&mut impl_block.items) { match item { syn::ImplItem::Const(mut item) => { - key_modules.push(process_const( - &mut item, params, self_ty, &self_name, &self_args, - )?); + let (property, module) = + process_const(&mut item, params, self_ty, &self_name, &self_args)?; + properties.push(property); + key_modules.push(module); impl_block.items.push(syn::ImplItem::Const(item)); } syn::ImplItem::Method(method) => { @@ -49,17 +51,34 @@ fn expand(mut impl_block: syn::ItemImpl) -> Result<TokenStream2> { let construct = construct.ok_or_else(|| Error::new(impl_block.span(), "missing constructor"))?; - let set = if impl_block.items.is_empty() { - set.unwrap_or_else(|| { - parse_quote! { - fn set(_: &mut Args, _: &mut StyleMap) -> TypResult<()> { - Ok(()) + let set = set.unwrap_or_else(|| { + let sets = properties.into_iter().filter(|p| !p.skip).map(|property| { + let name = property.name; + let string = name.to_string().replace("_", "-").to_lowercase(); + + let alternative = if property.variadic { + quote! { + .or_else(|| { + let list: Vec<_> = args.all().collect(); + (!list.is_empty()).then(|| list) + }) } + } else if property.shorthand { + quote! { .or_else(|| args.find()) } + } else { + quote! {} + }; + + quote! { styles.set_opt(Self::#name, args.named(#string)? #alternative); } + }); + + parse_quote! { + fn set(args: &mut Args, styles: &mut StyleMap) -> TypResult<()> { + #(#sets)* + Ok(()) } - }) - } else { - set.ok_or_else(|| Error::new(impl_block.span(), "missing set method"))? - }; + } + }); // Put everything into a module with a hopefully unique type to isolate // it from the outside. @@ -87,6 +106,14 @@ fn expand(mut impl_block: syn::ItemImpl) -> Result<TokenStream2> { }) } +/// A style property. +struct Property { + name: Ident, + shorthand: bool, + variadic: bool, + skip: bool, +} + /// Parse the name and generic type arguments of the node type. fn parse_self(self_ty: &syn::Type) -> Result<(String, Vec<&syn::Type>)> { // Extract the node type for which we want to generate properties. @@ -120,7 +147,7 @@ fn process_const( self_ty: &syn::Type, self_name: &str, self_args: &[&syn::Type], -) -> Result<syn::ItemMod> { +) -> Result<(Property, syn::ItemMod)> { // The module that will contain the `Key` type. let module_name = &item.ident; @@ -139,13 +166,16 @@ fn process_const( let default = &item.expr; let mut folder = None; - let mut nonfolding = Some(quote! { - impl<#params> Nonfolding for Key<#key_args> {} - }); + let mut property = Property { + name: item.ident.clone(), + shorthand: false, + variadic: false, + skip: false, + }; - // Look for a folding function like `#[fold(u64::add)]`. - for attr in &item.attrs { + for attr in std::mem::take(&mut item.attrs) { if attr.path.is_ident("fold") { + // Look for a folding function like `#[fold(u64::add)]`. let func: syn::Expr = attr.parse_args()?; folder = Some(quote! { const FOLDING: bool = true; @@ -155,10 +185,30 @@ fn process_const( f(inner, outer) } }); - nonfolding = None; + } else if attr.path.is_ident("shorthand") { + property.shorthand = true; + } else if attr.path.is_ident("variadic") { + property.variadic = true; + } else if attr.path.is_ident("skip") { + property.skip = true; + } else { + item.attrs.push(attr); } } + if property.shorthand && property.variadic { + return Err(Error::new( + property.name.span(), + "shorthand and variadic are mutually exclusive", + )); + } + + let nonfolding = folder.is_none().then(|| { + quote! { + impl<#params> Nonfolding for Key<#key_args> {} + } + }); + // Generate the module code. let module = parse_quote! { #[allow(non_snake_case)] @@ -204,5 +254,5 @@ fn process_const( item.ty = parse_quote! { #module_name::Key<#key_args> }; item.expr = parse_quote! { #module_name::Key(PhantomData) }; - Ok(module) + Ok((property, module)) } |
