diff options
Diffstat (limited to 'crates/typst-macros/src/elem.rs')
| -rw-r--r-- | crates/typst-macros/src/elem.rs | 809 |
1 files changed, 204 insertions, 605 deletions
diff --git a/crates/typst-macros/src/elem.rs b/crates/typst-macros/src/elem.rs index 67fe7ed6..e1c901d8 100644 --- a/crates/typst-macros/src/elem.rs +++ b/crates/typst-macros/src/elem.rs @@ -1,9 +1,9 @@ -use heck::{ToKebabCase, ToShoutySnakeCase, ToUpperCamelCase}; +use heck::ToKebabCase; use proc_macro2::TokenStream; use quote::{format_ident, quote}; use syn::parse::{Parse, ParseStream}; use syn::punctuated::Punctuated; -use syn::{parse_quote, Ident, Result, Token}; +use syn::{Ident, Result, Token}; use crate::util::{ determine_name_and_title, documentation, foundations, has_attr, kw, parse_attr, @@ -39,14 +39,13 @@ struct Elem { } impl Elem { - /// Calls the closure to produce a token stream if the - /// element has the given capability. + /// Whether the element has the given trait listed as a capability. fn can(&self, name: &str) -> bool { self.capabilities.iter().any(|capability| capability == name) } - /// Calls the closure to produce a token stream if the - /// element does not have the given capability. + /// Whether the element does not have the given trait listed as a + /// capability. fn cannot(&self, name: &str) -> bool { !self.can(name) } @@ -68,21 +67,6 @@ impl Elem { self.struct_fields().filter(|field| !field.required) } - /// Fields that are relevant for equality. - /// - /// Synthesized fields are excluded to ensure equality before and after - /// synthesis. - fn eq_fields(&self) -> impl Iterator<Item = &Field> + Clone { - self.struct_fields().filter(|field| !field.synthesized) - } - - /// Fields that show up in the documentation. - fn doc_fields(&self) -> impl Iterator<Item = &Field> + Clone { - self.fields - .iter() - .filter(|field| !field.internal && !field.synthesized) - } - /// Fields that are relevant for `Construct` impl. /// /// The reason why fields that are `parse` and internal are allowed is @@ -98,44 +82,20 @@ impl Elem { fn set_fields(&self) -> impl Iterator<Item = &Field> + Clone { self.construct_fields().filter(|field| !field.required) } - - /// Fields that can be accessed from the style chain. - fn style_fields(&self) -> impl Iterator<Item = &Field> + Clone { - self.real_fields() - .filter(|field| !field.required && !field.synthesized) - } - - /// Fields that are visible to the user. - fn visible_fields(&self) -> impl Iterator<Item = &Field> + Clone { - self.real_fields().filter(|field| !field.internal) - } } /// A field of an [element definition][`Elem`]. struct Field { + /// The index of the field among all. + i: u8, /// The name of this field. ident: Ident, - /// The identifier `{ident}_in`. - ident_in: Ident, /// The identifier `with_{ident}`. with_ident: Ident, - /// The identifier `push_{ident}`. - push_ident: Ident, - /// The identifier `set_{ident}`. - set_ident: Ident, - /// The upper camel-case version of `ident`, used for the enum variant name. - enum_ident: Ident, - /// The all-caps snake-case version of `ident`, used for the constant name. - const_ident: Ident, /// The visibility of this field. vis: syn::Visibility, /// The type of this field. ty: syn::Type, - /// The type returned by accessor methods for this field. - /// - /// Usually, this is the same as `ty`, but this might be different - /// if this field has a `#[resolve]` attribute. - output: syn::Type, /// The field's identifier as exposed to Typst. name: String, /// The documentation for this field as a string. @@ -147,16 +107,12 @@ struct Field { /// Whether this field is variadic; that is, has its values /// taken from a variable number of arguments. variadic: bool, - /// Whether this field has a `#[resolve]` attribute. - resolve: bool, /// Whether this field has a `#[fold]` attribute. fold: bool, /// Whether this field is excluded from documentation. internal: bool, /// Whether this field exists only in documentation. external: bool, - /// Whether this field has a `#[borrowed]` attribute. - borrowed: bool, /// Whether this field has a `#[ghost]` attribute. ghost: bool, /// Whether this field has a `#[synthesized]` attribute. @@ -206,7 +162,12 @@ fn parse(stream: TokenStream, body: &syn::ItemStruct) -> Result<Elem> { bail!(body, "expected named fields"); }; - let fields = named.named.iter().map(parse_field).collect::<Result<Vec<_>>>()?; + let mut fields = named.named.iter().map(parse_field).collect::<Result<Vec<_>>>()?; + fields.sort_by_key(|field| field.internal); + for (i, field) in fields.iter_mut().enumerate() { + field.i = i as u8; + } + if fields.iter().any(|field| field.ghost && !field.internal) && meta.capabilities.iter().all(|capability| capability != "Construct") { @@ -243,27 +204,20 @@ fn parse_field(field: &syn::Field) -> Result<Field> { let required = has_attr(&mut attrs, "required") || variadic; let positional = has_attr(&mut attrs, "positional") || required; - let mut field = Field { + let field = Field { + i: 0, ident: ident.clone(), - ident_in: format_ident!("{ident}_in"), with_ident: format_ident!("with_{ident}"), - push_ident: format_ident!("push_{ident}"), - set_ident: format_ident!("set_{ident}"), - enum_ident: Ident::new(&ident.to_string().to_upper_camel_case(), ident.span()), - const_ident: Ident::new(&ident.to_string().to_shouty_snake_case(), ident.span()), vis: field.vis.clone(), ty: field.ty.clone(), - output: field.ty.clone(), name: ident.to_string().to_kebab_case(), docs: documentation(&attrs), positional, required, variadic, - resolve: has_attr(&mut attrs, "resolve"), fold: has_attr(&mut attrs, "fold"), internal: has_attr(&mut attrs, "internal"), external: has_attr(&mut attrs, "external"), - borrowed: has_attr(&mut attrs, "borrowed"), ghost: has_attr(&mut attrs, "ghost"), synthesized: has_attr(&mut attrs, "synthesized"), parse: parse_attr(&mut attrs, "parse")?.flatten(), @@ -275,17 +229,9 @@ fn parse_field(field: &syn::Field) -> Result<Field> { } if (field.required || field.synthesized) - && (field.default.is_some() || field.fold || field.resolve || field.ghost) + && (field.default.is_some() || field.fold || field.ghost) { - bail!( - ident, - "required and synthesized fields cannot be default, fold, resolve, or ghost" - ); - } - - if field.resolve { - let ty = &field.ty; - field.output = parse_quote! { <#ty as #foundations::Resolve>::Output }; + bail!(ident, "required and synthesized fields cannot be default, fold, or ghost"); } validate_attrs(&attrs)?; @@ -297,30 +243,17 @@ fn parse_field(field: &syn::Field) -> Result<Field> { fn create(element: &Elem) -> Result<TokenStream> { // The struct itself. let struct_ = create_struct(element); - let inherent_impl = create_inherent_impl(element); - // The enum with the struct's fields. - let fields_enum = create_fields_enum(element); - - // The statics with borrowed fields' default values. - let default_statics = element - .style_fields() - .filter(|field| field.borrowed) - .map(create_default_static); - - // Trait implementations. + // Implementations. + let inherent_impl = create_inherent_impl(element); let native_element_impl = create_native_elem_impl(element); - let partial_eq_impl = - element.cannot("PartialEq").then(|| create_partial_eq_impl(element)); + let field_impls = + element.fields.iter().map(|field| create_field_impl(element, field)); let construct_impl = element.cannot("Construct").then(|| create_construct_impl(element)); let set_impl = element.cannot("Set").then(|| create_set_impl(element)); - let capable_impl = create_capable_impl(element); - let fields_impl = create_fields_impl(element); - let repr_impl = element.cannot("Repr").then(|| create_repr_impl(element)); let locatable_impl = element.can("Locatable").then(|| create_locatable_impl(element)); let mathy_impl = element.can("Mathy").then(|| create_mathy_impl(element)); - let into_value_impl = create_into_value_impl(element); // We use a const block to create an anonymous scope, as to not leak any // local definitions. @@ -328,19 +261,13 @@ fn create(element: &Elem) -> Result<TokenStream> { #struct_ const _: () = { - #fields_enum - #(#default_statics)* #inherent_impl #native_element_impl - #fields_impl - #capable_impl + #(#field_impls)* #construct_impl #set_impl - #partial_eq_impl - #repr_impl #locatable_impl #mathy_impl - #into_value_impl }; }) } @@ -365,80 +292,13 @@ fn create_struct(element: &Elem) -> TokenStream { /// Create a field declaration for the struct. fn create_field(field: &Field) -> TokenStream { - let Field { vis, ident, ty, .. } = field; + let Field { i, vis, ident, ty, .. } = field; if field.required { quote! { #vis #ident: #ty } + } else if field.synthesized { + quote! { #vis #ident: ::std::option::Option<#ty> } } else { - quote! { #ident: ::std::option::Option<#ty> } - } -} - -/// Creates the element's enum for field identifiers. -fn create_fields_enum(element: &Elem) -> TokenStream { - let variants: Vec<_> = element.real_fields().map(|field| &field.enum_ident).collect(); - let names: Vec<_> = element.real_fields().map(|field| &field.name).collect(); - let consts: Vec<_> = element.real_fields().map(|field| &field.const_ident).collect(); - let repr = (!variants.is_empty()).then(|| quote! { #[repr(u8)] }); - - quote! { - #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] - #repr - pub enum Fields { - #(#variants,)* - } - - impl Fields { - /// Converts the field identifier to the field name. - pub fn to_str(self) -> &'static str { - match self { - #(Self::#variants => #names,)* - } - } - } - - impl ::std::convert::TryFrom<u8> for Fields { - type Error = #foundations::FieldAccessError; - - fn try_from(value: u8) -> Result<Self, Self::Error> { - #(const #consts: u8 = Fields::#variants as u8;)* - match value { - #(#consts => Ok(Self::#variants),)* - _ => Err(#foundations::FieldAccessError::Internal), - } - } - } - - impl ::std::str::FromStr for Fields { - type Err = (); - - fn from_str(s: &str) -> Result<Self, Self::Err> { - match s { - #(#names => Ok(Self::#variants),)* - _ => Err(()), - } - } - } - - impl ::std::fmt::Display for Fields { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - f.pad(self.to_str()) - } - } - } -} - -/// Creates a static with a borrowed field's default value. -fn create_default_static(field: &Field) -> TokenStream { - let Field { const_ident, default, ty, .. } = field; - - let init = match default { - Some(default) => quote! { || #default }, - None => quote! { ::std::default::Default::default }, - }; - - quote! { - static #const_ident: ::std::sync::LazyLock<#ty> = - ::std::sync::LazyLock::new(#init); + quote! { #vis #ident: #foundations::Settable<Self, #i> } } } @@ -448,19 +308,23 @@ fn create_inherent_impl(element: &Elem) -> TokenStream { let new_func = create_new_func(element); let with_field_methods = element.accessor_fields().map(create_with_field_method); - let push_field_methods = element.accessor_fields().map(create_push_field_method); - let field_methods = element.accessor_fields().map(create_field_method); - let field_in_methods = element.style_fields().map(create_field_in_method); - let set_field_methods = element.style_fields().map(create_set_field_method); + + let style_consts = element.real_fields().map(|field| { + let Field { i, vis, ident, .. } = field; + quote! { + #vis const #ident: #foundations::Field<Self, #i> + = #foundations::Field::new(); + } + }); quote! { impl #ident { #new_func #(#with_field_methods)* - #(#push_field_methods)* - #(#field_methods)* - #(#field_in_methods)* - #(#set_field_methods)* + } + #[allow(non_upper_case_globals)] + impl #ident { + #(#style_consts)* } } } @@ -476,8 +340,10 @@ fn create_new_func(element: &Elem) -> TokenStream { let ident = &field.ident; if field.required { quote! { #ident } - } else { + } else if field.synthesized { quote! { #ident: None } + } else { + quote! { #ident: #foundations::Settable::new() } } }); @@ -491,254 +357,189 @@ fn create_new_func(element: &Elem) -> TokenStream { /// Create a builder-style setter method for a field. fn create_with_field_method(field: &Field) -> TokenStream { - let Field { vis, ident, with_ident, push_ident, name, ty, .. } = field; + let Field { vis, ident, with_ident, name, ty, .. } = field; let doc = format!("Builder-style setter for the [`{name}`](Self::{ident}) field."); - quote! { - #[doc = #doc] - #vis fn #with_ident(mut self, #ident: #ty) -> Self { - self.#push_ident(#ident); - self - } - } -} - -/// Create a setter method for a field. -fn create_push_field_method(field: &Field) -> TokenStream { - let Field { vis, ident, push_ident, name, ty, .. } = field; - let doc = format!("Setter for the [`{name}`](Self::{ident}) field."); let expr = if field.required { - quote! { #ident } + quote! { self.#ident = #ident } + } else if field.synthesized { + quote! { self.#ident = Some(#ident) } } else { - quote! { Some(#ident) } + quote! { self.#ident.set(#ident) } }; quote! { #[doc = #doc] - #vis fn #push_ident(&mut self, #ident: #ty) { - self.#ident = #expr; + #vis fn #with_ident(mut self, #ident: #ty) -> Self { + #expr; + self } } } -/// Create an accessor method for a field. -fn create_field_method(field: &Field) -> TokenStream { - let Field { vis, docs, ident, output, .. } = field; +/// Creates the element's `NativeElement` implementation. +fn create_native_elem_impl(element: &Elem) -> TokenStream { + let Elem { name, ident, title, scope, keywords, docs, .. } = element; - if field.required { - quote! { - #[doc = #docs] - #vis fn #ident(&self) -> &#output { - &self.#ident - } - } - } else if field.synthesized { - quote! { - #[doc = #docs] - #[track_caller] - #vis fn #ident(&self) -> ::std::option::Option<&#output> { - self.#ident.as_ref() - } - } - } else { - let sig = if field.borrowed { - quote! { <'a>(&'a self, styles: #foundations::StyleChain<'a>) -> &'a #output } + let fields = element.fields.iter().filter(|field| !field.internal).map(|field| { + let i = field.i; + if field.external { + quote! { #foundations::ExternalFieldData::<#ident, #i>::vtable() } + } else if field.variadic { + quote! { #foundations::RequiredFieldData::<#ident, #i>::vtable_variadic() } + } else if field.required { + quote! { #foundations::RequiredFieldData::<#ident, #i>::vtable() } + } else if field.synthesized { + quote! { #foundations::SynthesizedFieldData::<#ident, #i>::vtable() } + } else if field.ghost { + quote! { #foundations::SettablePropertyData::<#ident, #i>::vtable() } } else { - quote! { (&self, styles: #foundations::StyleChain) -> #output } - }; - - let mut value = create_style_chain_access( - field, - field.borrowed, - quote! { self.#ident.as_ref() }, - ); - if field.resolve { - value = quote! { #foundations::Resolve::resolve(#value, styles) }; - } - - quote! { - #[doc = #docs] - #vis fn #ident #sig { - #value - } - } - } -} - -/// Create a style accessor method for a field. -fn create_field_in_method(field: &Field) -> TokenStream { - let Field { vis, ident_in, name, output, .. } = field; - let doc = format!("Access the `{name}` field in the given style chain."); - - let ref_ = field.borrowed.then(|| quote! { & }); - - let mut value = create_style_chain_access(field, field.borrowed, quote! { None }); - if field.resolve { - value = quote! { #foundations::Resolve::resolve(#value, styles) }; - } - - quote! { - #[doc = #doc] - #vis fn #ident_in(styles: #foundations::StyleChain) -> #ref_ #output { - #value - } - } -} - -/// Create a style setter method for a field. -fn create_set_field_method(field: &Field) -> TokenStream { - let Field { vis, ident, set_ident, enum_ident, ty, name, .. } = field; - let doc = format!("Create a style property for the `{name}` field."); - - quote! { - #[doc = #doc] - #vis fn #set_ident(#ident: #ty) -> #foundations::Property { - #foundations::Property::new::<Self, _>( - Fields::#enum_ident as u8, - #ident, - ) + quote! { #foundations::SettableFieldData::<#ident, #i>::vtable() } } - } -} - -/// Create a style chain access method for a field. -fn create_style_chain_access( - field: &Field, - borrowed: bool, - inherent: TokenStream, -) -> TokenStream { - let Field { ty, default, enum_ident, const_ident, .. } = field; - - let getter = match (field.fold, borrowed) { - (false, false) => quote! { get }, - (false, true) => quote! { get_ref }, - (true, _) => quote! { get_folded }, - }; + }); - let default = if borrowed { - quote! { || &#const_ident } - } else { - match default { - Some(default) => quote! { || #default }, - None => quote! { ::std::default::Default::default }, + let field_arms = element + .fields + .iter() + .filter(|field| !field.internal && !field.external) + .map(|field| { + let Field { name, i, .. } = field; + quote! { #name => Some(#i) } + }); + let field_id = quote! { + |name| match name { + #(#field_arms,)* + _ => None, } }; - quote! { - styles.#getter::<#ty>( - <Self as #foundations::NativeElement>::elem(), - Fields::#enum_ident as u8, - #inherent, - #default, - ) - } -} - -/// Creates the element's `NativeElement` implementation. -fn create_native_elem_impl(element: &Elem) -> TokenStream { - let Elem { name, ident, title, scope, keywords, docs, .. } = element; + let capable_func = create_capable_func(element); - let local_name = if element.can("LocalName") { - quote! { Some(<#foundations::Packed<#ident> as ::typst_library::text::LocalName>::local_name) } - } else { - quote! { None } - }; + let with_keywords = + (!keywords.is_empty()).then(|| quote! { .with_keywords(&[#(#keywords),*]) }); + let with_repr = element.can("Repr").then(|| quote! { .with_repr() }); + let with_partial_eq = element.can("PartialEq").then(|| quote! { .with_partial_eq() }); + let with_local_name = element.can("LocalName").then(|| quote! { .with_local_name() }); + let with_scope = scope.then(|| quote! { .with_scope() }); - let scope = if *scope { - quote! { <#ident as #foundations::NativeScope>::scope() } - } else { - quote! { #foundations::Scope::new() } + quote! { + unsafe impl #foundations::NativeElement for #ident { + const ELEM: #foundations::Element = #foundations::Element::from_vtable({ + static STORE: #foundations::LazyElementStore + = #foundations::LazyElementStore::new(); + static VTABLE: #foundations::ContentVtable = + #foundations::ContentVtable::new::<#ident>( + #name, + #title, + #docs, + &[#(#fields),*], + #field_id, + #capable_func, + || &STORE, + ) #with_keywords + #with_repr + #with_partial_eq + #with_local_name + #with_scope + .erase(); + &VTABLE + }); + } + } +} + +/// Creates the appropriate trait implementation for a field. +fn create_field_impl(element: &Elem, field: &Field) -> TokenStream { + let elem_ident = &element.ident; + let Field { i, ty, ident, default, positional, name, docs, .. } = field; + + let default = match default { + Some(default) => quote! { || #default }, + None => quote! { std::default::Default::default }, }; - let params = element.doc_fields().map(create_param_info); - - let data = quote! { - #foundations::NativeElementData { - name: #name, - title: #title, - docs: #docs, - keywords: &[#(#keywords),*], - construct: <#ident as #foundations::Construct>::construct, - set: <#ident as #foundations::Set>::set, - vtable: <#ident as #foundations::Capable>::vtable, - field_id: |name| name.parse().ok().map(|id: Fields| id as u8), - field_name: |id| id.try_into().ok().map(Fields::to_str), - field_from_styles: <#ident as #foundations::Fields>::field_from_styles, - local_name: #local_name, - scope: ::std::sync::LazyLock::new(|| #scope), - params: ::std::sync::LazyLock::new(|| ::std::vec![#(#params),*]) + if field.external { + quote! { + impl #foundations::ExternalField<#i> for #elem_ident { + type Type = #ty; + const FIELD: #foundations::ExternalFieldData<Self, #i> = + #foundations::ExternalFieldData::<Self, #i>::new( + #name, + #docs, + #default, + ); + } } - }; - - quote! { - impl #foundations::NativeElement for #ident { - fn data() -> &'static #foundations::NativeElementData { - static DATA: #foundations::NativeElementData = #data; - &DATA + } else if field.required { + quote! { + impl #foundations::RequiredField<#i> for #elem_ident { + type Type = #ty; + const FIELD: #foundations::RequiredFieldData<Self, #i> = + #foundations::RequiredFieldData::<Self, #i>::new( + #name, + #docs, + |elem| &elem.#ident, + ); } } - } -} - -/// Creates a parameter info for a field. -fn create_param_info(field: &Field) -> TokenStream { - let Field { - name, - docs, - positional, - variadic, - required, - default, - ty, - .. - } = field; - - let named = !positional; - let settable = !field.required; - - let default = if settable { - let default = default - .clone() - .unwrap_or_else(|| parse_quote! { ::std::default::Default::default() }); + } else if field.synthesized { quote! { - Some(|| <#ty as #foundations::IntoValue>::into_value(#default)) + impl #foundations::SynthesizedField<#i> for #elem_ident { + type Type = #ty; + const FIELD: #foundations::SynthesizedFieldData<Self, #i> = + #foundations::SynthesizedFieldData::<Self, #i>::new( + #name, + #docs, + |elem| &elem.#ident, + ); + } } } else { - quote! { None } - }; - - let ty = if *variadic { - quote! { <#ty as #foundations::Container>::Inner } - } else { - quote! { #ty } - }; - - quote! { - #foundations::ParamInfo { - name: #name, - docs: #docs, - input: <#ty as #foundations::Reflect>::input(), - default: #default, - positional: #positional, - named: #named, - variadic: #variadic, - required: #required, - settable: #settable, - } - } -} + let slot = quote! { + || { + static LOCK: ::std::sync::OnceLock<#ty> = ::std::sync::OnceLock::new(); + &LOCK + } + }; -/// Creates the element's `PartialEq` implementation. -fn create_partial_eq_impl(element: &Elem) -> TokenStream { - let ident = &element.ident; - let empty = element.eq_fields().next().is_none().then(|| quote! { true }); - let fields = element.eq_fields().map(|field| &field.ident); + let with_fold = field.fold.then(|| quote! { .with_fold() }); + let refable = (!field.fold).then(|| { + quote! { + impl #foundations::RefableProperty<#i> for #elem_ident {} + } + }); - quote! { - impl PartialEq for #ident { - fn eq(&self, other: &Self) -> bool { - #empty - #(self.#fields == other.#fields)&&* + if field.ghost { + quote! { + impl #foundations::SettableProperty<#i> for #elem_ident { + type Type = #ty; + const FIELD: #foundations::SettablePropertyData<Self, #i> = + #foundations::SettablePropertyData::<Self, #i>::new( + #name, + #docs, + #positional, + #default, + #slot, + ) #with_fold; + } + #refable + } + } else { + quote! { + impl #foundations::SettableField<#i> for #elem_ident { + type Type = #ty; + const FIELD: #foundations::SettableFieldData<Self, #i> = + #foundations::SettableFieldData::<Self, #i>::new( + #name, + #docs, + #positional, + |elem| &elem.#ident, + |elem| &mut elem.#ident, + #default, + #slot, + ) #with_fold; + } + #refable } } } @@ -758,10 +559,12 @@ fn create_construct_impl(element: &Elem) -> TokenStream { let fields = element.struct_fields().map(|field| { let ident = &field.ident; - if field.synthesized { + if field.required { + quote! { #ident } + } else if field.synthesized { quote! { #ident: None } } else { - quote! { #ident } + quote! { #ident: #foundations::Settable::from(#ident) } } }); @@ -782,12 +585,12 @@ fn create_construct_impl(element: &Elem) -> TokenStream { fn create_set_impl(element: &Elem) -> TokenStream { let ident = &element.ident; let handlers = element.set_fields().map(|field| { - let set_ident = &field.set_ident; + let field_ident = &field.ident; let (prefix, value) = create_field_parser(field); quote! { #prefix if let Some(value) = #value { - styles.set(Self::#set_ident(value)); + styles.set(Self::#field_ident, value); } } }); @@ -827,7 +630,7 @@ fn create_field_parser(field: &Field) -> (TokenStream, TokenStream) { } /// Creates the element's casting vtable. -fn create_capable_impl(element: &Elem) -> TokenStream { +fn create_capable_func(element: &Elem) -> TokenStream { // Forbidden capabilities (i.e capabilities that are not object safe). const FORBIDDEN: &[&str] = &["Debug", "PartialEq", "Hash", "Construct", "Set", "Repr", "LocalName"]; @@ -851,202 +654,10 @@ fn create_capable_impl(element: &Elem) -> TokenStream { }); quote! { - unsafe impl #foundations::Capable for #ident { - fn vtable(capability: ::std::any::TypeId) -> ::std::option::Option<::std::ptr::NonNull<()>> { - let dangling = ::std::ptr::NonNull::<#foundations::Packed<#ident>>::dangling().as_ptr(); - #(#checks)* - None - } - } - } -} - -/// Creates the element's `Fields` implementation. -fn create_fields_impl(element: &Elem) -> TokenStream { - let into_value = quote! { #foundations::IntoValue::into_value }; - let visible_non_ghost = || element.visible_fields().filter(|field| !field.ghost); - - // Fields that can be checked using the `has` method. - let has_arms = visible_non_ghost().map(|field| { - let Field { enum_ident, ident, .. } = field; - - let expr = if field.required { - quote! { true } - } else { - quote! { self.#ident.is_some() } - }; - - quote! { Fields::#enum_ident => #expr } - }); - - // Fields that can be accessed using the `field` method. - let field_arms = visible_non_ghost().map(|field| { - let Field { enum_ident, ident, .. } = field; - - let expr = if field.required { - quote! { Ok(#into_value(self.#ident.clone())) } - } else { - quote! { self.#ident.clone().map(#into_value).ok_or(#foundations::FieldAccessError::Unset) } - }; - - quote! { Fields::#enum_ident => #expr } - }); - - // Fields that can be accessed using the `field_with_styles` method. - let field_with_styles_arms = element.visible_fields().map(|field| { - let Field { enum_ident, ident, .. } = field; - - let expr = if field.required { - quote! { Ok(#into_value(self.#ident.clone())) } - } else if field.synthesized { - quote! { self.#ident.clone().map(#into_value).ok_or(#foundations::FieldAccessError::Unset) } - } else { - let value = create_style_chain_access( - field, - false, - if field.ghost { quote!(None) } else { quote!(self.#ident.as_ref()) }, - ); - - quote! { Ok(#into_value(#value)) } - }; - - quote! { Fields::#enum_ident => #expr } - }); - - // Fields that can be accessed using the `field_from_styles` method. - let field_from_styles_arms = element.visible_fields().map(|field| { - let Field { enum_ident, .. } = field; - - let expr = if field.required || field.synthesized { - quote! { Err(#foundations::FieldAccessError::Unknown) } - } else { - let value = create_style_chain_access(field, false, quote!(None)); - quote! { Ok(#into_value(#value)) } - }; - - quote! { Fields::#enum_ident => #expr } - }); - - // Sets fields from the style chain. - let materializes = visible_non_ghost() - .filter(|field| !field.required && !field.synthesized) - .map(|field| { - let Field { ident, .. } = field; - let value = create_style_chain_access( - field, - false, - if field.ghost { quote!(None) } else { quote!(self.#ident.as_ref()) }, - ); - - if field.fold { - quote! { self.#ident = Some(#value); } - } else { - quote! { - if self.#ident.is_none() { - self.#ident = Some(#value); - } - } - } - }); - - // Creation of the `fields` dictionary for inherent fields. - let field_inserts = visible_non_ghost().map(|field| { - let Field { ident, name, .. } = field; - let string = quote! { #name.into() }; - - if field.required { - quote! { - fields.insert(#string, #into_value(self.#ident.clone())); - } - } else { - quote! { - if let Some(value) = &self.#ident { - fields.insert(#string, #into_value(value.clone())); - } - } - } - }); - - let Elem { ident, .. } = element; - - let result = quote! { - Result<#foundations::Value, #foundations::FieldAccessError> - }; - - quote! { - impl #foundations::Fields for #ident { - type Enum = Fields; - - fn has(&self, id: u8) -> bool { - let Ok(id) = Fields::try_from(id) else { - return false; - }; - - match id { - #(#has_arms,)* - _ => false, - } - } - - fn field(&self, id: u8) -> #result { - let id = Fields::try_from(id)?; - match id { - #(#field_arms,)* - // This arm might be reached if someone tries to access an - // internal field. - _ => Err(#foundations::FieldAccessError::Unknown), - } - } - - fn field_with_styles(&self, id: u8, styles: #foundations::StyleChain) -> #result { - let id = Fields::try_from(id)?; - match id { - #(#field_with_styles_arms,)* - // This arm might be reached if someone tries to access an - // internal field. - _ => Err(#foundations::FieldAccessError::Unknown), - } - } - - fn field_from_styles(id: u8, styles: #foundations::StyleChain) -> #result { - let id = Fields::try_from(id)?; - match id { - #(#field_from_styles_arms,)* - // This arm might be reached if someone tries to access an - // internal field. - _ => Err(#foundations::FieldAccessError::Unknown), - } - } - - fn materialize(&mut self, styles: #foundations::StyleChain) { - #(#materializes)* - } - - fn fields(&self) -> #foundations::Dict { - let mut fields = #foundations::Dict::new(); - #(#field_inserts)* - fields - } - } - } -} - -/// Creates the element's `Repr` implementation. -fn create_repr_impl(element: &Elem) -> TokenStream { - let ident = &element.ident; - let repr_format = format!("{}{{}}", element.name); - quote! { - impl #foundations::Repr for #ident { - fn repr(&self) -> ::ecow::EcoString { - let fields = #foundations::Fields::fields(self) - .into_iter() - .map(|(name, value)| ::ecow::eco_format!("{}: {}", name, value.repr())) - .collect::<Vec<_>>(); - ::ecow::eco_format!( - #repr_format, - #foundations::repr::pretty_array_like(&fields, false), - ) - } + |capability| { + let dangling = ::std::ptr::NonNull::<#foundations::Packed<#ident>>::dangling().as_ptr(); + #(#checks)* + None } } } @@ -1062,15 +673,3 @@ fn create_mathy_impl(element: &Elem) -> TokenStream { let ident = &element.ident; quote! { impl ::typst_library::math::Mathy for #foundations::Packed<#ident> {} } } - -/// Creates the element's `IntoValue` implementation. -fn create_into_value_impl(element: &Elem) -> TokenStream { - let Elem { ident, .. } = element; - quote! { - impl #foundations::IntoValue for #ident { - fn into_value(self) -> #foundations::Value { - #foundations::Value::Content(#foundations::Content::new(self)) - } - } - } -} |
