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#[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 pub const fn new() -> Self {
24 Self(PhantomData)
25 }
26
27 pub const fn index(self) -> u8 {
29 I
30 }
31
32 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
52pub trait RequiredField<const I: u8>: NativeElement {
54 type Type: Clone;
55
56 const FIELD: RequiredFieldData<Self, I>;
57}
58
59pub 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 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 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 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
132pub trait SynthesizedField<const I: u8>: NativeElement {
135 type Type: Clone;
136
137 const FIELD: SynthesizedFieldData<Self, I>;
138}
139
140pub 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 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 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 eq: |_, _| true,
185 }
186 }
187}
188
189pub trait ExternalField<const I: u8>: NativeElement {
191 type Type;
192
193 const FIELD: ExternalFieldData<Self, I>;
194}
195
196pub 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 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 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
242pub trait SettableField<const I: u8>: NativeElement {
245 type Type: Clone;
246
247 const FIELD: SettableFieldData<Self, I>;
248}
249
250pub 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 #[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 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 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
324pub 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 fn default() -> Self::Type {
338 if std::mem::needs_drop::<Self::Type>() {
341 Self::default_ref().clone()
342 } else {
343 (Self::FIELD.default)()
344 }
345 }
346
347 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
363pub 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 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 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 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
435pub trait RefableProperty<const I: u8>: SettableProperty<I> {}
438
439#[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 pub fn new() -> Self {
455 Self(None)
456 }
457
458 pub fn set(&mut self, value: E::Type) {
460 self.0 = Some(value);
461 }
462
463 pub fn unset(&mut self) {
465 self.0 = None;
466 }
467
468 pub fn as_option(&self) -> &Option<E::Type> {
471 &self.0
472 }
473
474 pub fn as_option_mut(&mut self) -> &mut Option<E::Type> {
476 &mut self.0
477 }
478
479 pub fn is_set(&self) -> bool {
481 self.0.is_some()
482 }
483
484 pub fn get(&self, styles: StyleChain) -> E::Type
487 where
488 E::Type: Copy,
489 {
490 self.get_cloned(styles)
491 }
492
493 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 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 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 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#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
571pub enum FieldAccessError {
572 Unknown,
573 Unset,
574}
575
576impl FieldAccessError {
577 #[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 #[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}