summaryrefslogtreecommitdiff
path: root/macros/src/lib.rs
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2022-04-07 10:50:39 +0200
committerLaurenz <laurmaedje@gmail.com>2022-04-07 11:07:00 +0200
commit3d52387eea321e94c13b61666f7a758052b20c5d (patch)
tree5c55c51ca7e4b53dee61d280c39b7f664b8b9d6b /macros/src/lib.rs
parent20b4d590b3efbd9b7a44fd6d3a658e7b84d21b99 (diff)
Rework style chains
Diffstat (limited to 'macros/src/lib.rs')
-rw-r--r--macros/src/lib.rs172
1 files changed, 109 insertions, 63 deletions
diff --git a/macros/src/lib.rs b/macros/src/lib.rs
index 7478c8cb..09739b05 100644
--- a/macros/src/lib.rs
+++ b/macros/src/lib.rs
@@ -1,8 +1,8 @@
extern crate proc_macro;
use proc_macro::TokenStream;
-use proc_macro2::TokenStream as TokenStream2;
-use quote::quote;
+use proc_macro2::{TokenStream as TokenStream2, TokenTree};
+use quote::{quote, quote_spanned};
use syn::parse_quote;
use syn::punctuated::Punctuated;
use syn::spanned::Spanned;
@@ -54,7 +54,7 @@ fn expand(stream: TokenStream2, mut impl_block: syn::ItemImpl) -> Result<TokenSt
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.skip).map(|property| {
+ let sets = properties.into_iter().filter(|p| !p.hidden).map(|property| {
let name = property.name;
let string = name.to_string().replace("_", "-").to_lowercase();
@@ -116,14 +116,6 @@ fn expand(stream: TokenStream2, mut impl_block: syn::ItemImpl) -> Result<TokenSt
})
}
-/// 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,
@@ -153,12 +145,21 @@ fn process_const(
self_name: &str,
self_args: &Punctuated<syn::GenericArgument, syn::Token![,]>,
) -> Result<(Property, syn::ItemMod)> {
+ let property = parse_property(item)?;
+
// The display name, e.g. `TextNode::STRONG`.
let name = format!("{}::{}", self_name, &item.ident);
// The type of the property's value is what the user of our macro wrote
// as type of the const ...
let value_ty = &item.ty;
+ let output_ty = if property.fold {
+ parse_quote!(<#value_ty as eval::Fold>::Output)
+ } else if property.referenced {
+ parse_quote!(&'a #value_ty)
+ } else {
+ value_ty.clone()
+ };
// ... but the real type of the const becomes this..
let key = quote! { Key<#value_ty, #self_args> };
@@ -172,50 +173,41 @@ fn process_const(
_ => true,
});
- // The default value of the property is what the user wrote as
- // initialization value of the const.
let default = &item.expr;
- let mut fold = None;
- let mut property = Property {
- name: item.ident.clone(),
- shorthand: false,
- variadic: false,
- skip: false,
- };
-
- for attr in std::mem::take(&mut item.attrs) {
- match attr.path.get_ident().map(ToString::to_string).as_deref() {
- Some("fold") => {
- // Look for a folding function like `#[fold(u64::add)]`.
- let func: syn::Expr = attr.parse_args()?;
- fold = Some(quote! {
- const FOLDING: bool = true;
-
- fn fold(inner: Self::Value, outer: Self::Value) -> Self::Value {
- let f: fn(Self::Value, Self::Value) -> Self::Value = #func;
- f(inner, outer)
- }
- });
+ // Ensure that the type is either `Copy` or that the property is referenced
+ // or that the property isn't copy but can't be referenced because it needs
+ // folding.
+ let get;
+ let mut copy = None;
+
+ if property.referenced {
+ get = quote! {
+ values.next().unwrap_or_else(|| {
+ static LAZY: Lazy<#value_ty> = Lazy::new(|| #default);
+ &*LAZY
+ })
+ };
+ } else if property.fold {
+ get = quote! {
+ match values.next().cloned() {
+ Some(inner) => eval::Fold::fold(inner, Self::get(values)),
+ None => #default,
}
- Some("shorthand") => property.shorthand = true,
- Some("variadic") => property.variadic = true,
- Some("skip") => property.skip = true,
- _ => item.attrs.push(attr),
- }
- }
-
- if property.shorthand && property.variadic {
- return Err(Error::new(
- property.name.span(),
- "shorthand and variadic are mutually exclusive",
- ));
+ };
+ } else {
+ get = quote! {
+ values.next().copied().unwrap_or(#default)
+ };
+
+ copy = Some(quote_spanned! { item.ty.span() =>
+ const _: fn() -> () = || {
+ fn type_must_be_copy_or_fold_or_referenced<T: Copy>() {}
+ type_must_be_copy_or_fold_or_referenced::<#value_ty>();
+ };
+ });
}
- let referencable = fold.is_none().then(|| {
- quote! { impl<#params> eval::Referencable for #key {} }
- });
-
// Generate the module code.
let module_name = &item.ident;
let module = parse_quote! {
@@ -232,28 +224,21 @@ fn process_const(
}
}
- impl<#params> eval::Key for #key {
+ impl<'a, #params> eval::Key<'a> for #key {
type Value = #value_ty;
-
+ type Output = #output_ty;
const NAME: &'static str = #name;
- fn node_id() -> TypeId {
+ fn node() -> TypeId {
TypeId::of::<#self_ty>()
}
- fn default() -> Self::Value {
- #default
- }
-
- fn default_ref() -> &'static Self::Value {
- static LAZY: Lazy<#value_ty> = Lazy::new(|| #default);
- &*LAZY
+ fn get(mut values: impl Iterator<Item = &'a Self::Value>) -> Self::Output {
+ #get
}
-
- #fold
}
- #referencable
+ #copy
}
};
@@ -263,3 +248,64 @@ fn process_const(
Ok((property, module))
}
+
+/// A style property.
+struct Property {
+ name: Ident,
+ hidden: bool,
+ referenced: bool,
+ shorthand: bool,
+ variadic: bool,
+ fold: bool,
+}
+
+/// 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,
+ variadic: false,
+ fold: false,
+ };
+
+ if let Some(idx) = item
+ .attrs
+ .iter()
+ .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>()? {
+ match token {
+ TokenTree::Ident(ident) => match ident.to_string().as_str() {
+ "hidden" => property.hidden = true,
+ "shorthand" => property.shorthand = true,
+ "referenced" => property.referenced = true,
+ "variadic" => property.variadic = true,
+ "fold" => property.fold = true,
+ _ => return Err(Error::new(ident.span(), "invalid attribute")),
+ },
+ TokenTree::Punct(_) => {}
+ _ => return Err(Error::new(token.span(), "invalid token")),
+ }
+ }
+ }
+
+ let span = property.name.span();
+ if property.shorthand && property.variadic {
+ return Err(Error::new(
+ span,
+ "shorthand and variadic are mutually exclusive",
+ ));
+ }
+
+ if property.referenced && property.fold {
+ return Err(Error::new(
+ span,
+ "referenced and fold are mutually exclusive",
+ ));
+ }
+
+ Ok(property)
+}