diff options
| author | Laurenz <laurmaedje@gmail.com> | 2022-01-07 21:24:36 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2022-01-08 00:20:48 +0100 |
| commit | e74ae6ce70d4c6ca006613eadf07f920951789e3 (patch) | |
| tree | 0b9b2ddabf79dad8d55631780ee5d70afe7362d7 /macros | |
| parent | 0b624390906e911bde325b487b2710b67c8205c8 (diff) | |
Make all nodes into classes
Diffstat (limited to 'macros')
| -rw-r--r-- | macros/src/lib.rs | 83 |
1 files changed, 58 insertions, 25 deletions
diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 16fb7885..b667d2e2 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -7,48 +7,82 @@ use syn::parse_quote; use syn::spanned::Spanned; use syn::{Error, Result}; -/// Generate node properties. +/// Turn a node into a class. #[proc_macro_attribute] -pub fn properties(_: TokenStream, item: TokenStream) -> TokenStream { +pub fn class(_: TokenStream, item: TokenStream) -> TokenStream { let impl_block = syn::parse_macro_input!(item as syn::ItemImpl); expand(impl_block).unwrap_or_else(|err| err.to_compile_error()).into() } -/// Expand a property impl block for a node. +/// Expand an impl block for a node. fn expand(mut impl_block: syn::ItemImpl) -> Result<TokenStream2> { // Split the node type into name and generic type arguments. + let params = &impl_block.generics.params; let self_ty = &*impl_block.self_ty; let (self_name, self_args) = parse_self(self_ty)?; - // Rewrite the const items from values to keys. - let mut modules = vec![]; - for item in &mut impl_block.items { - if let syn::ImplItem::Const(item) = item { - let module = process_const( - item, - &impl_block.generics, - self_ty, - &self_name, - &self_args, - )?; - modules.push(module); + let module = quote::format_ident!("{}_types", self_name); + + let mut key_modules = 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, + )?); + impl_block.items.push(syn::ImplItem::Const(item)); + } + syn::ImplItem::Method(method) => { + match method.sig.ident.to_string().as_str() { + "construct" => construct = Some(method), + "set" => set = Some(method), + _ => return Err(Error::new(method.span(), "unexpected method")), + } + } + _ => return Err(Error::new(item.span(), "unexpected item")), } } + 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(()) + } + } + }) + } 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. - let module = quote::format_ident!("{}_types", self_name); Ok(quote! { #[allow(non_snake_case)] mod #module { use std::any::TypeId; use std::marker::PhantomData; use once_cell::sync::Lazy; - use crate::eval::{Nonfolding, Property}; + use crate::eval::{Construct, Nonfolding, Property, Set}; use super::*; #impl_block - #(#modules)* + + impl<#params> Construct for #self_ty { + #construct + } + + impl<#params> Set for #self_ty { + #set + } + + #(#key_modules)* } }) } @@ -82,7 +116,7 @@ fn parse_self(self_ty: &syn::Type) -> Result<(String, Vec<&syn::Type>)> { /// Process a single const item. fn process_const( item: &mut syn::ImplItemConst, - impl_generics: &syn::Generics, + params: &syn::punctuated::Punctuated<syn::GenericParam, syn::Token![,]>, self_ty: &syn::Type, self_name: &str, self_args: &[&syn::Type], @@ -95,7 +129,6 @@ fn process_const( let value_ty = &item.ty; // ... but the real type of the const becomes Key<#key_args>. - let key_params = &impl_generics.params; let key_args = quote! { #value_ty #(, #self_args)* }; // The display name, e.g. `TextNode::STRONG`. @@ -107,7 +140,7 @@ fn process_const( let mut folder = None; let mut nonfolding = Some(quote! { - impl<#key_params> Nonfolding for Key<#key_args> {} + impl<#params> Nonfolding for Key<#key_args> {} }); // Look for a folding function like `#[fold(u64::add)]`. @@ -132,16 +165,16 @@ fn process_const( mod #module_name { use super::*; - pub struct Key<T, #key_params>(pub PhantomData<(T, #key_args)>); + pub struct Key<VALUE, #params>(pub PhantomData<(VALUE, #key_args)>); - impl<#key_params> Copy for Key<#key_args> {} - impl<#key_params> Clone for Key<#key_args> { + impl<#params> Copy for Key<#key_args> {} + impl<#params> Clone for Key<#key_args> { fn clone(&self) -> Self { *self } } - impl<#key_params> Property for Key<#key_args> { + impl<#params> Property for Key<#key_args> { type Value = #value_ty; const NAME: &'static str = #name; |
