summaryrefslogtreecommitdiff
path: root/macros/src/castable.rs
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2023-07-02 19:59:52 +0200
committerLaurenz <laurmaedje@gmail.com>2023-07-02 20:07:43 +0200
commitebfdb1dafa430786db10dad2ef7d5467c1bdbed1 (patch)
tree2bbc24ddb4124c4bb14dec0e536129d4de37b056 /macros/src/castable.rs
parent3ab19185093d7709f824b95b979060ce125389d8 (diff)
Move everything into `crates/` directory
Diffstat (limited to 'macros/src/castable.rs')
-rw-r--r--macros/src/castable.rs331
1 files changed, 0 insertions, 331 deletions
diff --git a/macros/src/castable.rs b/macros/src/castable.rs
deleted file mode 100644
index 05c1b4d1..00000000
--- a/macros/src/castable.rs
+++ /dev/null
@@ -1,331 +0,0 @@
-use super::*;
-
-/// Expand the `#[derive(Cast)]` macro.
-pub fn derive_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! {
- #ty,
- self => ::typst::eval::IntoValue::into_value(match self {
- #(#variants_to_strs),*
- }),
- #(#strs_to_variants),*
- }
- })
-}
-
-/// An enum variant in a `derive(Cast)`.
-struct Variant {
- ident: Ident,
- string: String,
- docs: String,
-}
-
-/// Expand the `cast!` macro.
-pub fn cast(stream: TokenStream) -> Result<TokenStream> {
- let input: CastInput = syn::parse2(stream)?;
- let ty = &input.ty;
- let eval = quote! { ::typst::eval };
-
- let castable_body = create_castable_body(&input);
- let describe_body = create_describe_body(&input);
- let into_value_body = create_into_value_body(&input);
- let from_value_body = create_from_value_body(&input);
-
- let reflect = (!input.from_value.is_empty() || input.name.is_some()).then(|| {
- quote! {
- impl #eval::Reflect for #ty {
- fn describe() -> #eval::CastInfo {
- #describe_body
- }
-
- fn castable(value: &#eval::Value) -> bool {
- #castable_body
- }
- }
- }
- });
-
- let into_value = (input.into_value.is_some() || input.name.is_some()).then(|| {
- quote! {
- impl #eval::IntoValue for #ty {
- fn into_value(self) -> #eval::Value {
- #into_value_body
- }
- }
- }
- });
-
- let from_value = (!input.from_value.is_empty() || input.name.is_some()).then(|| {
- quote! {
- impl #eval::FromValue for #ty {
- fn from_value(value: #eval::Value) -> ::typst::diag::StrResult<Self> {
- #from_value_body
- }
- }
- }
- });
-
- let ty = input.name.as_ref().map(|name| {
- quote! {
- impl #eval::Type for #ty {
- const TYPE_NAME: &'static str = #name;
- }
- }
- });
-
- Ok(quote! {
- #reflect
- #into_value
- #from_value
- #ty
- })
-}
-
-/// The input to `cast!`.
-struct CastInput {
- ty: syn::Type,
- name: Option<syn::LitStr>,
- into_value: Option<syn::Expr>,
- from_value: Punctuated<Cast, Token![,]>,
-}
-
-impl Parse for CastInput {
- fn parse(input: ParseStream) -> Result<Self> {
- let ty;
- let mut name = None;
- if input.peek(syn::Token![type]) {
- let _: syn::Token![type] = input.parse()?;
- ty = input.parse()?;
- let _: syn::Token![:] = input.parse()?;
- name = Some(input.parse()?);
- } else {
- ty = input.parse()?;
- }
-
- let _: syn::Token![,] = input.parse()?;
-
- let mut into_value = None;
- if input.peek(syn::Token![self]) {
- let _: syn::Token![self] = input.parse()?;
- let _: syn::Token![=>] = input.parse()?;
- into_value = Some(input.parse()?);
- let _: syn::Token![,] = input.parse()?;
- }
-
- let from_value = Punctuated::parse_terminated(input)?;
- Ok(Self { ty, name, into_value, from_value })
- }
-}
-
-impl Parse for Cast {
- fn parse(input: ParseStream) -> Result<Self> {
- let attrs = input.call(syn::Attribute::parse_outer)?;
- let pattern = input.parse()?;
- let _: syn::Token![=>] = input.parse()?;
- let expr = input.parse()?;
- Ok(Self { attrs, pattern, expr })
- }
-}
-
-impl Parse for Pattern {
- fn parse(input: ParseStream) -> Result<Self> {
- if input.peek(syn::LitStr) {
- Ok(Pattern::Str(input.parse()?))
- } else {
- let pat = syn::Pat::parse_single(input)?;
- let _: syn::Token![:] = input.parse()?;
- let ty = input.parse()?;
- Ok(Pattern::Ty(pat, ty))
- }
- }
-}
-
-/// A single cast, e.g. `v: i64 => Self::Int(v)`.
-struct Cast {
- attrs: Vec<syn::Attribute>,
- pattern: Pattern,
- expr: syn::Expr,
-}
-
-/// A pattern in a cast, e.g.`"ascender"` or `v: i64`.
-enum Pattern {
- Str(syn::LitStr),
- Ty(syn::Pat, syn::Type),
-}
-
-fn create_castable_body(input: &CastInput) -> TokenStream {
- let mut strings = vec![];
- let mut casts = vec![];
-
- for cast in &input.from_value {
- match &cast.pattern {
- Pattern::Str(lit) => {
- strings.push(quote! { #lit => return true });
- }
- Pattern::Ty(_, ty) => {
- casts.push(quote! {
- if <#ty as ::typst::eval::Reflect>::castable(value) {
- return true;
- }
- });
- }
- }
- }
-
- let dynamic_check = input.name.is_some().then(|| {
- quote! {
- if let ::typst::eval::Value::Dyn(dynamic) = &value {
- if dynamic.is::<Self>() {
- return true;
- }
- }
- }
- });
-
- let str_check = (!strings.is_empty()).then(|| {
- quote! {
- if let ::typst::eval::Value::Str(string) = &value {
- match string.as_str() {
- #(#strings,)*
- _ => {}
- }
- }
- }
- });
-
- quote! {
- #dynamic_check
- #str_check
- #(#casts)*
- false
- }
-}
-
-fn create_describe_body(input: &CastInput) -> TokenStream {
- let mut infos = vec![];
-
- for cast in &input.from_value {
- let docs = documentation(&cast.attrs);
- infos.push(match &cast.pattern {
- Pattern::Str(lit) => {
- quote! {
- ::typst::eval::CastInfo::Value(
- ::typst::eval::IntoValue::into_value(#lit),
- #docs,
- )
- }
- }
- Pattern::Ty(_, ty) => {
- quote! { <#ty as ::typst::eval::Reflect>::describe() }
- }
- });
- }
-
- if let Some(name) = &input.name {
- infos.push(quote! {
- ::typst::eval::CastInfo::Type(#name)
- });
- }
-
- quote! {
- #(#infos)+*
- }
-}
-
-fn create_into_value_body(input: &CastInput) -> TokenStream {
- if let Some(expr) = &input.into_value {
- quote! { #expr }
- } else {
- quote! { ::typst::eval::Value::dynamic(self) }
- }
-}
-
-fn create_from_value_body(input: &CastInput) -> TokenStream {
- let mut string_arms = vec![];
- let mut cast_checks = vec![];
-
- for cast in &input.from_value {
- let expr = &cast.expr;
- match &cast.pattern {
- Pattern::Str(lit) => {
- string_arms.push(quote! { #lit => return Ok(#expr) });
- }
- Pattern::Ty(binding, ty) => {
- cast_checks.push(quote! {
- if <#ty as ::typst::eval::Reflect>::castable(&value) {
- let #binding = <#ty as ::typst::eval::FromValue>::from_value(value)?;
- return Ok(#expr);
- }
- });
- }
- }
- }
-
- let dynamic_check = input.name.is_some().then(|| {
- quote! {
- if let ::typst::eval::Value::Dyn(dynamic) = &value {
- if let Some(concrete) = dynamic.downcast::<Self>() {
- return Ok(concrete.clone());
- }
- }
- }
- });
-
- let str_check = (!string_arms.is_empty()).then(|| {
- quote! {
- if let ::typst::eval::Value::Str(string) = &value {
- match string.as_str() {
- #(#string_arms,)*
- _ => {}
- }
- }
- }
- });
-
- quote! {
- #dynamic_check
- #str_check
- #(#cast_checks)*
- Err(<Self as ::typst::eval::Reflect>::error(&value))
- }
-}