css_ast/traits/feature_metadata.rs
1use css_lexer::Span;
2use css_parse::Comparison;
3
4use crate::{CssAtomSet, traits::declaration_metadata::PropertyGroup};
5
6/// Which conditional rule context a feature belongs to.
7#[derive(Debug, Copy, Clone, PartialEq, Eq)]
8pub enum FeatureType {
9 /// Feature is part of an `@media` query.
10 Media,
11 /// Feature is part of an `@container` query.
12 Container,
13}
14
15/// What kind of value a conditional feature evaluates against.
16#[derive(Debug, Copy, Clone, PartialEq, Eq)]
17pub enum FeatureEvaluator {
18 /// Evaluates against a CSS `<length>` value.
19 Length,
20 /// Evaluates against a CSS `<integer>` value.
21 Integer,
22 /// Evaluates against a CSS `<number>` (float) value.
23 Number,
24 /// Evaluates against a CSS `<ratio>` value.
25 Ratio,
26 /// Evaluates against a CSS `<resolution>` value.
27 Resolution,
28 /// Evaluates against a discrete keyword value.
29 Keyword,
30 /// Evaluates as a boolean (feature present/absent or 0/non-0).
31 Boolean,
32}
33
34/// The form of a ranged feature expression.
35///
36/// Covers all syntactic forms including bare presence check, legacy min/max prefix,
37/// modern colon, and range comparison operators.
38#[derive(Debug, Clone)]
39pub enum RangedForm {
40 /// `(feature)` — bare presence/boolean check of a ranged feature.
41 Bare,
42 /// `(feature: value)` — modern colon form.
43 Exact { value: Span },
44 /// `(min-feature: value)` — legacy min-prefixed colon form.
45 LegacyMin { value: Span },
46 /// `(max-feature: value)` — legacy max-prefixed colon form.
47 LegacyMax { value: Span },
48 /// `(feature op value)` — feature name on the left of the comparison.
49 Left { comparison: Comparison, value: Span },
50 /// `(value op feature)` — feature name on the right of the comparison.
51 Right { value: Span, comparison: Comparison },
52 /// `(v1 op feature op v2)` — range with feature name in the middle.
53 Range { left: Span, left_cmp: Comparison, right_cmp: Comparison, right: Span },
54}
55
56/// Structured data extracted from a parsed conditional feature node.
57///
58/// Built by [`FeatureMetadata::feature_metadata()`]. `Plain` covers both discrete
59/// and boolean features (the distinction is expressed via [`FeatureEvaluator`] in
60/// the static metadata). `Ranged` covers all ranged forms including bare presence.
61#[derive(Debug, Clone)]
62pub enum ConditionalFeature {
63 /// Discrete or boolean feature — bare `(feature)` or colon `(feature: value)`.
64 Plain {
65 /// Canonical atom for the feature name (e.g. `CssAtomSet::Width`).
66 name: CssAtomSet,
67 /// Value span, present when the colon form is used.
68 value: Option<Span>,
69 },
70 /// Ranged feature — any form from bare through full range expression.
71 Ranged {
72 /// Canonical atom for the feature name (e.g. `CssAtomSet::Width`).
73 /// For legacy min/max forms this is still the canonical atom, not the prefixed one.
74 name: CssAtomSet,
75 /// The specific comparison form and associated value span(s).
76 form: RangedForm,
77 },
78}
79
80impl ConditionalFeature {
81 /// Returns the canonical feature-name atom regardless of variant.
82 pub fn name(&self) -> CssAtomSet {
83 match self {
84 Self::Plain { name, .. } | Self::Ranged { name, .. } => *name,
85 }
86 }
87}
88
89pub trait FeatureMetadata: Sized {
90 /// Which conditional rule context this feature belongs to.
91 fn feature_type() -> FeatureType {
92 FeatureType::Media
93 }
94
95 /// What kind of value this feature evaluates against.
96 fn evaluator() -> FeatureEvaluator {
97 FeatureEvaluator::Keyword
98 }
99
100 /// Which CSS specification module this feature belongs to.
101 fn property_group() -> PropertyGroup {
102 PropertyGroup::none()
103 }
104
105 /// Extracts structured instance data from this parsed feature node.
106 fn feature_metadata(&self) -> ConditionalFeature;
107}