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#[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 pub const fn new() -> Self {
23 Self(PhantomData)
24 }
25
26 pub const fn index(self) -> u8 {
28 I
29 }
30
31 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
51pub trait RequiredField<const I: u8>: NativeElement {
53 type Type: Clone;
54
55 const FIELD: RequiredFieldData<Self, I>;
56}
57
58pub 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 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 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 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
127pub trait SynthesizedField<const I: u8>: NativeElement {
130 type Type: Clone;
131
132 const FIELD: SynthesizedFieldData<Self, I>;
133}
134
135pub 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 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 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 eq: |_, _| true,
177 }
178 }
179}
180
181pub trait ExternalField<const I: u8>: NativeElement {
183 type Type;
184
185 const FIELD: ExternalFieldData<Self, I>;
186}
187
188pub 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 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 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
231pub trait SettableField<const I: u8>: NativeElement {
234 type Type: Clone;
235
236 const FIELD: SettableFieldData<Self, I>;
237}
238
239pub 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 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 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 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
308pub 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 fn default() -> Self::Type {
322 if std::mem::needs_drop::<Self::Type>() {
325 Self::default_ref().clone()
326 } else {
327 (Self::FIELD.default)()
328 }
329 }
330
331 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
347pub 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 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 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 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
408pub trait RefableProperty<const I: u8>: SettableProperty<I> {}
411
412#[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 pub fn new() -> Self {
428 Self(None)
429 }
430
431 pub fn set(&mut self, value: E::Type) {
433 self.0 = Some(value);
434 }
435
436 pub fn unset(&mut self) {
438 self.0 = None;
439 }
440
441 pub fn as_option(&self) -> &Option<E::Type> {
444 &self.0
445 }
446
447 pub fn as_option_mut(&mut self) -> &mut Option<E::Type> {
449 &mut self.0
450 }
451
452 pub fn is_set(&self) -> bool {
454 self.0.is_some()
455 }
456
457 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 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 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 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#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
534pub enum FieldAccessError {
535 Unknown,
536 Unset,
537}
538
539impl FieldAccessError {
540 #[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 #[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}