diff options
| author | Laurenz <laurmaedje@gmail.com> | 2022-04-30 14:12:28 +0200 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2022-04-30 14:12:28 +0200 |
| commit | f9e115daf54c29358f890b137f50a33a781af680 (patch) | |
| tree | 496de52246629ea8039db6beea94eb779ed2851d /macros | |
| parent | f7c67cde72e6a67f45180856b332bae9863243bd (diff) | |
New block spacing model
Diffstat (limited to 'macros')
| -rw-r--r-- | macros/src/lib.rs | 120 |
1 files changed, 82 insertions, 38 deletions
diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 15925df7..027db89d 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -53,38 +53,7 @@ fn expand(stream: TokenStream2, mut impl_block: syn::ItemImpl) -> Result<TokenSt let construct = construct.ok_or_else(|| Error::new(impl_block.span(), "missing constructor"))?; - let set = set.unwrap_or_else(|| { - let sets = properties.into_iter().filter(|p| !p.hidden).map(|property| { - let name = property.name; - let string = name.to_string().replace("_", "-").to_lowercase(); - - let value = if property.variadic { - quote! { - match args.named(#string)? { - Some(value) => value, - None => { - let list: Vec<_> = args.all()?; - (!list.is_empty()).then(|| list) - } - } - } - } else if property.shorthand { - quote! { args.named_or_find(#string)? } - } else { - quote! { args.named(#string)? } - }; - - quote! { styles.set_opt(Self::#name, #value); } - }); - - parse_quote! { - fn set(args: &mut Args) -> TypResult<StyleMap> { - let mut styles = StyleMap::new(); - #(#sets)* - Ok(styles) - } - } - }); + let set = set.unwrap_or_else(|| generate_set(&properties)); let showable = match stream.to_string().as_str() { "" => false, @@ -204,7 +173,7 @@ fn process_const( }; } else if property.resolve { get = quote! { - let value = values.next().cloned().unwrap_or(#default); + let value = values.next().cloned().unwrap_or_else(|| #default); model::Resolve::resolve(value, chain) }; } else if property.fold { @@ -277,19 +246,24 @@ struct Property { name: Ident, hidden: bool, referenced: bool, - shorthand: bool, + shorthand: Option<Shorthand>, variadic: bool, resolve: bool, fold: bool, } +enum Shorthand { + Positional, + Named(Ident), +} + /// Parse a style property attribute. fn parse_property(item: &mut syn::ImplItemConst) -> Result<Property> { let mut property = Property { name: item.ident.clone(), hidden: false, referenced: false, - shorthand: false, + shorthand: None, variadic: false, resolve: false, fold: false, @@ -301,11 +275,26 @@ fn parse_property(item: &mut syn::ImplItemConst) -> Result<Property> { .position(|attr| attr.path.get_ident().map_or(false, |name| name == "property")) { let attr = item.attrs.remove(idx); - for token in attr.parse_args::<TokenStream2>()? { + let mut stream = attr.parse_args::<TokenStream2>()?.into_iter().peekable(); + while let Some(token) = stream.next() { match token { TokenTree::Ident(ident) => match ident.to_string().as_str() { "hidden" => property.hidden = true, - "shorthand" => property.shorthand = true, + "shorthand" => { + let short = if let Some(TokenTree::Group(group)) = stream.peek() { + let span = group.span(); + let repr = group.to_string(); + let ident = repr.trim_matches(|c| matches!(c, '(' | ')')); + if !ident.chars().all(|c| c.is_ascii_alphabetic()) { + return Err(Error::new(span, "invalid args")); + } + stream.next(); + Shorthand::Named(Ident::new(ident, span)) + } else { + Shorthand::Positional + }; + property.shorthand = Some(short); + } "referenced" => property.referenced = true, "variadic" => property.variadic = true, "resolve" => property.resolve = true, @@ -319,7 +308,7 @@ fn parse_property(item: &mut syn::ImplItemConst) -> Result<Property> { } let span = property.name.span(); - if property.shorthand && property.variadic { + if property.shorthand.is_some() && property.variadic { return Err(Error::new( span, "shorthand and variadic are mutually exclusive", @@ -335,3 +324,58 @@ fn parse_property(item: &mut syn::ImplItemConst) -> Result<Property> { Ok(property) } + +/// Auto-generate a `set` function from properties. +fn generate_set(properties: &[Property]) -> syn::ImplItemMethod { + let mut shorthands = vec![]; + let sets: Vec<_> = properties + .iter() + .filter(|p| !p.hidden) + .map(|property| { + let name = &property.name; + let string = name.to_string().replace("_", "-").to_lowercase(); + + let value = if property.variadic { + quote! { + match args.named(#string)? { + Some(value) => value, + None => { + let list: Vec<_> = args.all()?; + (!list.is_empty()).then(|| list) + } + } + } + } else if let Some(short) = &property.shorthand { + match short { + Shorthand::Positional => quote! { args.named_or_find(#string)? }, + Shorthand::Named(named) => { + shorthands.push(named); + quote! { args.named(#string)?.or_else(|| #named.clone()) } + } + } + } else { + quote! { args.named(#string)? } + }; + + + quote! { styles.set_opt(Self::#name, #value); } + }) + .collect(); + + shorthands.sort(); + shorthands.dedup_by_key(|ident| ident.to_string()); + + let bindings = shorthands.into_iter().map(|ident| { + let string = ident.to_string(); + quote! { let #ident = args.named(#string)?; } + }); + + parse_quote! { + fn set(args: &mut Args) -> TypResult<StyleMap> { + let mut styles = StyleMap::new(); + #(#bindings)* + #(#sets)* + Ok(styles) + } + } +} |
