diff options
| author | Laurenz <laurmaedje@gmail.com> | 2023-03-13 21:39:38 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2023-03-13 21:40:06 +0100 |
| commit | 880b1847bd4170ce80be5781c2163ba085cdcaff (patch) | |
| tree | 3fbfdb70cb04c4922f0ec9e3f29f2c63d11d753b /macros/src/castable.rs | |
| parent | cb3c263c4a67f4d361dbdb5048a1c073bd1fff96 (diff) | |
Derive `Cast` for enums
Diffstat (limited to 'macros/src/castable.rs')
| -rw-r--r-- | macros/src/castable.rs | 62 |
1 files changed, 62 insertions, 0 deletions
diff --git a/macros/src/castable.rs b/macros/src/castable.rs index c0d0c1ad..cd05ed2d 100644 --- a/macros/src/castable.rs +++ b/macros/src/castable.rs @@ -1,5 +1,67 @@ use super::*; +/// Expand the `#[derive(Cast)]` macro. +pub fn cast(item: DeriveInput) -> Result<TokenStream> { + let ty = &item.ident; + + let syn::Data::Enum(data) = &item.data else { + bail!(item, "only enums are supported"); + }; + + let mut variants = vec![]; + for variant in &data.variants { + if let Some((_, expr)) = &variant.discriminant { + bail!(expr, "explicit discriminant is not allowed"); + } + + let string = if let Some(attr) = + variant.attrs.iter().find(|attr| attr.path.is_ident("string")) + { + attr.parse_args::<syn::LitStr>()?.value() + } else { + kebab_case(&variant.ident) + }; + + variants.push(Variant { + ident: variant.ident.clone(), + string, + docs: documentation(&variant.attrs), + }); + } + + let strs_to_variants = variants.iter().map(|Variant { ident, string, docs }| { + quote! { + #[doc = #docs] + #string => Self::#ident + } + }); + + let variants_to_strs = variants.iter().map(|Variant { ident, string, .. }| { + quote! { + #ty::#ident => #string + } + }); + + Ok(quote! { + ::typst::eval::cast_from_value! { + #ty, + #(#strs_to_variants),* + } + + ::typst::eval::cast_to_value! { + v: #ty => ::typst::eval::Value::from(match v { + #(#variants_to_strs),* + }) + } + }) +} + +struct Variant { + ident: Ident, + string: String, + docs: String, +} + /// Expand the `cast_from_value!` macro. pub fn cast_from_value(stream: TokenStream) -> Result<TokenStream> { let castable: Castable = syn::parse2(stream)?; |
