summaryrefslogtreecommitdiff
path: root/macros/src/castable.rs
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2023-03-13 21:39:38 +0100
committerLaurenz <laurmaedje@gmail.com>2023-03-13 21:40:06 +0100
commit880b1847bd4170ce80be5781c2163ba085cdcaff (patch)
tree3fbfdb70cb04c4922f0ec9e3f29f2c63d11d753b /macros/src/castable.rs
parentcb3c263c4a67f4d361dbdb5048a1c073bd1fff96 (diff)
Derive `Cast` for enums
Diffstat (limited to 'macros/src/castable.rs')
-rw-r--r--macros/src/castable.rs62
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)?;