1use std::{marker::PhantomData, str::FromStr};
2
3use crate::ns::*;
4use num_traits::ToPrimitive;
5use serde::{Serialize, Deserialize};
6
7#[derive(Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
11pub enum CssCombinatorType {
12 Descendant,
13 Child,
14 Preceded,
15 Sibling,
16}
17
18impl ToString for CssCombinatorType {
19 fn to_string(&self) -> String {
21 match self {
22 Self::Descendant => " ".into(),
23 Self::Child => ">".into(),
24 Self::Preceded => "+".into(),
25 Self::Sibling => "~".into(),
26 }
27 }
28}
29
30#[derive(Clone, Serialize, Deserialize)]
31pub enum CssDirective {
32 Invalidated(InvalidatedNode),
33 FontFace(CssFontFace),
34 MediaQuery(CssMediaQuery),
35 NamespaceDefinition(CssNamespaceDefinition),
36 Rule(CssRule),
37}
38
39impl CssDirective {
40 pub fn location(&self) -> Location {
41 match self {
42 Self::Invalidated(v) => v.location.clone(),
43 Self::FontFace(v) => v.location.clone(),
44 Self::MediaQuery(v) => v.location.clone(),
45 Self::NamespaceDefinition(v) => v.location.clone(),
46 Self::Rule(v) => v.location.clone(),
47 }
48 }
49}
50
51#[derive(Clone, Serialize, Deserialize)]
52pub enum CssPropertyValue {
53 Invalidated(InvalidatedNode),
54 Array(CssArrayPropertyValue),
56 MultiValue(CssMultiValuePropertyValue),
58 Color(CssColorPropertyValue),
60 Number(CssNumberPropertyValue),
62 RgbColor(CssRgbColorPropertyValue),
64 String(CssStringPropertyValue),
66 Identifier(CssIdentifierPropertyValue),
68 ClassReference(CssClassReferencePropertyValue),
70 PropertyReference(CssPropertyReferencePropertyValue),
72 Url(CssUrlPropertyValue),
74 Local(CssLocalPropertyValue),
76 Embed(CssEmbedPropertyValue),
78}
79
80impl CssPropertyValue {
81 pub fn location(&self) -> Location {
82 match self {
83 Self::Invalidated(v) => v.location.clone(),
84 Self::Array(v) => v.location.clone(),
85 Self::MultiValue(v) => v.location.clone(),
86 Self::Color(v) => v.location.clone(),
87 Self::Number(v) => v.location.clone(),
88 Self::RgbColor(v) => v.location.clone(),
89 Self::String(v) => v.location.clone(),
90 Self::Identifier(v) => v.location.clone(),
91 Self::ClassReference(v) => v.location.clone(),
92 Self::PropertyReference(v) => v.location.clone(),
93 Self::Url(v) => v.location.clone(),
94 Self::Local(v) => v.location.clone(),
95 Self::Embed(v) => v.location.clone(),
96 }
97 }
98
99 pub fn as_array(&self) -> Option<&CssArrayPropertyValue> {
100 let Self::Array(v) = self else { return None; };
101 Some(v)
102 }
103}
104
105#[derive(Clone, Serialize, Deserialize)]
106pub enum CssSelector {
107 Invalidated(InvalidatedNode),
108 Base(CssBaseSelector),
109 Combinator(CssCombinatorSelector),
110}
111
112impl CssSelector {
113 pub fn location(&self) -> Location {
114 match self {
115 Self::Invalidated(v) => v.location.clone(),
116 Self::Base(v) => v.location.clone(),
117 Self::Combinator(v) => v.location.clone(),
118 }
119 }
120}
121
122#[derive(Clone, Serialize, Deserialize)]
130pub struct CssArrayPropertyValue {
131 pub location: Location,
132 pub elements: Vec<Rc<CssPropertyValue>>,
133}
134
135#[derive(Clone, Serialize, Deserialize)]
143pub struct CssMultiValuePropertyValue {
144 pub location: Location,
145 pub values: Vec<Rc<CssPropertyValue>>,
146}
147
148#[derive(Clone, Serialize, Deserialize)]
150pub struct CssBaseSelector {
151 pub location: Location,
152 pub namespace_prefix: Option<(String, Location)>,
153 pub element_name: Option<(String, Location)>,
154 pub conditions: Vec<Rc<CssSelectorCondition>>,
155}
156
157#[derive(Clone, Serialize, Deserialize)]
159pub enum CssSelectorCondition {
160 Invalidated(InvalidatedNode),
161 Class((String, Location)),
163 Id((String, Location)),
165 Pseudo((String, Location)),
167 PseudoElement((String, Location)),
169 Not {
171 location: Location,
172 condition: Rc<CssSelectorCondition>,
173 },
174 Attribute {
176 location: Location,
177 name: (String, Location),
178 operator: Option<CssAttributeOperator>,
179 value: Option<(String, Location)>,
180 },
181}
182
183impl CssSelectorCondition {
184 pub fn location(&self) -> Location {
185 match self {
186 Self::Invalidated(v) => v.location.clone(),
187 Self::Class((_, l)) => l.clone(),
188 Self::Id((_, l)) => l.clone(),
189 Self::Pseudo((_, l)) => l.clone(),
190 Self::PseudoElement((_, l)) => l.clone(),
191 Self::Not { location, .. } => location.clone(),
192 Self::Attribute { location, .. } => location.clone(),
193 }
194 }
195}
196
197#[derive(Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
198pub enum CssAttributeOperator {
199 Equals,
200 BeginsWith,
201 EndsWith,
202 Contains,
203 ListMatch,
204 HreflangMatch,
205}
206
207impl ToString for CssAttributeOperator {
208 fn to_string(&self) -> String {
209 match self {
210 Self::Equals => "=".into(),
211 Self::BeginsWith => "^=".into(),
212 Self::EndsWith => "$=".into(),
213 Self::Contains => "*=".into(),
214 Self::ListMatch => "~=".into(),
215 Self::HreflangMatch => "|=".into(),
216 }
217 }
218}
219
220#[derive(Clone, Serialize, Deserialize)]
221pub struct CssColorPropertyValue {
222 pub location: Location,
223 pub color_int: u32,
224}
225
226impl CssColorPropertyValue {
227 pub fn from_hex(location: Location, token_text: &str) -> Result<Self, ParserError> {
228 let mut token_text = if token_text.starts_with('#') { token_text.to_owned() } else {
229 "#".to_owned() + token_text
230 };
231 if token_text.len() == 4 {
232 let mut six = String::new();
233 let chars: Vec<_> = token_text.chars().collect();
234 six.push('#');
235 six.push(chars[1]);
236 six.push(chars[1]);
237 six.push(chars[2]);
238 six.push(chars[2]);
239 six.push(chars[3]);
240 six.push(chars[3]);
241 token_text = six;
242 }
243 Ok(Self {
244 location,
245 color_int: u32::from_str_radix(&token_text[1..], 16).map_err(|_| ParserError::Common)?.clamp(0x000000, 0xFFFFFF),
246 })
247 }
248
249 pub fn text(&self) -> String {
250 self.location.text()
251 }
252}
253
254#[derive(Clone, Serialize, Deserialize)]
255pub struct CssNumberPropertyValue {
256 pub location: Location,
257 pub value: f64,
258 pub unit: Option<String>,
259}
260
261#[derive(Clone, Serialize, Deserialize)]
262pub struct CssRgbColorPropertyValue {
263 pub location: Location,
264 pub color_int: u32,
265}
266
267impl CssRgbColorPropertyValue {
268 pub fn from_raw_arguments(location: &Location, raw_arguments: &[String]) -> Result<Self, ParserError> {
269 Ok(CssRgbColorPropertyValue {
270 location: location.clone(),
271 color_int: (Self::parse_component(&raw_arguments[0])? << 16)
272 | (Self::parse_component(&raw_arguments[1])? << 8)
273 | Self::parse_component(&raw_arguments[2])?,
274 })
275 }
276
277 fn parse_component(input: &str) -> Result<u32, ParserError> {
278 let i = input.find('%');
279 let v: u32;
280 if let Some(i) = i {
281 let percent = f64::from_str(&input[..i]).map_err(|_| ParserError::Common)?.clamp(0.0, 100.0);
282 v = (255.0 * (percent / 100.0)).round().to_u32().ok_or(ParserError::Common)?;
283 } else if input.contains('.') {
284 let ratio = f64::from_str(input).map_err(|_| ParserError::Common)?.clamp(0.0, 1.0);
285 v = (255.0 * ratio).round().to_u32().ok_or(ParserError::Common)?;
286 } else {
287 v = u32::from_str(input).map_err(|_| ParserError::Common)?;
288 }
289 Ok(v.clamp(0, 255))
290 }
291}
292
293#[derive(Clone, Serialize, Deserialize)]
295pub struct CssStringPropertyValue {
296 pub location: Location,
297 pub value: String,
298}
299
300#[derive(Clone, Serialize, Deserialize)]
301pub struct CssIdentifierPropertyValue {
302 pub location: Location,
303 pub value: String,
304}
305
306#[derive(Clone, Serialize, Deserialize)]
307pub struct CssClassReferencePropertyValue {
308 pub location: Location,
309 pub name: (String, Location),
311}
312
313#[derive(Clone, Serialize, Deserialize)]
314pub struct CssPropertyReferencePropertyValue {
315 pub location: Location,
316 pub name: (String, Location),
317}
318
319#[derive(Clone, Serialize, Deserialize)]
320pub struct CssUrlPropertyValue {
321 pub location: Location,
322 pub url: (String, Location),
323 pub format: Option<(String, Location)>,
324}
325
326#[derive(Clone, Serialize, Deserialize)]
327pub struct CssLocalPropertyValue {
328 pub location: Location,
329 pub name: (String, Location),
330}
331
332#[derive(Clone, Serialize, Deserialize)]
333pub struct CssEmbedPropertyValue {
334 pub location: Location,
335 pub entries: Vec<Rc<CssEmbedEntry>>,
336}
337
338#[derive(Clone, Serialize, Deserialize)]
341pub struct CssEmbedEntry {
342 pub location: Location,
343 pub key: Option<(String, Location)>,
344 pub value: (String, Location),
345}
346
347#[derive(Clone, Serialize, Deserialize)]
349pub struct CssCombinatorSelector {
350 pub location: Location,
351 pub left: Rc<CssSelector>,
352 pub right: Rc<CssSelector>,
353 pub combinator_type: CssCombinatorType,
354}
355
356#[derive(Clone, Serialize, Deserialize)]
359pub struct CssDocument {
360 pub location: Location,
361 pub directives: Vec<Rc<CssDirective>>,
362}
363
364#[derive(Clone, Serialize, Deserialize)]
366pub struct CssFontFace {
367 pub location: Location,
368 pub properties: Vec<Rc<CssProperty>>,
369}
370
371#[derive(Clone, Serialize, Deserialize)]
372pub struct CssProperty {
373 pub location: Location,
374 pub name: (String, Location),
375 pub value: Rc<CssPropertyValue>,
376 #[serde(skip)]
377 _phantom: PhantomData<()>,
378}
379
380impl CssProperty {
381 pub fn new(location: Location, name: (String, Location), value: Rc<CssPropertyValue>) -> Self {
382 Self {
383 location,
384 name: (Self::normalize(&name.0), name.1),
385 value,
386 _phantom: PhantomData::default(),
387 }
388 }
389
390 fn normalize(name: &str) -> String {
393 let mut split = name.split('-').map(|s| s.to_owned()).collect::<Vec<_>>();
394 let mut v = split[0].chars();
395 let mut v1 = String::new();
396 if let Some(ch) = v.next() {
397 v1.push_str(&ch.to_lowercase().to_string());
398 for ch in v {
399 v1.push(ch);
400 }
401 }
402 split[0] = v1;
403 for i in 1..split.len() {
404 let mut v = split[i].chars();
405 let mut v1 = String::new();
406 if let Some(ch) = v.next() {
407 v1.push_str(&ch.to_uppercase().to_string());
408 for ch in v {
409 v1.push(ch);
410 }
411 }
412 split[i] = v1;
413 }
414 split.join("")
415 }
416}
417
418#[derive(Clone, Serialize, Deserialize)]
419pub struct CssMediaQuery {
420 pub location: Location,
421 pub conditions: Vec<Rc<CssMediaQueryCondition>>,
422 pub rules: Vec<Rc<CssRule>>,
423}
424
425#[derive(Clone, Serialize, Deserialize)]
426pub enum CssMediaQueryCondition {
427 Invalidated(InvalidatedNode),
428 Id((String, Location)),
430 OnlyId {
433 location: Location,
434 id: (String, Location),
435 },
436 ParenProperty((Rc<CssProperty>, Location)),
439 And {
441 location: Location,
442 left: Rc<CssMediaQueryCondition>,
443 right: Rc<CssMediaQueryCondition>,
444 },
445}
446
447impl CssMediaQueryCondition {
448 pub fn location(&self) -> Location {
449 match self {
450 Self::Invalidated(v) => v.location.clone(),
451 Self::Id((_, l)) => l.clone(),
452 Self::OnlyId { location, .. } => location.clone(),
453 Self::ParenProperty((_, l)) => l.clone(),
454 Self::And { location, .. } => location.clone(),
455 }
456 }
457}
458
459#[derive(Clone, Serialize, Deserialize)]
460pub struct CssRule {
461 pub location: Location,
462 pub selectors: Vec<Rc<CssSelector>>,
463 pub properties: Vec<Rc<CssProperty>>,
464}
465
466#[derive(Clone, Serialize, Deserialize)]
467pub struct CssNamespaceDefinition {
468 pub location: Location,
469 pub prefix: (String, Location),
470 pub uri: (String, Location),
471}