typst_library/foundations/
element.rs

1use std::any::TypeId;
2use std::cmp::Ordering;
3use std::fmt::{self, Debug};
4use std::hash::Hash;
5use std::ptr::NonNull;
6use std::sync::LazyLock;
7
8use ecow::EcoString;
9use smallvec::SmallVec;
10#[doc(inline)]
11pub use typst_macros::elem;
12use typst_utils::Static;
13
14use crate::diag::SourceResult;
15use crate::engine::Engine;
16use crate::foundations::{
17    cast, Args, Content, Dict, FieldAccessError, Func, ParamInfo, Repr, Scope, Selector,
18    StyleChain, Styles, Value,
19};
20use crate::text::{Lang, Region};
21
22/// A document element.
23#[derive(Copy, Clone, Eq, PartialEq, Hash)]
24pub struct Element(Static<NativeElementData>);
25
26impl Element {
27    /// Get the element for `T`.
28    pub fn of<T: NativeElement>() -> Self {
29        T::elem()
30    }
31
32    /// The element's normal name (e.g. `enum`).
33    pub fn name(self) -> &'static str {
34        self.0.name
35    }
36
37    /// The element's title case name, for use in documentation
38    /// (e.g. `Numbered List`).
39    pub fn title(&self) -> &'static str {
40        self.0.title
41    }
42
43    /// Documentation for the element (as Markdown).
44    pub fn docs(&self) -> &'static str {
45        self.0.docs
46    }
47
48    /// Search keywords for the element.
49    pub fn keywords(&self) -> &'static [&'static str] {
50        self.0.keywords
51    }
52
53    /// Construct an instance of this element.
54    pub fn construct(
55        self,
56        engine: &mut Engine,
57        args: &mut Args,
58    ) -> SourceResult<Content> {
59        (self.0.construct)(engine, args)
60    }
61
62    /// Execute the set rule for the element and return the resulting style map.
63    pub fn set(self, engine: &mut Engine, mut args: Args) -> SourceResult<Styles> {
64        let styles = (self.0.set)(engine, &mut args)?;
65        args.finish()?;
66        Ok(styles)
67    }
68
69    /// Whether the element has the given capability.
70    pub fn can<C>(self) -> bool
71    where
72        C: ?Sized + 'static,
73    {
74        self.can_type_id(TypeId::of::<C>())
75    }
76
77    /// Whether the element has the given capability where the capability is
78    /// given by a `TypeId`.
79    pub fn can_type_id(self, type_id: TypeId) -> bool {
80        (self.0.vtable)(type_id).is_some()
81    }
82
83    /// The VTable for capabilities dispatch.
84    pub fn vtable(self) -> fn(of: TypeId) -> Option<NonNull<()>> {
85        self.0.vtable
86    }
87
88    /// Create a selector for this element.
89    pub fn select(self) -> Selector {
90        Selector::Elem(self, None)
91    }
92
93    /// Create a selector for this element, filtering for those that
94    /// [fields](crate::foundations::Content::field) match the given argument.
95    pub fn where_(self, fields: SmallVec<[(u8, Value); 1]>) -> Selector {
96        Selector::Elem(self, Some(fields))
97    }
98
99    /// The element's associated scope of sub-definition.
100    pub fn scope(&self) -> &'static Scope {
101        &(self.0).0.scope
102    }
103
104    /// Details about the element's fields.
105    pub fn params(&self) -> &'static [ParamInfo] {
106        &(self.0).0.params
107    }
108
109    /// Extract the field ID for the given field name.
110    pub fn field_id(&self, name: &str) -> Option<u8> {
111        if name == "label" {
112            return Some(255);
113        }
114        (self.0.field_id)(name)
115    }
116
117    /// Extract the field name for the given field ID.
118    pub fn field_name(&self, id: u8) -> Option<&'static str> {
119        if id == 255 {
120            return Some("label");
121        }
122        (self.0.field_name)(id)
123    }
124
125    /// Extract the value of the field for the given field ID and style chain.
126    pub fn field_from_styles(
127        &self,
128        id: u8,
129        styles: StyleChain,
130    ) -> Result<Value, FieldAccessError> {
131        (self.0.field_from_styles)(id, styles)
132    }
133
134    /// The element's local name, if any.
135    pub fn local_name(&self, lang: Lang, region: Option<Region>) -> Option<&'static str> {
136        (self.0).0.local_name.map(|f| f(lang, region))
137    }
138}
139
140impl Debug for Element {
141    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
142        write!(f, "Element({})", self.name())
143    }
144}
145
146impl Repr for Element {
147    fn repr(&self) -> EcoString {
148        self.name().into()
149    }
150}
151
152impl Ord for Element {
153    fn cmp(&self, other: &Self) -> Ordering {
154        self.name().cmp(other.name())
155    }
156}
157
158impl PartialOrd for Element {
159    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
160        Some(self.cmp(other))
161    }
162}
163
164cast! {
165    Element,
166    self => Value::Func(self.into()),
167    v: Func => v.element().ok_or("expected element")?,
168}
169
170/// A Typst element that is defined by a native Rust type.
171pub trait NativeElement:
172    Debug
173    + Clone
174    + PartialEq
175    + Hash
176    + Construct
177    + Set
178    + Capable
179    + Fields
180    + Repr
181    + Send
182    + Sync
183    + 'static
184{
185    /// Get the element for the native Rust element.
186    fn elem() -> Element
187    where
188        Self: Sized,
189    {
190        Element::from(Self::data())
191    }
192
193    /// Pack the element into type-erased content.
194    fn pack(self) -> Content
195    where
196        Self: Sized,
197    {
198        Content::new(self)
199    }
200
201    /// Get the element data for the native Rust element.
202    fn data() -> &'static NativeElementData
203    where
204        Self: Sized;
205}
206
207/// Used to cast an element to a trait object for a trait it implements.
208///
209/// # Safety
210/// If the `vtable` function returns `Some(p)`, then `p` must be a valid pointer
211/// to a vtable of `Packed<Self>` w.r.t to the trait `C` where `capability` is
212/// `TypeId::of::<dyn C>()`.
213pub unsafe trait Capable {
214    /// Get the pointer to the vtable for the given capability / trait.
215    fn vtable(capability: TypeId) -> Option<NonNull<()>>;
216}
217
218/// Defines how fields of an element are accessed.
219pub trait Fields {
220    /// An enum with the fields of the element.
221    type Enum
222    where
223        Self: Sized;
224
225    /// Whether the element has the given field set.
226    fn has(&self, id: u8) -> bool;
227
228    /// Get the field with the given field ID.
229    fn field(&self, id: u8) -> Result<Value, FieldAccessError>;
230
231    /// Get the field with the given ID in the presence of styles.
232    fn field_with_styles(
233        &self,
234        id: u8,
235        styles: StyleChain,
236    ) -> Result<Value, FieldAccessError>;
237
238    /// Get the field with the given ID from the styles.
239    fn field_from_styles(id: u8, styles: StyleChain) -> Result<Value, FieldAccessError>
240    where
241        Self: Sized;
242
243    /// Resolve all fields with the styles and save them in-place.
244    fn materialize(&mut self, styles: StyleChain);
245
246    /// Get the fields of the element.
247    fn fields(&self) -> Dict;
248}
249
250/// An element's constructor function.
251pub trait Construct {
252    /// Construct an element from the arguments.
253    ///
254    /// This is passed only the arguments that remain after execution of the
255    /// element's set rule.
256    fn construct(engine: &mut Engine, args: &mut Args) -> SourceResult<Content>
257    where
258        Self: Sized;
259}
260
261/// An element's set rule.
262pub trait Set {
263    /// Parse relevant arguments into style properties for this element.
264    fn set(engine: &mut Engine, args: &mut Args) -> SourceResult<Styles>
265    where
266        Self: Sized;
267}
268
269/// Defines a native element.
270#[derive(Debug)]
271pub struct NativeElementData {
272    /// The element's normal name (e.g. `align`), as exposed to Typst.
273    pub name: &'static str,
274    /// The element's title case name (e.g. `Align`).
275    pub title: &'static str,
276    /// The documentation for this element as a string.
277    pub docs: &'static str,
278    /// A list of alternate search terms for this element.
279    pub keywords: &'static [&'static str],
280    /// The constructor for this element (see [`Construct`]).
281    pub construct: fn(&mut Engine, &mut Args) -> SourceResult<Content>,
282    /// Executes this element's set rule (see [`Set`]).
283    pub set: fn(&mut Engine, &mut Args) -> SourceResult<Styles>,
284    /// Gets the vtable for one of this element's capabilities
285    /// (see [`Capable`]).
286    pub vtable: fn(capability: TypeId) -> Option<NonNull<()>>,
287    /// Gets the numeric index of this field by its name.
288    pub field_id: fn(name: &str) -> Option<u8>,
289    /// Gets the name of a field by its numeric index.
290    pub field_name: fn(u8) -> Option<&'static str>,
291    /// Get the field with the given ID in the presence of styles (see [`Fields`]).
292    pub field_from_styles: fn(u8, StyleChain) -> Result<Value, FieldAccessError>,
293    /// Gets the localized name for this element (see [`LocalName`][crate::text::LocalName]).
294    pub local_name: Option<fn(Lang, Option<Region>) -> &'static str>,
295    pub scope: LazyLock<Scope>,
296    /// A list of parameter information for each field.
297    pub params: LazyLock<Vec<ParamInfo>>,
298}
299
300impl From<&'static NativeElementData> for Element {
301    fn from(data: &'static NativeElementData) -> Self {
302        Self(Static(data))
303    }
304}
305
306cast! {
307    &'static NativeElementData,
308    self => Element::from(self).into_value(),
309}
310
311/// Synthesize fields on an element. This happens before execution of any show
312/// rule.
313pub trait Synthesize {
314    /// Prepare the element for show rule application.
315    fn synthesize(&mut self, engine: &mut Engine, styles: StyleChain)
316        -> SourceResult<()>;
317}
318
319/// Defines a built-in show rule for an element.
320pub trait Show {
321    /// Execute the base recipe for this element.
322    fn show(&self, engine: &mut Engine, styles: StyleChain) -> SourceResult<Content>;
323}
324
325/// Defines built-in show set rules for an element.
326///
327/// This is a bit more powerful than a user-defined show-set because it can
328/// access the element's fields.
329pub trait ShowSet {
330    /// Finalize the fully realized form of the element. Use this for effects
331    /// that should work even in the face of a user-defined show rule.
332    fn show_set(&self, styles: StyleChain) -> Styles;
333}