style/typed_om/mod.rs
1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5//! Typed OM.
6//!
7//! https://drafts.css-houdini.org/css-typed-om-1/
8
9use crate::derives::*;
10use crate::values::computed::url::ComputedUrl;
11use crate::values::generics::transform::GenericMatrix3D;
12use crate::values::specified::url::SpecifiedUrl;
13use crate::values::CSSFloat;
14use crate::{One, Zero};
15use app_units::Au;
16use servo_arc::Arc;
17use style_traits::CssString;
18use thin_vec::ThinVec;
19
20pub mod numeric;
21pub mod numeric_declaration;
22pub mod numeric_type;
23pub mod sum_value;
24
25pub use numeric_type::NumericType;
26
27/// A single segment of an unparsed Typed OM value.
28///
29/// This corresponds to the `CSSUnparsedSegment` union in the Typed OM
30/// specification. Unparsed values are represented as a list of string
31/// fragments and variable references.
32#[derive(Clone, Debug)]
33#[repr(C)]
34pub enum UnparsedSegment {
35 /// A string fragment.
36 ///
37 /// This corresponds to the string branch of `CSSUnparsedSegment` and is
38 /// used for the non-variable parts of a `CSSUnparsedValue`.
39 String(CssString),
40
41 /// A `var()` reference segment.
42 ///
43 /// This corresponds to `CSSVariableReferenceValue` in the Typed OM
44 /// specification.
45 VariableReference(VariableReferenceValue),
46}
47
48/// An unparsed value used by the Typed OM.
49///
50/// This corresponds to `CSSUnparsedValue` in the Typed OM specification. It
51/// is used for values that cannot be reified into a more specific
52/// property-agnostic representation and therefore need to preserve their
53/// token-like structure as a sequence of string fragments and variable
54/// references.
55///
56/// The underlying list of segments corresponds to the `[[tokens]]` internal
57/// slot of `CSSUnparsedValue`.
58///
59/// This is represented as a type alias over `ThinVec<UnparsedSegment>` rather
60/// than a dedicated struct. This avoids the need for additional wrapper types
61/// when embedding unparsed values within other structures, while still
62/// allowing recursive representations via the segment list.
63pub type UnparsedValue = ThinVec<UnparsedSegment>;
64
65/// A variable reference inside an unparsed Typed OM value.
66///
67/// This corresponds to `CSSVariableReferenceValue` in the Typed OM
68/// specification.
69#[derive(Clone, Debug)]
70#[repr(C)]
71pub struct VariableReferenceValue {
72 /// The referenced custom property name.
73 ///
74 /// This corresponds to the `variable` attribute of
75 /// `CSSVariableReferenceValue`.
76 pub variable: CssString,
77
78 /// The fallback value, if present.
79 ///
80 /// This corresponds to the `fallback` attribute of
81 /// `CSSVariableReferenceValue`. When `has_fallback` is false, this value
82 /// must be ignored. When `has_fallback` is true, this contains the
83 /// fallback tokens (which may be empty).
84 pub fallback: UnparsedValue,
85
86 /// Whether a fallback was explicitly provided.
87 ///
88 /// This is needed to distinguish between the absence of a fallback
89 /// (`var(--a)`) and an explicitly empty fallback (`var(--a,)`), which are
90 /// observable via Typed OM.
91 pub has_fallback: bool,
92}
93
94/// A keyword value used by the Typed OM.
95///
96/// This corresponds to `CSSKeywordValue` in the Typed OM specification.
97/// The keyword is stored as a `CssString` so it can be represented and
98/// transferred independently of any specific property (e.g. `"none"`,
99/// `"block"`, `"thin"`).
100#[derive(Clone, Debug)]
101#[repr(C)]
102pub struct KeywordValue(pub CssString);
103
104/// A single numeric value with an associated unit.
105///
106/// This corresponds to `CSSUnitValue` in the Typed OM specification. The
107/// numeric component is stored separately from the textual unit identifier.
108#[derive(Clone, Debug)]
109#[repr(C)]
110pub struct UnitValue {
111 /// The numeric type associated with this value.
112 pub numeric_type: NumericType,
113
114 /// The numeric component of the value.
115 pub value: f32,
116
117 /// The textual unit string (e.g. `"px"`, `"em"`, `"%"`, `"deg"`).
118 pub unit: CssString,
119}
120
121impl UnitValue {
122 /// Returns the unit as a string slice.
123 #[inline]
124 pub fn unit_str(&self) -> &str {
125 #[cfg(feature = "gecko")]
126 unsafe {
127 self.unit.as_str_unchecked()
128 }
129
130 #[cfg(feature = "servo")]
131 {
132 &self.unit
133 }
134 }
135}
136
137/// A sum of numeric values.
138///
139/// This corresponds to `CSSMathSum` in the Typed OM specification. A sum
140/// value represents an expression such as `10px + 2em`. Each entry is itself
141/// a `NumericValue`, allowing nested sums if needed.
142#[derive(Clone, Debug)]
143#[repr(C)]
144pub struct MathSum {
145 /// The numeric type associated with this sum.
146 pub numeric_type: NumericType,
147
148 /// The list of numeric terms that make up the sum.
149 pub values: ThinVec<NumericValue>,
150}
151
152impl MathSum {
153 /// Creates a math sum from a sequence of numeric values.
154 ///
155 /// Returns an error if the values do not have addable numeric types.
156 pub fn try_from_numeric_values(values: ThinVec<NumericValue>) -> Result<Self, ()> {
157 // Temporarily ignore NumericValue variants that don't expose a
158 // numeric type. This filter can be removed once numeric type support
159 // is implemented for all NumericValue variants.
160 let numeric_type = NumericType::add_types(values.iter().filter_map(|v| v.numeric_type()))?;
161
162 Ok(Self {
163 numeric_type,
164 values,
165 })
166 }
167
168 /// Creates a math sum from a previously validated sequence of numeric
169 /// values.
170 pub fn from_numeric_values_unchecked(values: ThinVec<NumericValue>) -> Self {
171 let result = Self::try_from_numeric_values(values);
172 debug_assert!(result.is_ok(), "Expected addable values");
173
174 result.unwrap_or_else(|_| Self {
175 numeric_type: NumericType::number(),
176 values: ThinVec::from([NumericValue::zero()]),
177 })
178 }
179}
180
181/// A product of numeric values.
182///
183/// This corresponds to `CSSMathProduct` in the Typed OM specification. A
184/// product value represents an expression such as `10px * 2`. Each entry is
185/// itself a `NumericValue`, allowing nested math expressions if needed.
186pub type MathProduct = ThinVec<NumericValue>;
187
188/// A negated numeric value.
189///
190/// This corresponds to `CSSMathNegate` in the Typed OM specification. A negate
191/// expression represents constructs such as `-10px` or `-(10px + 2em)`.
192pub type MathNegate = Box<NumericValue>;
193
194/// An inverted numeric value.
195///
196/// This corresponds to `CSSMathInvert` in the Typed OM specification. An
197/// invert expression represents constructs such as `1 / 2`, `1 / 10px`, or
198/// more generally the reciprocal of another numeric value.
199pub type MathInvert = Box<NumericValue>;
200
201/// A minimum expression over numeric values.
202///
203/// This corresponds to `CSSMathMin` in the Typed OM specification. A minimum
204/// expression represents constructs such as `min(10px, 20%)`. Each entry is
205/// itself a `NumericValue`, allowing nested math expressions if needed.
206pub type MathMin = ThinVec<NumericValue>;
207
208/// A maximum expression over numeric values.
209///
210/// This corresponds to `CSSMathMax` in the Typed OM specification. A maximum
211/// expression represents constructs such as `max(10px, 20%)`. Each entry is
212/// itself a `NumericValue`, allowing nested math expressions if needed.
213pub type MathMax = ThinVec<NumericValue>;
214
215/// A clamp expression over numeric values.
216///
217/// This corresponds to `CSSMathClamp` in the Typed OM specification. A clamp
218/// expression represents constructs such as `clamp(10px, 20%, 30px)`.
219///
220/// The array entries correspond to the lower bound, value, and upper bound,
221/// respectively.
222pub type MathClamp = crate::OwnedArray<NumericValue, 3>;
223
224/// A math expression used by the Typed OM.
225///
226/// This corresponds to `CSSMathValue` and its subclasses in the Typed OM
227/// specification.
228#[derive(Clone, Debug)]
229#[repr(C)]
230pub enum MathValue {
231 /// A sum of numeric values.
232 ///
233 /// This corresponds to `CSSMathSum`.
234 Sum(MathSum),
235
236 /// A product of numeric values.
237 ///
238 /// This corresponds to `CSSMathProduct`.
239 Product(MathProduct),
240
241 /// A negated numeric value.
242 ///
243 /// This corresponds to `CSSMathNegate`.
244 Negate(MathNegate),
245
246 /// An inverted numeric value.
247 ///
248 /// This corresponds to `CSSMathInvert`.
249 Invert(MathInvert),
250
251 /// A minimum expression over numeric values.
252 ///
253 /// This corresponds to `CSSMathMin`.
254 Min(MathMin),
255
256 /// A maximum expression over numeric values.
257 ///
258 /// This corresponds to `CSSMathMax`.
259 Max(MathMax),
260
261 /// A clamp expression over numeric values.
262 ///
263 /// This corresponds to `CSSMathClamp`.
264 Clamp(MathClamp),
265}
266
267impl MathValue {
268 /// Returns the numeric type associated with this math value, if
269 /// available.
270 pub fn numeric_type(&self) -> Option<&NumericType> {
271 match self {
272 Self::Sum(math_sum) => Some(&math_sum.numeric_type),
273 _ => None,
274 }
275 }
276}
277
278/// A numeric value used by the Typed OM.
279///
280/// This corresponds to `CSSNumericValue` and its subclasses in the Typed OM
281/// specification. It represents numbers that can appear in CSS values,
282/// including both simple unit quantities and composite expressions.
283///
284/// Unlike the parser-level representation, `NumericValue` is property-agnostic
285/// and suitable for conversion to or from the `CSSNumericValue` family of DOM
286/// objects.
287#[derive(Clone, Debug)]
288#[repr(C)]
289pub enum NumericValue {
290 /// A single numeric value with a concrete unit.
291 ///
292 /// This corresponds to `CSSUnitValue`.
293 Unit(UnitValue),
294
295 /// A math expression.
296 ///
297 /// This corresponds to `CSSMathValue` and its subclasses.
298 Math(MathValue),
299}
300
301impl NumericValue {
302 /// Returns a zero pixel unit value.
303 #[inline]
304 pub fn zero_px() -> Self {
305 Self::Unit(UnitValue {
306 numeric_type: NumericType::length(),
307 value: 0.0,
308 unit: CssString::from("px"),
309 })
310 }
311
312 /// Returns the numeric type associated with this numeric value, if
313 /// available.
314 pub fn numeric_type(&self) -> Option<&NumericType> {
315 match self {
316 Self::Unit(unit_value) => Some(&unit_value.numeric_type),
317 Self::Math(math_value) => math_value.numeric_type(),
318 }
319 }
320}
321
322impl Zero for NumericValue {
323 #[inline]
324 fn zero() -> Self {
325 Self::Unit(UnitValue {
326 numeric_type: NumericType::number(),
327 value: 0.0,
328 unit: CssString::from("number"),
329 })
330 }
331
332 #[inline]
333 fn is_zero(&self) -> bool {
334 match *self {
335 Self::Unit(ref value) => value.value == 0.0,
336 _ => false,
337 }
338 }
339}
340
341impl One for NumericValue {
342 #[inline]
343 fn one() -> Self {
344 Self::Unit(UnitValue {
345 numeric_type: NumericType::number(),
346 value: 1.0,
347 unit: CssString::from("number"),
348 })
349 }
350
351 #[inline]
352 fn is_one(&self) -> bool {
353 match *self {
354 Self::Unit(ref value) => value.value == 1.0,
355 _ => false,
356 }
357 }
358}
359
360/// A translate transform component used by the Typed OM.
361///
362/// This corresponds to `CSSTranslate` in the Typed OM specification. The `x`,
363/// `y`, and `z` components are always present; omitted offsets are represented
364/// as `0px`.
365///
366/// The `is_2d` flag indicates whether the component was reified from a 2D
367/// translate function.
368#[derive(Clone, Debug)]
369#[repr(C)]
370pub struct TranslateComponent {
371 /// The x-axis translation component.
372 pub x: NumericValue,
373
374 /// The y-axis translation component.
375 pub y: NumericValue,
376
377 /// The z-axis translation component.
378 pub z: NumericValue,
379
380 /// Whether this translate component is two-dimensional.
381 pub is_2d: bool,
382}
383
384/// A rotate transform component used by the Typed OM.
385///
386/// This corresponds to `CSSRotate` in the Typed OM specification. The `angle`,
387/// `x`, `y`, and `z` components are always present; omitted axis coordinates
388/// are represented using the implicit axis for the corresponding rotate
389/// function.
390///
391/// The `is_2d` flag indicates whether the component was reified from a 2D
392/// rotate function.
393#[derive(Clone, Debug)]
394#[repr(C)]
395pub struct RotateComponent {
396 /// The rotation angle.
397 pub angle: NumericValue,
398
399 /// The x-axis rotation coordinate.
400 pub x: NumericValue,
401
402 /// The y-axis rotation coordinate.
403 pub y: NumericValue,
404
405 /// The z-axis rotation coordinate.
406 pub z: NumericValue,
407
408 /// Whether this rotate component is two-dimensional.
409 pub is_2d: bool,
410}
411
412/// A scale transform component used by the Typed OM.
413///
414/// This corresponds to `CSSScale` in the Typed OM specification. The `x`, `y`,
415/// and `z` components are always present; omitted scale factors are
416/// represented as `1`.
417///
418/// The `is_2d` flag indicates whether the component was reified from a 2D
419/// scale function.
420#[derive(Clone, Debug)]
421#[repr(C)]
422pub struct ScaleComponent {
423 /// The x-axis scale factor.
424 pub x: NumericValue,
425
426 /// The y-axis scale factor.
427 pub y: NumericValue,
428
429 /// The z-axis scale factor.
430 pub z: NumericValue,
431
432 /// Whether this scale component is two-dimensional.
433 pub is_2d: bool,
434}
435
436/// A skew transform component used by the Typed OM.
437///
438/// This corresponds to `CSSSkew` in the Typed OM specification. The `ax` and
439/// `ay` components are always present; omitted angles are represented as
440/// `0deg`.
441///
442/// Skew components are always two-dimensional.
443#[derive(Clone, Debug)]
444#[repr(C)]
445pub struct SkewComponent {
446 /// The x-axis skew angle.
447 pub ax: NumericValue,
448
449 /// The y-axis skew angle.
450 pub ay: NumericValue,
451}
452
453/// A skewX transform component used by the Typed OM.
454///
455/// This corresponds to `CSSSkewX` in the Typed OM specification. The value is
456/// always present; omitted angles are represented as `0deg`.
457///
458/// SkewX components are always two-dimensional.
459pub type SkewXComponent = NumericValue;
460
461/// A skewY transform component used by the Typed OM.
462///
463/// This corresponds to `CSSSkewY` in the Typed OM specification. The value is
464/// always present; omitted angles are represented as `0deg`.
465///
466/// SkewY components are always two-dimensional.
467pub type SkewYComponent = NumericValue;
468
469/// A perspective value used by a perspective component.
470///
471/// This corresponds to the `CSSPerspectiveValue` union in the Typed OM
472/// specification.
473#[derive(Clone, Debug)]
474#[repr(C)]
475pub enum PerspectiveValue {
476 /// A numeric perspective value.
477 ///
478 /// This corresponds to `CSSNumericValue`.
479 Numeric(NumericValue),
480
481 /// A keyword perspective value.
482 ///
483 /// This corresponds to `CSSKeywordValue`.
484 Keyword(KeywordValue),
485}
486
487/// A perspective transform component used by the Typed OM.
488///
489/// This corresponds to `CSSPerspective` in the Typed OM specification. The
490/// `length` component is always present.
491///
492/// Perspective components are always three-dimensional.
493#[derive(Clone, Debug)]
494#[repr(C)]
495pub struct PerspectiveComponent {
496 /// The perspective length.
497 pub length: PerspectiveValue,
498}
499
500/// A matrix transform component used by the Typed OM.
501///
502/// This corresponds to `CSSMatrixComponent` in the Typed OM specification.
503///
504/// The `matrix` field always stores a full 4×4 matrix. Two-dimensional
505/// matrices are expanded to their equivalent 3D representation during
506/// reification.
507///
508/// The `is_2d` flag indicates whether the component was reified from a 2D
509/// matrix function.
510#[derive(Clone, Debug)]
511#[repr(C)]
512pub struct MatrixComponent {
513 /// The 4×4 matrix.
514 pub matrix: GenericMatrix3D<CSSFloat>,
515
516 /// Whether this matrix component is two-dimensional.
517 pub is_2d: bool,
518}
519
520/// A single transform component used by the Typed OM.
521///
522/// This corresponds to `CSSTransformComponent` in the Typed OM specification.
523/// Each variant represents one concrete transform component subclass.
524#[derive(Clone, Debug)]
525#[repr(C)]
526pub enum TransformComponent {
527 /// A translate transform component.
528 ///
529 /// This corresponds to `CSSTranslate`.
530 Translate(TranslateComponent),
531
532 /// A rotate transform component.
533 ///
534 /// This corresponds to `CSSRotate`.
535 Rotate(RotateComponent),
536
537 /// A scale transform component.
538 ///
539 /// This corresponds to `CSSScale`.
540 Scale(ScaleComponent),
541
542 /// A skew transform component.
543 ///
544 /// This corresponds to `CSSSkew`.
545 Skew(SkewComponent),
546
547 /// A skewX transform component.
548 ///
549 /// This corresponds to `CSSSkewX`.
550 SkewX(SkewXComponent),
551
552 /// A skewY transform component.
553 ///
554 /// This corresponds to `CSSSkewY`.
555 SkewY(SkewYComponent),
556
557 /// A perspective transform component.
558 ///
559 /// This corresponds to `CSSPerspective`.
560 Perspective(PerspectiveComponent),
561
562 /// A matrix transform component.
563 ///
564 /// This corresponds to `CSSMatrixComponent`.
565 Matrix(MatrixComponent),
566}
567
568/// A transform value used by the Typed OM.
569///
570/// This corresponds to `CSSTransformValue` in the Typed OM specification. It
571/// represents a `<transform-list>` as an ordered list of transform components.
572pub type TransformValue = ThinVec<TransformComponent>;
573
574/// An image value used by the Typed OM.
575///
576/// This corresponds to `CSSImageValue` in the Typed OM specification.
577///
578/// `CSSImageValue` objects represent values for properties that take
579/// `<image>` values.
580#[derive(Clone, Debug, ToCss)]
581#[repr(C)]
582pub enum ImageValue {
583 /// A specified image URL value.
584 ///
585 /// Relative URLs are preserved and continue to resolve against the
586 /// originating stylesheet or document when later used.
587 Specified(SpecifiedUrl),
588
589 /// A computed image URL value.
590 ///
591 /// Computed URLs are already resolved according to normal CSS computed
592 /// value processing.
593 Computed(ComputedUrl),
594}
595
596/// A property-agnostic representation of a value, used by Typed OM.
597///
598/// `TypedValue` is the internal counterpart of the various `CSSStyleValue`
599/// subclasses defined by the Typed OM specification. It captures values that
600/// can be represented independently of any particular property.
601#[derive(Clone, Debug)]
602#[repr(C)]
603pub enum TypedValue {
604 /// An unparsed value consisting of string fragments and variable
605 /// references.
606 ///
607 /// This corresponds to `CSSUnparsedValue` in the Typed OM specification.
608 Unparsed(UnparsedValue),
609
610 /// A keyword value such as `"block"`, `"none"`, or `"thin"`.
611 ///
612 /// This corresponds to `CSSKeywordValue` in the Typed OM specification.
613 /// Keywords are represented as a standalone `KeywordValue` so they can
614 /// be carried and compared independently of any particular property.
615 Keyword(KeywordValue),
616
617 /// A numeric value such as a length, angle, time, or a sum thereof.
618 ///
619 /// This corresponds to the `CSSNumericValue` hierarchy in the Typed OM
620 /// specification, including `CSSUnitValue` and `CSSMathSum`.
621 Numeric(NumericValue),
622
623 /// A transform value such as `translate(10px, 20px)`.
624 ///
625 /// This corresponds to `CSSTransformValue` in the Typed OM specification.
626 Transform(TransformValue),
627
628 /// An image value.
629 ///
630 /// This corresponds to `CSSImageValue` in the Typed OM specification.
631 Image(ImageValue),
632}
633
634/// A list of property-agnostic values used by the Typed OM.
635///
636/// `TypedValueList` is the internal counterpart of CSS value lists exposed by
637/// Typed OM. It stores one or more [`TypedValue`] items in source order and
638/// is used when a value reifies to multiple property-agnostic components.
639#[derive(Clone, Debug)]
640#[repr(C)]
641pub struct TypedValueList {
642 /// The list of reified values.
643 pub values: ThinVec<TypedValue>,
644}
645
646/// Reifies a value into its Typed OM representation.
647///
648/// This trait is the Typed OM analogue of [`ToCss`]. Instead of serializing
649/// values into CSS syntax, it converts them into [`TypedValue`]s that can be
650/// exposed to the DOM as `CSSStyleValue` subclasses.
651///
652/// Most consumers should use [`ToTyped::to_typed_value`] or
653/// [`ToTyped::to_typed_value_list`], depending on whether they need a single
654/// reified value or the full list of reified values.
655///
656/// This trait is derivable with `#[derive(ToTyped)]`. The derived
657/// implementation currently supports:
658///
659/// * Keyword enums: Enums whose variants are all unit variants are
660/// automatically reified as [`TypedValue::Keyword`], using the same
661/// serialization logic as [`ToCss`].
662///
663/// * Bitflags structs: Structs annotated with
664/// `#[css(bitflags(single = "...", mixed = "...", overlapping_bits))]`
665/// are automatically reified as [`TypedValue::Keyword`] values when they
666/// can be represented as a single CSS keyword. Values that would serialize
667/// to multiple CSS keywords are treated as unsupported.
668///
669/// * Structs and data-carrying variants: Unless treated specially (such as
670/// bitflags structs), the derive attempts to call `.to_typed()` recursively
671/// on supported fields or variant payloads, producing [`TypedValue`]s when
672/// possible.
673///
674/// * Other cases: If no automatic mapping is defined, or recursion is
675/// explicitly disabled, the derived implementation falls back to the
676/// default method (which returns `Err(())`, and thus `to_typed_value()`
677/// returns `None`).
678///
679/// Over time, the derive may be extended to handle additional CSS value
680/// categories such as numeric, color, and transform types.
681///
682/// Summary of derive attributes recognized by `#[derive(ToTyped)]`:
683///
684/// * `#[css(bitflags(single = "...", mixed = "...", overlapping_bits))]` on a
685/// struct generates keyword reification for CSS bitflags types. Values that
686/// can be represented as a single CSS keyword are reified as
687/// [`TypedValue::Keyword`]; values that would serialize to multiple CSS
688/// keywords are treated as unsupported and return `Err(())`.
689///
690/// `overlapping_bits` is supported for bitflags where one keyword subsumes
691/// other internal bits, such as `contain: size`.
692///
693/// * `#[typed(skip_derive_fields)]` on the type disables recursion for
694/// structs and data-carrying enum variants.
695///
696/// * `#[css(skip)]`, `#[typed(skip)]`, or `#[typed(todo)]` on a variant cause
697/// that variant to be treated as unsupported (the derived implementation
698/// returns `Err(())`).
699///
700/// * `#[css(skip)]` on a field causes that field to be ignored during
701/// reification.
702///
703/// * `#[css(skip_if = "...")]` / `#[typed(skip_if = "...")]` on a field
704/// conditionally disables reification for that field. If the provided
705/// function returns `true` for the field value, the field is ignored.
706///
707/// * `#[css(contextual_skip_if = "...")]` /
708/// `#[typed(contextual_skip_if = "...")]` on a field conditionally disables
709/// reification for that field. The provided function is called with all
710/// fields in the current struct or variant. If it returns `true`, the field
711/// is ignored.
712///
713/// Typed skip annotations override CSS skip annotations when both are
714/// present.
715///
716/// * `#[css(keyword = "...")]` on a unit variant overrides the keyword that
717/// would otherwise be derived from the Rust identifier.
718///
719/// * `#[css(comma)]` on the variant indicates that supported fields may reify
720/// to multiple separate values. When this attribute is present, multiple
721/// [`TypedValue`] items may be produced, unless
722/// `#[typed(no_multiple_values)]` is also present. If multiple values are
723/// not allowed and the derived implementation would produce more than one
724/// item, it returns `Err(())`.
725///
726/// * `#[typed(no_multiple_values)]` on a variant prevents it from reifying to
727/// multiple [`TypedValue`] items, even if `#[css(comma)]` is present.
728///
729/// * `#[css(iterable)]` on a field indicates that the field represents a list
730/// of values. Each item in the iterable is reified individually by calling
731/// `ToTyped::to_typed` on the element type.
732///
733/// * `#[css(if_empty = "...")]` on an iterable field specifies a keyword
734/// value that should be produced when the iterable is empty.
735///
736/// * `#[css(represents_keyword)]` on a bool field causes the field name to be
737/// reified as a keyword when the field is true.
738pub trait ToTyped {
739 /// Attempt to convert `self` into one or more [`TypedValue`] items.
740 ///
741 /// Implementations append any resulting values to `dest`. This is the
742 /// low-level entry point used by the Typed OM reification infrastructure.
743 /// Most callers should prefer [`ToTyped::to_typed_value`] or
744 /// [`ToTyped::to_typed_value_list`].
745 ///
746 /// Returning `Err(())` indicates that the value cannot be represented as
747 /// a property-agnostic Typed OM value.
748 fn to_typed(&self, _dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {
749 Err(())
750 }
751
752 /// Attempt to convert `self` into a [`TypedValue`].
753 ///
754 /// Returns the first reified value as `Some(TypedValue)` if the value can
755 /// be reified into a property-agnostic CSSStyleValue subclass. Returns
756 /// `None` if the value is unrepresentable, in which case consumers
757 /// produce a property-tied CSSStyleValue instead.
758 fn to_typed_value(&self) -> Option<TypedValue> {
759 let mut dest = ThinVec::new();
760 self.to_typed(&mut dest).ok()?;
761 dest.into_iter().next()
762 }
763
764 /// Attempt to convert `self` into a [`NumericValue`].
765 ///
766 /// Returns `Some(NumericValue)` if the value reifies to a single
767 /// `TypedValue::Numeric` item. Returns `None` otherwise.
768 fn to_numeric_value(&self) -> Option<NumericValue> {
769 match self.to_typed_value()? {
770 TypedValue::Numeric(value) => Some(value),
771 _ => None,
772 }
773 }
774
775 /// Attempt to convert `self` into a [`TypedValueList`].
776 ///
777 /// Returns `Some(TypedValueList)` if the value can be reified into one or
778 /// more property-agnostic Typed OM values. Returns `None` if the value is
779 /// unrepresentable, in which case consumers produce a property-tied
780 /// `CSSStyleValue` instead.
781 fn to_typed_value_list(&self) -> Option<TypedValueList> {
782 let mut dest = ThinVec::new();
783 self.to_typed(&mut dest).ok()?;
784 Some(TypedValueList { values: dest })
785 }
786}
787
788impl<'a, T> ToTyped for &'a T
789where
790 T: ToTyped + ?Sized,
791{
792 fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {
793 (*self).to_typed(dest)
794 }
795}
796
797impl<T> ToTyped for Box<T>
798where
799 T: ?Sized + ToTyped,
800{
801 fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {
802 (**self).to_typed(dest)
803 }
804}
805
806impl<T> ToTyped for Arc<T>
807where
808 T: ?Sized + ToTyped,
809{
810 fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {
811 (**self).to_typed(dest)
812 }
813}
814
815impl ToTyped for Au {
816 fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {
817 let numeric_type = NumericType::length();
818 let value = self.to_f32_px();
819 let unit = CssString::from("px");
820 dest.push(TypedValue::Numeric(NumericValue::Unit(UnitValue {
821 numeric_type,
822 value,
823 unit,
824 })));
825 Ok(())
826 }
827}
828
829macro_rules! impl_to_typed_for_predefined_type {
830 ($name: ty) => {
831 impl<'a> ToTyped for $name {
832 fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {
833 dest.push(TypedValue::Numeric(NumericValue::Unit(UnitValue {
834 numeric_type: NumericType::number(),
835 value: *self as f32,
836 unit: CssString::from("number"),
837 })));
838 Ok(())
839 }
840 }
841 };
842}
843
844impl_to_typed_for_predefined_type!(f32);
845impl_to_typed_for_predefined_type!(i8);
846impl_to_typed_for_predefined_type!(i32);