1use crate::traits::{AppliesTo, BoxPortion, BoxSide, PropertyGroup};
2use bitmask_enum::bitmask;
3use css_lexer::{Span, ToSpan};
4use css_parse::{NodeMetadata, SemanticEq, ToCursors};
5
6#[bitmask(u32)]
7#[bitmask_config(vec_debug)]
8#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
9pub enum AtRuleId {
10 Charset,
11 ColorProfile,
12 Container,
13 CounterStyle,
14 FontFace,
15 FontFeatureValues,
16 FontPaletteValues,
17 Import,
18 Keyframes,
19 Layer,
20 Media,
21 Namespace,
22 Page,
23 Property,
24 Scope,
25 StartingStyle,
26 Supports,
27 Document,
28 WebkitKeyframes,
29 MozDocument,
30}
31
32#[bitmask(u8)]
33#[bitmask_config(vec_debug)]
34#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
35pub enum VendorPrefixes {
36 Moz,
37 WebKit,
38 O,
39 Ms,
40}
41
42#[bitmask(u8)]
43#[bitmask_config(vec_debug)]
44#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
45pub enum DeclarationKind {
46 Unknown,
48 Important,
50 CssWideKeywords,
52 Custom,
54 Computed,
56 Shorthands,
58 Longhands,
60}
61
62#[bitmask(u16)]
63#[bitmask_config(vec_debug)]
64#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
65pub enum RuleKind {
66 Unknown,
68 NestedAtRule,
70 NestedStyleRules,
72}
73
74#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
78#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
79pub struct CssMetadata {
80 pub property_groups: PropertyGroup,
82 pub applies_to: AppliesTo,
84 pub box_sides: BoxSide,
86 pub box_portions: BoxPortion,
88 pub declaration_kinds: DeclarationKind,
90 pub rule_kinds: RuleKind,
92 pub used_at_rules: AtRuleId,
94 pub vendor_prefixes: VendorPrefixes,
96}
97
98impl Default for CssMetadata {
99 fn default() -> Self {
100 Self {
101 property_groups: PropertyGroup::none(),
102 applies_to: AppliesTo::none(),
103 box_sides: BoxSide::none(),
104 box_portions: BoxPortion::none(),
105 declaration_kinds: DeclarationKind::none(),
106 rule_kinds: RuleKind::none(),
107 used_at_rules: AtRuleId::none(),
108 vendor_prefixes: VendorPrefixes::none(),
109 }
110 }
111}
112
113impl CssMetadata {
114 #[inline]
116 pub fn is_empty(&self) -> bool {
117 self.property_groups == PropertyGroup::none()
118 && self.applies_to == AppliesTo::none()
119 && self.box_sides == BoxSide::none()
120 && self.box_portions == BoxPortion::none()
121 && self.declaration_kinds == DeclarationKind::none()
122 && self.rule_kinds == RuleKind::none()
123 && self.used_at_rules == AtRuleId::none()
124 && self.vendor_prefixes == VendorPrefixes::none()
125 }
126
127 #[inline]
129 pub fn modifies_box(&self) -> bool {
130 !self.box_portions.is_none()
131 }
132
133 #[inline]
135 pub fn has_colors(&self) -> bool {
136 self.property_groups.intersects(PropertyGroup::Color | PropertyGroup::ColorHdr | PropertyGroup::ColorAdjust)
137 }
138}
139
140impl NodeMetadata for CssMetadata {
141 #[inline]
142 fn merge(&mut self, other: &Self) {
143 self.property_groups |= other.property_groups;
144 self.applies_to |= other.applies_to;
145 self.box_sides |= other.box_sides;
146 self.box_portions |= other.box_portions;
147 self.declaration_kinds |= other.declaration_kinds;
148 self.rule_kinds |= other.rule_kinds;
149 self.used_at_rules |= other.used_at_rules;
150 self.vendor_prefixes |= other.vendor_prefixes;
151 }
152}
153
154impl ToCursors for CssMetadata {
156 fn to_cursors(&self, _: &mut impl css_parse::CursorSink) {}
157}
158impl ToSpan for CssMetadata {
159 fn to_span(&self) -> Span {
160 Span::DUMMY
161 }
162}
163
164impl SemanticEq for CssMetadata {
165 fn semantic_eq(&self, other: &Self) -> bool {
166 self == other
167 }
168}
169
170#[cfg(test)]
171mod tests {
172 use super::*;
173 use crate::{CssAtomSet, StyleSheet};
174 use css_lexer::Lexer;
175 use css_parse::{NodeWithMetadata, Parser};
176
177 #[test]
178 fn test_block_metadata_merge() {
179 let mut meta1 = CssMetadata::default();
180 meta1.property_groups = PropertyGroup::Color;
181 meta1.declaration_kinds = DeclarationKind::Important;
182
183 let mut meta2 = CssMetadata::default();
184 meta2.property_groups = PropertyGroup::Position;
185 meta2.declaration_kinds = DeclarationKind::Custom;
186
187 meta1.merge(&meta2);
188
189 assert!(meta1.property_groups.contains(PropertyGroup::Color));
190 assert!(meta1.property_groups.contains(PropertyGroup::Position));
191 assert!(meta1.declaration_kinds.contains(DeclarationKind::Important));
192 assert!(meta1.declaration_kinds.contains(DeclarationKind::Custom));
193 }
194
195 #[test]
196 fn test_stylesheet_metadata_simple() {
197 let css = "body { color: red; width: 100px; }";
198 let bump = bumpalo::Bump::new();
199 let lexer = Lexer::new(&CssAtomSet::ATOMS, css);
200 let mut parser = Parser::new(&bump, css, lexer);
201 let stylesheet = parser.parse::<StyleSheet>().unwrap();
202
203 let metadata = stylesheet.metadata();
204
205 dbg!(metadata);
206 assert!(metadata.property_groups.contains(PropertyGroup::Color));
207 assert!(metadata.property_groups.contains(PropertyGroup::Sizing));
208 assert!(metadata.modifies_box());
209 assert!(metadata.has_colors());
210 assert!(metadata.declaration_kinds.is_none());
211 }
212
213 #[test]
214 fn test_stylesheet_metadata_with_important() {
215 let css = "body { color: red !important; }";
216 let bump = bumpalo::Bump::new();
217 let lexer = Lexer::new(&CssAtomSet::ATOMS, css);
218 let mut parser = Parser::new(&bump, css, lexer);
219 let stylesheet = parser.parse::<StyleSheet>().unwrap();
220
221 let metadata = stylesheet.metadata();
222
223 assert!(metadata.declaration_kinds.contains(DeclarationKind::Important));
224 assert!(metadata.property_groups.contains(PropertyGroup::Color));
225 }
226
227 #[test]
228 fn test_stylesheet_metadata_custom_properties() {
229 let css = "body { --custom: value; }";
230 let bump = bumpalo::Bump::new();
231 let lexer = Lexer::new(&CssAtomSet::ATOMS, css);
232 let mut parser = Parser::new(&bump, css, lexer);
233 let stylesheet = parser.parse::<StyleSheet>().unwrap();
234
235 let metadata = stylesheet.metadata();
236
237 assert!(metadata.declaration_kinds.contains(DeclarationKind::Custom));
238 }
239
240 #[test]
241 fn test_stylesheet_metadata_nested_media() {
242 let css = "@media screen { body { color: red; } }";
243 let bump = bumpalo::Bump::new();
244 let lexer = Lexer::new(&CssAtomSet::ATOMS, css);
245 let mut parser = Parser::new(&bump, css, lexer);
246 let stylesheet = parser.parse::<StyleSheet>().unwrap();
247
248 let metadata = stylesheet.metadata();
249
250 assert!(metadata.property_groups.contains(PropertyGroup::Color));
251 assert!(metadata.used_at_rules.contains(AtRuleId::Media));
252 assert!(metadata.has_colors());
253 }
254}