Skip to main content

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}