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 Import(CssImport),
34 FontFace(CssFontFace),
35 MediaQuery(CssMediaQuery),
36 NamespaceDefinition(CssNamespaceDefinition),
37 Rule(CssRule),
38}
39
40impl CssDirective {
41 pub fn location(&self) -> Location {
42 match self {
43 Self::Invalidated(v) => v.location.clone(),
44 Self::Import(v) => v.location.clone(),
45 Self::FontFace(v) => v.location.clone(),
46 Self::MediaQuery(v) => v.location.clone(),
47 Self::NamespaceDefinition(v) => v.location.clone(),
48 Self::Rule(v) => v.location.clone(),
49 }
50 }
51}
52
53#[derive(Clone, Serialize, Deserialize)]
54pub enum CssPropertyValue {
55 Invalidated(InvalidatedNode),
56 Array(CssArrayPropertyValue),
58 MultiValue(CssMultiValuePropertyValue),
60 Color(CssColorPropertyValue),
62 Number(CssNumberPropertyValue),
64 RgbColor(CssRgbColorPropertyValue),
66 String(CssStringPropertyValue),
68 Identifier(CssIdentifierPropertyValue),
70 ClassReference(CssClassReferencePropertyValue),
72 PropertyReference(CssPropertyReferencePropertyValue),
74 Url(CssUrlPropertyValue),
76 Local(CssLocalPropertyValue),
78 Embed(CssEmbedPropertyValue),
80}
81
82impl CssPropertyValue {
83 pub fn location(&self) -> Location {
84 match self {
85 Self::Invalidated(v) => v.location.clone(),
86 Self::Array(v) => v.location.clone(),
87 Self::MultiValue(v) => v.location.clone(),
88 Self::Color(v) => v.location.clone(),
89 Self::Number(v) => v.location.clone(),
90 Self::RgbColor(v) => v.location.clone(),
91 Self::String(v) => v.location.clone(),
92 Self::Identifier(v) => v.location.clone(),
93 Self::ClassReference(v) => v.location.clone(),
94 Self::PropertyReference(v) => v.location.clone(),
95 Self::Url(v) => v.location.clone(),
96 Self::Local(v) => v.location.clone(),
97 Self::Embed(v) => v.location.clone(),
98 }
99 }
100
101 pub fn as_array(&self) -> Option<&CssArrayPropertyValue> {
102 let Self::Array(v) = self else { return None; };
103 Some(v)
104 }
105}
106
107#[derive(Clone, Serialize, Deserialize)]
108pub enum CssSelector {
109 Invalidated(InvalidatedNode),
110 Base(CssBaseSelector),
111 Combinator(CssCombinatorSelector),
112}
113
114impl CssSelector {
115 pub fn location(&self) -> Location {
116 match self {
117 Self::Invalidated(v) => v.location.clone(),
118 Self::Base(v) => v.location.clone(),
119 Self::Combinator(v) => v.location.clone(),
120 }
121 }
122}
123
124impl Eq for CssSelector {}
125
126impl PartialEq for CssSelector {
127 fn eq(&self, other: &Self) -> bool {
128 match self {
129 Self::Invalidated(_) => matches!(other, Self::Invalidated(_)),
130 Self::Base(v1) => {
131 if let Self::Base(v2) = other {
132 v1 == v2
133 } else {
134 false
135 }
136 },
137 Self::Combinator(v1) => {
138 if let Self::Combinator(v2) = other {
139 v1 == v2
140 } else {
141 false
142 }
143 },
144 }
145 }
146}
147
148#[derive(Clone, Serialize, Deserialize)]
156pub struct CssArrayPropertyValue {
157 pub location: Location,
158 pub elements: Vec<Rc<CssPropertyValue>>,
159}
160
161#[derive(Clone, Serialize, Deserialize)]
169pub struct CssMultiValuePropertyValue {
170 pub location: Location,
171 pub values: Vec<Rc<CssPropertyValue>>,
172}
173
174#[derive(Clone, Serialize, Deserialize)]
176pub struct CssBaseSelector {
177 pub location: Location,
178 pub namespace_prefix: Option<(String, Location)>,
179 pub element_name: Option<(String, Location)>,
180 pub conditions: Vec<Rc<CssSelectorCondition>>,
181}
182
183impl Eq for CssBaseSelector {}
184
185impl PartialEq for CssBaseSelector {
186 fn eq(&self, other: &Self) -> bool {
187 self.namespace_prefix.as_ref().map(|p| &p.0) == other.namespace_prefix.as_ref().map(|p| &p.0) &&
188 self.element_name.as_ref().map(|n| &n.0) == other.element_name.as_ref().map(|n| &n.0) &&
189 self.conditions == other.conditions
190 }
191}
192
193#[derive(Clone, Serialize, Deserialize)]
195pub enum CssSelectorCondition {
196 Invalidated(InvalidatedNode),
197 Class((String, Location)),
199 Id((String, Location)),
201 Pseudo((String, Location)),
203 PseudoElement((String, Location)),
205 NthChild((CssNthChildKind, Location)),
208 Not {
210 location: Location,
211 condition: Rc<CssSelectorCondition>,
212 },
213 Attribute {
215 location: Location,
216 name: (String, Location),
217 operator: Option<CssAttributeOperator>,
218 value: Option<(String, Location)>,
219 },
220}
221
222impl Eq for CssSelectorCondition {}
223
224impl PartialEq for CssSelectorCondition {
225 fn eq(&self, other: &Self) -> bool {
226 match self {
227 Self::Invalidated(_) => matches!(other, Self::Invalidated(_)),
228 Self::Class((v1, _)) => {
229 if let Self::Class((v2, _)) = other {
230 v1 == v2
231 } else {
232 false
233 }
234 },
235 Self::Id((v1, _)) => {
236 if let Self::Id((v2, _)) = other {
237 v1 == v2
238 } else {
239 false
240 }
241 },
242 Self::Pseudo((v1, _)) => {
243 if let Self::Pseudo((v2, _)) = other {
244 v1 == v2
245 } else {
246 false
247 }
248 },
249 Self::PseudoElement((v1, _)) => {
250 if let Self::PseudoElement((v2, _)) = other {
251 v1 == v2
252 } else {
253 false
254 }
255 },
256 Self::NthChild((v1, _)) => {
257 if let Self::NthChild((v2, _)) = other {
258 v1 == v2
259 } else {
260 false
261 }
262 },
263 Self::Not { condition: c1, .. } => {
264 if let Self::Not { condition: c2, ..} = other {
265 c1 == c2
266 } else {
267 false
268 }
269 },
270 Self::Attribute { name: (name1, _), operator: op1, value: value1, .. } => {
271 let value1 = value1.as_ref().map(|(v, _)| v);
272 if let Self::Attribute { name: (name2, _), operator: op2, value: value2, .. } = other {
273 let value2 = value2.as_ref().map(|(v, _)| v);
274 name1 == name2 && op1 == op2 && value1 == value2
275 } else {
276 false
277 }
278 },
279 }
280 }
281}
282
283#[derive(Clone, Serialize, Deserialize, PartialEq, Eq)]
284pub enum CssNthChildKind {
285 Invalidated,
286 Odd,
287 Even,
288 Number(u32),
289}
290
291impl CssSelectorCondition {
292 pub fn location(&self) -> Location {
293 match self {
294 Self::Invalidated(v) => v.location.clone(),
295 Self::Class((_, l)) => l.clone(),
296 Self::Id((_, l)) => l.clone(),
297 Self::NthChild((_, l)) => l.clone(),
298 Self::Pseudo((_, l)) => l.clone(),
299 Self::PseudoElement((_, l)) => l.clone(),
300 Self::Not { location, .. } => location.clone(),
301 Self::Attribute { location, .. } => location.clone(),
302 }
303 }
304}
305
306#[derive(Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
307pub enum CssAttributeOperator {
308 Equals,
309 BeginsWith,
310 EndsWith,
311 Contains,
312 ListMatch,
313 HreflangMatch,
314}
315
316impl ToString for CssAttributeOperator {
317 fn to_string(&self) -> String {
318 match self {
319 Self::Equals => "=".into(),
320 Self::BeginsWith => "^=".into(),
321 Self::EndsWith => "$=".into(),
322 Self::Contains => "*=".into(),
323 Self::ListMatch => "~=".into(),
324 Self::HreflangMatch => "|=".into(),
325 }
326 }
327}
328
329#[derive(Clone, Serialize, Deserialize)]
330pub struct CssColorPropertyValue {
331 pub location: Location,
332 pub color_int: u32,
333}
334
335impl CssColorPropertyValue {
336 pub fn from_hex(location: Location, token_text: &str) -> Result<Self, ParserError> {
337 let mut token_text = if token_text.starts_with('#') { token_text.to_owned() } else {
338 "#".to_owned() + token_text
339 };
340 if token_text.len() == 4 {
341 let mut six = String::new();
342 let chars: Vec<_> = token_text.chars().collect();
343 six.push('#');
344 six.push(chars[1]);
345 six.push(chars[1]);
346 six.push(chars[2]);
347 six.push(chars[2]);
348 six.push(chars[3]);
349 six.push(chars[3]);
350 token_text = six;
351 }
352 Ok(Self {
353 location,
354 color_int: u32::from_str_radix(&token_text[1..], 16).map_err(|_| ParserError::Common)?.clamp(0x000000, 0xFFFFFF),
355 })
356 }
357
358 pub fn text(&self) -> String {
359 self.location.text()
360 }
361}
362
363#[derive(Clone, Serialize, Deserialize)]
364pub struct CssNumberPropertyValue {
365 pub location: Location,
366 pub value: f64,
367 pub unit: Option<String>,
368}
369
370#[derive(Clone, Serialize, Deserialize)]
371pub struct CssRgbColorPropertyValue {
372 pub location: Location,
373 pub color_int: u32,
374}
375
376impl CssRgbColorPropertyValue {
377 pub fn from_raw_arguments(location: &Location, raw_arguments: &[String]) -> Result<Self, ParserError> {
378 Ok(CssRgbColorPropertyValue {
379 location: location.clone(),
380 color_int: (Self::parse_component(&raw_arguments[0])? << 16)
381 | (Self::parse_component(&raw_arguments[1])? << 8)
382 | Self::parse_component(&raw_arguments[2])?,
383 })
384 }
385
386 fn parse_component(input: &str) -> Result<u32, ParserError> {
387 let i = input.find('%');
388 let v: u32;
389 if let Some(i) = i {
390 let percent = f64::from_str(&input[..i]).map_err(|_| ParserError::Common)?.clamp(0.0, 100.0);
391 v = (255.0 * (percent / 100.0)).round().to_u32().ok_or(ParserError::Common)?;
392 } else if input.contains('.') {
393 let ratio = f64::from_str(input).map_err(|_| ParserError::Common)?.clamp(0.0, 1.0);
394 v = (255.0 * ratio).round().to_u32().ok_or(ParserError::Common)?;
395 } else {
396 v = u32::from_str(input).map_err(|_| ParserError::Common)?;
397 }
398 Ok(v.clamp(0, 255))
399 }
400}
401
402#[derive(Clone, Serialize, Deserialize)]
404pub struct CssStringPropertyValue {
405 pub location: Location,
406 pub value: String,
407}
408
409#[derive(Clone, Serialize, Deserialize)]
410pub struct CssIdentifierPropertyValue {
411 pub location: Location,
412 pub value: String,
413}
414
415#[derive(Clone, Serialize, Deserialize)]
416pub struct CssClassReferencePropertyValue {
417 pub location: Location,
418 pub name: (String, Location),
420}
421
422#[derive(Clone, Serialize, Deserialize)]
423pub struct CssPropertyReferencePropertyValue {
424 pub location: Location,
425 pub name: (String, Location),
426}
427
428#[derive(Clone, Serialize, Deserialize)]
429pub struct CssUrlPropertyValue {
430 pub location: Location,
431 pub url: (String, Location),
432 pub format: Option<(String, Location)>,
433}
434
435#[derive(Clone, Serialize, Deserialize)]
436pub struct CssLocalPropertyValue {
437 pub location: Location,
438 pub name: (String, Location),
439}
440
441#[derive(Clone, Serialize, Deserialize)]
442pub struct CssEmbedPropertyValue {
443 pub location: Location,
444 pub entries: Vec<Rc<CssEmbedEntry>>,
445}
446
447#[derive(Clone, Serialize, Deserialize)]
450pub struct CssEmbedEntry {
451 pub location: Location,
452 pub key: Option<(String, Location)>,
453 pub value: (String, Location),
454}
455
456#[derive(Clone, Serialize, Deserialize)]
458pub struct CssCombinatorSelector {
459 pub location: Location,
460 pub left: Rc<CssSelector>,
461 pub right: Rc<CssSelector>,
462 pub combinator_type: CssCombinatorType,
463}
464
465impl Eq for CssCombinatorSelector {}
466
467impl PartialEq for CssCombinatorSelector {
468 fn eq(&self, other: &Self) -> bool {
469 self.left == other.left &&
470 self.right == other.right &&
471 self.combinator_type == other.combinator_type
472 }
473}
474
475#[derive(Clone, Serialize, Deserialize)]
478pub struct CssDocument {
479 pub location: Location,
480 pub directives: Vec<Rc<CssDirective>>,
481}
482
483#[derive(Clone, Serialize, Deserialize)]
485pub struct CssFontFace {
486 pub location: Location,
487 pub properties: Vec<Rc<CssProperty>>,
488}
489
490#[derive(Clone, Serialize, Deserialize)]
492pub struct CssImport {
493 pub location: Location,
494 pub url: Option<String>,
496}
497
498#[derive(Clone, Serialize, Deserialize)]
499pub struct CssProperty {
500 pub location: Location,
501 pub name: (String, Location),
502 pub value: Rc<CssPropertyValue>,
503 #[serde(skip)]
504 _phantom: PhantomData<()>,
505}
506
507impl CssProperty {
508 pub fn new(location: Location, name: (String, Location), value: Rc<CssPropertyValue>) -> Self {
509 Self {
510 location,
511 name: (Self::normalize(&name.0), name.1),
512 value,
513 _phantom: PhantomData::default(),
514 }
515 }
516
517 fn normalize(name: &str) -> String {
520 let mut split = name.split('-').map(|s| s.to_owned()).collect::<Vec<_>>();
521 let mut v = split[0].chars();
522 let mut v1 = String::new();
523 if let Some(ch) = v.next() {
524 v1.push_str(&ch.to_lowercase().to_string());
525 for ch in v {
526 v1.push(ch);
527 }
528 }
529 split[0] = v1;
530 for i in 1..split.len() {
531 let mut v = split[i].chars();
532 let mut v1 = String::new();
533 if let Some(ch) = v.next() {
534 v1.push_str(&ch.to_uppercase().to_string());
535 for ch in v {
536 v1.push(ch);
537 }
538 }
539 split[i] = v1;
540 }
541 split.join("")
542 }
543}
544
545#[derive(Clone, Serialize, Deserialize)]
546pub struct CssMediaQuery {
547 pub location: Location,
548 pub conditions: Vec<Rc<CssMediaQueryCondition>>,
549 pub rules: Vec<Rc<CssRule>>,
550}
551
552#[derive(Clone, Serialize, Deserialize)]
553pub enum CssMediaQueryCondition {
554 Invalidated(InvalidatedNode),
555 Id((String, Location)),
557 OnlyId {
560 location: Location,
561 id: (String, Location),
562 },
563 ParenProperty((Rc<CssProperty>, Location)),
566 And {
568 location: Location,
569 left: Rc<CssMediaQueryCondition>,
570 right: Rc<CssMediaQueryCondition>,
571 },
572}
573
574impl CssMediaQueryCondition {
575 pub fn location(&self) -> Location {
576 match self {
577 Self::Invalidated(v) => v.location.clone(),
578 Self::Id((_, l)) => l.clone(),
579 Self::OnlyId { location, .. } => location.clone(),
580 Self::ParenProperty((_, l)) => l.clone(),
581 Self::And { location, .. } => location.clone(),
582 }
583 }
584}
585
586#[derive(Clone, Serialize, Deserialize)]
587pub struct CssRule {
588 pub location: Location,
589 pub selectors: Vec<Rc<CssSelector>>,
590 pub properties: Vec<Rc<CssProperty>>,
591}
592
593#[derive(Clone, Serialize, Deserialize)]
594pub struct CssNamespaceDefinition {
595 pub location: Location,
596 pub prefix: (String, Location),
597 pub uri: (String, Location),
598}