Skip to main content

typst_library/foundations/content/
field.rs

1use std::fmt::{self, Debug};
2use std::hash::Hash;
3use std::marker::PhantomData;
4use std::sync::OnceLock;
5
6use ecow::{EcoString, eco_format};
7use typst_utils::DefSite;
8
9use crate::foundations::{
10    Container, Content, FieldVtable, Fold, FoldFn, IntoValue, NativeElement, Packed,
11    Property, Reflect, Repr, Resolve, StyleChain, Styles,
12};
13
14/// An accessor for the `I`-th field of the element `E`. Values of this type are
15/// generated for each field of an element can be used to interact with this
16/// field programmatically, for example to access the style chain, as in
17/// `styles.get(TextElem::size)`.
18#[derive(Copy, Clone)]
19pub struct Field<E: NativeElement, const I: u8>(pub PhantomData<E>);
20
21impl<E: NativeElement, const I: u8> Field<E, I> {
22    /// Creates a new zero-sized accessor.
23    pub const fn new() -> Self {
24        Self(PhantomData)
25    }
26
27    /// The index of the projected field.
28    pub const fn index(self) -> u8 {
29        I
30    }
31
32    /// Creates a dynamic property instance for this field.
33    ///
34    /// Prefer [`Content::set`] or
35    /// [`Styles::set`](crate::foundations::Styles::set) when working with
36    /// existing content or style value.
37    pub fn set(self, value: E::Type) -> Property
38    where
39        E: SettableProperty<I>,
40        E::Type: Debug + Clone + Hash + Send + Sync + 'static,
41    {
42        Property::new(self, value)
43    }
44}
45
46impl<E: NativeElement, const I: u8> Default for Field<E, I> {
47    fn default() -> Self {
48        Self::new()
49    }
50}
51
52/// A field that is present on every instance of the element.
53pub trait RequiredField<const I: u8>: NativeElement {
54    type Type: Clone;
55
56    const FIELD: RequiredFieldData<Self, I>;
57}
58
59/// Metadata and routines for a [`RequiredField`].
60pub struct RequiredFieldData<E: RequiredField<I>, const I: u8> {
61    name: &'static str,
62    docs: &'static str,
63    def_site: DefSite,
64    get: fn(&E) -> &E::Type,
65}
66
67impl<E: RequiredField<I>, const I: u8> RequiredFieldData<E, I> {
68    /// Creates the data from its parts. This is called in the `#[elem]` macro.
69    pub const fn new(
70        name: &'static str,
71        docs: &'static str,
72        def_site: DefSite,
73        get: fn(&E) -> &E::Type,
74    ) -> Self {
75        Self { name, docs, def_site, get }
76    }
77
78    /// Creates the vtable for a `#[required]` field.
79    pub const fn vtable() -> FieldVtable<Packed<E>>
80    where
81        E: RequiredField<I>,
82        E::Type: Reflect + IntoValue + PartialEq,
83    {
84        FieldVtable {
85            name: E::FIELD.name,
86            docs: E::FIELD.docs,
87            def_site: E::FIELD.def_site,
88            positional: true,
89            required: true,
90            variadic: false,
91            settable: false,
92            synthesized: false,
93            input: || <E::Type as Reflect>::input(),
94            default: None,
95            has: |_| true,
96            get: |elem| Some((E::FIELD.get)(elem).clone().into_value()),
97            get_with_styles: |elem, _| Some((E::FIELD.get)(elem).clone().into_value()),
98            get_from_styles: |_| None,
99            materialize: |_, _| {},
100            eq: |a, b| (E::FIELD.get)(a) == (E::FIELD.get)(b),
101        }
102    }
103
104    /// Creates the vtable for a `#[variadic]` field.
105    pub const fn vtable_variadic() -> FieldVtable<Packed<E>>
106    where
107        E: RequiredField<I>,
108        E::Type: Container + IntoValue + PartialEq,
109        <E::Type as Container>::Inner: Reflect,
110    {
111        FieldVtable {
112            name: E::FIELD.name,
113            docs: E::FIELD.docs,
114            def_site: E::FIELD.def_site,
115            positional: true,
116            required: true,
117            variadic: true,
118            settable: false,
119            synthesized: false,
120            input: || <<E::Type as Container>::Inner as Reflect>::input(),
121            default: None,
122            has: |_| true,
123            get: |elem| Some((E::FIELD.get)(elem).clone().into_value()),
124            get_with_styles: |elem, _| Some((E::FIELD.get)(elem).clone().into_value()),
125            get_from_styles: |_| None,
126            materialize: |_, _| {},
127            eq: |a, b| (E::FIELD.get)(a) == (E::FIELD.get)(b),
128        }
129    }
130}
131
132/// A field that is initially unset, but may be set through a
133/// [`Synthesize`](crate::foundations::Synthesize) implementation.
134pub trait SynthesizedField<const I: u8>: NativeElement {
135    type Type: Clone;
136
137    const FIELD: SynthesizedFieldData<Self, I>;
138}
139
140/// Metadata and routines for a [`SynthesizedField`].
141pub struct SynthesizedFieldData<E: SynthesizedField<I>, const I: u8> {
142    name: &'static str,
143    docs: &'static str,
144    def_site: DefSite,
145    get: fn(&E) -> &Option<E::Type>,
146}
147
148impl<E: SynthesizedField<I>, const I: u8> SynthesizedFieldData<E, I> {
149    /// Creates the data from its parts. This is called in the `#[elem]` macro.
150    pub const fn new(
151        name: &'static str,
152        docs: &'static str,
153        def_site: DefSite,
154        get: fn(&E) -> &Option<E::Type>,
155    ) -> Self {
156        Self { name, docs, def_site, get }
157    }
158
159    /// Creates type-erased metadata and routines for a `#[synthesized]` field.
160    pub const fn vtable() -> FieldVtable<Packed<E>>
161    where
162        E: SynthesizedField<I>,
163        E::Type: Reflect + IntoValue + PartialEq,
164    {
165        FieldVtable {
166            name: E::FIELD.name,
167            docs: E::FIELD.docs,
168            def_site: E::FIELD.def_site,
169            positional: false,
170            required: false,
171            variadic: false,
172            settable: false,
173            synthesized: true,
174            input: || <E::Type as Reflect>::input(),
175            default: None,
176            has: |elem| (E::FIELD.get)(elem).is_some(),
177            get: |elem| (E::FIELD.get)(elem).clone().map(|v| v.into_value()),
178            get_with_styles: |elem, _| {
179                (E::FIELD.get)(elem).clone().map(|v| v.into_value())
180            },
181            get_from_styles: |_| None,
182            materialize: |_, _| {},
183            // Synthesized fields don't affect equality.
184            eq: |_, _| true,
185        }
186    }
187}
188
189/// A field that is not actually there. It's only visible in the docs.
190pub trait ExternalField<const I: u8>: NativeElement {
191    type Type;
192
193    const FIELD: ExternalFieldData<Self, I>;
194}
195
196/// Metadata for an [`ExternalField`].
197pub struct ExternalFieldData<E: ExternalField<I>, const I: u8> {
198    name: &'static str,
199    docs: &'static str,
200    def_site: DefSite,
201    default: fn() -> E::Type,
202}
203
204impl<E: ExternalField<I>, const I: u8> ExternalFieldData<E, I> {
205    /// Creates the data from its parts. This is called in the `#[elem]` macro.
206    pub const fn new(
207        name: &'static str,
208        docs: &'static str,
209        def_site: DefSite,
210        default: fn() -> E::Type,
211    ) -> Self {
212        Self { name, docs, def_site, default }
213    }
214
215    /// Creates type-erased metadata and routines for an `#[external]` field.
216    pub const fn vtable() -> FieldVtable<Packed<E>>
217    where
218        E: ExternalField<I>,
219        E::Type: Reflect + IntoValue,
220    {
221        FieldVtable {
222            name: E::FIELD.name,
223            docs: E::FIELD.docs,
224            def_site: E::FIELD.def_site,
225            positional: false,
226            required: false,
227            variadic: false,
228            settable: false,
229            synthesized: false,
230            input: || <E::Type as Reflect>::input(),
231            default: Some(|| (E::FIELD.default)().into_value()),
232            has: |_| false,
233            get: |_| None,
234            get_with_styles: |_, _| None,
235            get_from_styles: |_| None,
236            materialize: |_, _| {},
237            eq: |_, _| true,
238        }
239    }
240}
241
242/// A field that has a default value and can be configured via a set rule, but
243/// can also present on elements and be present in the constructor.
244pub trait SettableField<const I: u8>: NativeElement {
245    type Type: Clone;
246
247    const FIELD: SettableFieldData<Self, I>;
248}
249
250/// Metadata and routines for a [`SettableField`].
251pub struct SettableFieldData<E: SettableField<I>, const I: u8> {
252    get: fn(&E) -> &Settable<E, I>,
253    get_mut: fn(&mut E) -> &mut Settable<E, I>,
254    property: SettablePropertyData<E, I>,
255}
256
257impl<E: SettableField<I>, const I: u8> SettableFieldData<E, I> {
258    /// Creates the data from its parts. This is called in the `#[elem]` macro.
259    #[expect(clippy::too_many_arguments)]
260    pub const fn new(
261        name: &'static str,
262        docs: &'static str,
263        def_site: DefSite,
264        positional: bool,
265        get: fn(&E) -> &Settable<E, I>,
266        get_mut: fn(&mut E) -> &mut Settable<E, I>,
267        default: fn() -> E::Type,
268        slot: fn() -> &'static OnceLock<E::Type>,
269    ) -> Self {
270        Self {
271            get,
272            get_mut,
273            property: SettablePropertyData::new(
274                name, docs, def_site, positional, default, slot,
275            ),
276        }
277    }
278
279    /// Ensures that the property is folded on every access. See the
280    /// documentation of the [`Fold`] trait for more details.
281    pub const fn with_fold(mut self) -> Self
282    where
283        E::Type: Fold,
284    {
285        self.property.fold = Some(E::Type::fold);
286        self
287    }
288
289    /// Creates type-erased metadata and routines for a normal settable field.
290    pub const fn vtable() -> FieldVtable<Packed<E>>
291    where
292        E: SettableField<I>,
293        E::Type: Reflect + IntoValue + PartialEq,
294    {
295        FieldVtable {
296            name: E::FIELD.property.name,
297            docs: E::FIELD.property.docs,
298            def_site: E::FIELD.property.def_site,
299            positional: E::FIELD.property.positional,
300            required: false,
301            variadic: false,
302            settable: true,
303            synthesized: false,
304            input: || <E::Type as Reflect>::input(),
305            default: Some(|| E::default().into_value()),
306            has: |elem| (E::FIELD.get)(elem).is_set(),
307            get: |elem| (E::FIELD.get)(elem).as_option().clone().map(|v| v.into_value()),
308            get_with_styles: |elem, styles| {
309                Some((E::FIELD.get)(elem).get_cloned(styles).into_value())
310            },
311            get_from_styles: |styles| {
312                Some(styles.get_cloned::<E, I>(Field::new()).into_value())
313            },
314            materialize: |elem, styles| {
315                if !(E::FIELD.get)(elem).is_set() {
316                    (E::FIELD.get_mut)(elem).set(styles.get_cloned::<E, I>(Field::new()));
317                }
318            },
319            eq: |a, b| (E::FIELD.get)(a).as_option() == (E::FIELD.get)(b).as_option(),
320        }
321    }
322}
323
324/// A field that has a default value and can be configured via a set rule, but
325/// is never present on elements.
326///
327/// This is provided for all `SettableField` impls through a blanket impl. In
328/// the case of `#[ghost]` fields, which only live in the style chain and not in
329/// elements, it is also implemented manually.
330pub trait SettableProperty<const I: u8>: NativeElement {
331    type Type: Clone;
332
333    const FIELD: SettablePropertyData<Self, I>;
334    const FOLD: Option<FoldFn<Self::Type>> = Self::FIELD.fold;
335
336    /// Produces an instance of the property's default value.
337    fn default() -> Self::Type {
338        // Avoid recreating an expensive instance over and over, but also
339        // avoid unnecessary lazy initialization for cheap types.
340        if std::mem::needs_drop::<Self::Type>() {
341            Self::default_ref().clone()
342        } else {
343            (Self::FIELD.default)()
344        }
345    }
346
347    /// Produces a static reference to this property's default value.
348    fn default_ref() -> &'static Self::Type {
349        (Self::FIELD.slot)().get_or_init(Self::FIELD.default)
350    }
351}
352
353impl<T, const I: u8> SettableProperty<I> for T
354where
355    T: SettableField<I>,
356{
357    type Type = <Self as SettableField<I>>::Type;
358
359    const FIELD: SettablePropertyData<Self, I> =
360        <Self as SettableField<I>>::FIELD.property;
361}
362
363/// Metadata and routines for a [`SettableProperty`].
364pub struct SettablePropertyData<E: SettableProperty<I>, const I: u8> {
365    name: &'static str,
366    docs: &'static str,
367    def_site: DefSite,
368    positional: bool,
369    default: fn() -> E::Type,
370    slot: fn() -> &'static OnceLock<E::Type>,
371    fold: Option<FoldFn<E::Type>>,
372}
373
374impl<E: SettableProperty<I>, const I: u8> SettablePropertyData<E, I> {
375    /// Creates the data from its parts. This is called in the `#[elem]` macro.
376    pub const fn new(
377        name: &'static str,
378        docs: &'static str,
379        def_site: DefSite,
380        positional: bool,
381        default: fn() -> E::Type,
382        slot: fn() -> &'static OnceLock<E::Type>,
383    ) -> Self {
384        Self {
385            name,
386            docs,
387            def_site,
388            positional,
389            default,
390            slot,
391            fold: None,
392        }
393    }
394
395    /// Ensures that the property is folded on every access. See the
396    /// documentation of the [`Fold`] trait for more details.
397    pub const fn with_fold(self) -> Self
398    where
399        E::Type: Fold,
400    {
401        Self { fold: Some(E::Type::fold), ..self }
402    }
403
404    /// Creates type-erased metadata and routines for a `#[ghost]` field.
405    pub const fn vtable() -> FieldVtable<Packed<E>>
406    where
407        E: SettableProperty<I>,
408        E::Type: Reflect + IntoValue + PartialEq,
409    {
410        FieldVtable {
411            name: E::FIELD.name,
412            docs: E::FIELD.docs,
413            def_site: E::FIELD.def_site,
414            positional: E::FIELD.positional,
415            required: false,
416            variadic: false,
417            settable: true,
418            synthesized: false,
419            input: || <E::Type as Reflect>::input(),
420            default: Some(|| E::default().into_value()),
421            has: |_| false,
422            get: |_| None,
423            get_with_styles: |_, styles| {
424                Some(styles.get_cloned::<E, I>(Field::new()).into_value())
425            },
426            get_from_styles: |styles| {
427                Some(styles.get_cloned::<E, I>(Field::new()).into_value())
428            },
429            materialize: |_, _| {},
430            eq: |_, _| true,
431        }
432    }
433}
434
435/// A settable property that can be accessed by reference (because it is not
436/// folded).
437pub trait RefableProperty<const I: u8>: SettableProperty<I> {}
438
439/// A settable field of an element.
440///
441/// The field can be in two states: Unset or present.
442///
443/// See [`StyleChain`] for more details about the available accessor methods.
444#[derive(Copy, Clone, Hash)]
445pub struct Settable<E: NativeElement, const I: u8>(Option<E::Type>)
446where
447    E: SettableProperty<I>;
448
449impl<E: NativeElement, const I: u8> Settable<E, I>
450where
451    E: SettableProperty<I>,
452{
453    /// Creates a new unset instance.
454    pub fn new() -> Self {
455        Self(None)
456    }
457
458    /// Sets the instance to a value.
459    pub fn set(&mut self, value: E::Type) {
460        self.0 = Some(value);
461    }
462
463    /// Clears the value from the instance.
464    pub fn unset(&mut self) {
465        self.0 = None;
466    }
467
468    /// Views the type as an [`Option`] which is `Some` if the type is set
469    /// and `None` if it is unset.
470    pub fn as_option(&self) -> &Option<E::Type> {
471        &self.0
472    }
473
474    /// Views the type as a mutable [`Option`].
475    pub fn as_option_mut(&mut self) -> &mut Option<E::Type> {
476        &mut self.0
477    }
478
479    /// Whether the field is set.
480    pub fn is_set(&self) -> bool {
481        self.0.is_some()
482    }
483
484    /// Retrieves the value given styles. The styles are used if the value is
485    /// unset.
486    pub fn get(&self, styles: StyleChain) -> E::Type
487    where
488        E::Type: Copy,
489    {
490        self.get_cloned(styles)
491    }
492
493    /// Retrieves and clones the value given styles. The styles are used if the
494    /// value is unset or if it needs folding.
495    pub fn get_cloned(&self, styles: StyleChain) -> E::Type {
496        if let Some(fold) = E::FOLD {
497            let mut res = styles.get_cloned::<E, I>(Field::new());
498            if let Some(value) = &self.0 {
499                res = fold(value.clone(), res);
500            }
501            res
502        } else if let Some(value) = &self.0 {
503            value.clone()
504        } else {
505            styles.get_cloned::<E, I>(Field::new())
506        }
507    }
508
509    /// Retrieves a reference to the value given styles. The styles are used if
510    /// the value is unset.
511    pub fn get_ref<'a>(&'a self, styles: StyleChain<'a>) -> &'a E::Type
512    where
513        E: RefableProperty<I>,
514    {
515        if let Some(value) = &self.0 {
516            value
517        } else {
518            styles.get_ref::<E, I>(Field::new())
519        }
520    }
521
522    /// Retrieves the value and then immediately [resolves](Resolve) it.
523    pub fn resolve(&self, styles: StyleChain) -> <E::Type as Resolve>::Output
524    where
525        E::Type: Resolve,
526    {
527        self.get_cloned(styles).resolve(styles)
528    }
529
530    /// Copies the field (if any) into the given styles.
531    pub fn copy_into(&self, styles: &mut Styles)
532    where
533        E::Type: Debug + Hash + Send + Sync + 'static,
534    {
535        if let Some(value) = &self.0 {
536            styles.set(Field::<E, I>::new(), value.clone());
537        }
538    }
539}
540
541impl<E: NativeElement, const I: u8> Debug for Settable<E, I>
542where
543    E: SettableProperty<I>,
544    E::Type: Debug,
545{
546    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
547        self.0.fmt(f)
548    }
549}
550
551impl<E: NativeElement, const I: u8> Default for Settable<E, I>
552where
553    E: SettableProperty<I>,
554{
555    fn default() -> Self {
556        Self(None)
557    }
558}
559
560impl<E: NativeElement, const I: u8> From<Option<E::Type>> for Settable<E, I>
561where
562    E: SettableProperty<I>,
563{
564    fn from(value: Option<E::Type>) -> Self {
565        Self(value)
566    }
567}
568
569/// An error arising when trying to access a field of content.
570#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
571pub enum FieldAccessError {
572    Unknown,
573    Unset,
574}
575
576impl FieldAccessError {
577    /// Formats the error message given the content and the field name.
578    #[cold]
579    pub fn message(self, content: &Content, field: &str) -> EcoString {
580        let elem_name = content.elem().name();
581        match self {
582            FieldAccessError::Unknown => {
583                eco_format!("{elem_name} does not have field {}", field.repr())
584            }
585            FieldAccessError::Unset => {
586                eco_format!(
587                    "field {} in {elem_name} is not known at this point",
588                    field.repr()
589                )
590            }
591        }
592    }
593
594    /// Formats the error message for an `at` calls without a default value.
595    #[cold]
596    pub fn message_no_default(self, content: &Content, field: &str) -> EcoString {
597        let mut msg = self.message(content, field);
598        msg.push_str(" and no default was specified");
599        msg
600    }
601}