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