1use alloc::{string::String, vec::Vec};
3use core::fmt;
4
5use crate::{
6 css_properties::{CssProperty, CssPropertyType},
7 AzString,
8};
9
10#[derive(Debug, Default, PartialEq, PartialOrd, Clone)]
13#[repr(C)]
14pub struct Css {
15 pub stylesheets: StylesheetVec,
19}
20
21impl_vec!(Stylesheet, StylesheetVec, StylesheetVecDestructor);
22impl_vec_mut!(Stylesheet, StylesheetVec);
23impl_vec_debug!(Stylesheet, StylesheetVec);
24impl_vec_partialord!(Stylesheet, StylesheetVec);
25impl_vec_clone!(Stylesheet, StylesheetVec, StylesheetVecDestructor);
26impl_vec_partialeq!(Stylesheet, StylesheetVec);
27
28impl Css {
29 pub fn is_empty(&self) -> bool {
30 self.stylesheets.iter().all(|s| s.rules.as_ref().is_empty())
31 }
32
33 pub fn new(stylesheets: Vec<Stylesheet>) -> Self {
34 Self {
35 stylesheets: stylesheets.into(),
36 }
37 }
38
39 #[cfg(feature = "parser")]
40 pub fn from_string(s: crate::AzString) -> Self {
41 crate::parser::new_from_str(s.as_str()).unwrap_or_default()
42 }
43}
44
45#[derive(Debug, Default, PartialEq, PartialOrd, Clone)]
46#[repr(C)]
47pub struct Stylesheet {
48 pub rules: CssRuleBlockVec,
50}
51
52impl_vec!(CssRuleBlock, CssRuleBlockVec, CssRuleBlockVecDestructor);
53impl_vec_mut!(CssRuleBlock, CssRuleBlockVec);
54impl_vec_debug!(CssRuleBlock, CssRuleBlockVec);
55impl_vec_partialord!(CssRuleBlock, CssRuleBlockVec);
56impl_vec_clone!(CssRuleBlock, CssRuleBlockVec, CssRuleBlockVecDestructor);
57impl_vec_partialeq!(CssRuleBlock, CssRuleBlockVec);
58
59impl Stylesheet {
60 pub fn new(rules: Vec<CssRuleBlock>) -> Self {
61 Self {
62 rules: rules.into(),
63 }
64 }
65}
66
67impl From<Vec<CssRuleBlock>> for Stylesheet {
68 fn from(rules: Vec<CssRuleBlock>) -> Self {
69 Self {
70 rules: rules.into(),
71 }
72 }
73}
74
75#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
77#[repr(C, u8)]
78pub enum CssDeclaration {
79 Static(CssProperty),
81 Dynamic(DynamicCssProperty),
83}
84
85impl CssDeclaration {
86 pub const fn new_static(prop: CssProperty) -> Self {
87 CssDeclaration::Static(prop)
88 }
89
90 pub const fn new_dynamic(prop: DynamicCssProperty) -> Self {
91 CssDeclaration::Dynamic(prop)
92 }
93
94 pub fn get_type(&self) -> CssPropertyType {
96 use self::CssDeclaration::*;
97 match self {
98 Static(s) => s.get_type(),
99 Dynamic(d) => d.default_value.get_type(),
100 }
101 }
102
103 pub fn is_inheritable(&self) -> bool {
106 use self::CssDeclaration::*;
107 match self {
108 Static(s) => s.get_type().is_inheritable(),
109 Dynamic(d) => d.is_inheritable(),
110 }
111 }
112
113 pub fn can_trigger_relayout(&self) -> bool {
116 use self::CssDeclaration::*;
117 match self {
118 Static(s) => s.get_type().can_trigger_relayout(),
119 Dynamic(d) => d.can_trigger_relayout(),
120 }
121 }
122
123 pub fn to_str(&self) -> String {
124 use self::CssDeclaration::*;
125 match self {
126 Static(s) => format!("{:?}", s),
127 Dynamic(d) => format!("var(--{}, {:?})", d.dynamic_id, d.default_value),
128 }
129 }
130}
131
132#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
153#[repr(C)]
154pub struct DynamicCssProperty {
155 pub dynamic_id: AzString,
157 pub default_value: CssProperty,
159}
160
161#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
162#[repr(C, u8)] pub enum CssPropertyValue<T> {
164 Auto,
165 None,
166 Initial,
167 Inherit,
168 Exact(T),
169}
170
171pub trait PrintAsCssValue {
172 fn print_as_css_value(&self) -> String;
173}
174
175impl<T: PrintAsCssValue> CssPropertyValue<T> {
176 pub fn get_css_value_fmt(&self) -> String {
177 match self {
178 CssPropertyValue::Auto => format!("auto"),
179 CssPropertyValue::None => format!("none"),
180 CssPropertyValue::Initial => format!("initial"),
181 CssPropertyValue::Inherit => format!("inherit"),
182 CssPropertyValue::Exact(e) => e.print_as_css_value(),
183 }
184 }
185}
186
187impl<T: fmt::Display> fmt::Display for CssPropertyValue<T> {
188 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
189 use self::CssPropertyValue::*;
190 match self {
191 Auto => write!(f, "auto"),
192 None => write!(f, "none"),
193 Initial => write!(f, "initial"),
194 Inherit => write!(f, "inherit"),
195 Exact(e) => write!(f, "{}", e),
196 }
197 }
198}
199
200impl<T> From<T> for CssPropertyValue<T> {
201 fn from(c: T) -> Self {
202 CssPropertyValue::Exact(c)
203 }
204}
205
206impl<T> CssPropertyValue<T> {
207 #[inline]
210 pub fn map_property<F: Fn(T) -> U, U>(self, map_fn: F) -> CssPropertyValue<U> {
211 match self {
212 CssPropertyValue::Exact(c) => CssPropertyValue::Exact(map_fn(c)),
213 CssPropertyValue::Auto => CssPropertyValue::Auto,
214 CssPropertyValue::None => CssPropertyValue::None,
215 CssPropertyValue::Initial => CssPropertyValue::Initial,
216 CssPropertyValue::Inherit => CssPropertyValue::Inherit,
217 }
218 }
219
220 #[inline]
221 pub fn get_property(&self) -> Option<&T> {
222 match self {
223 CssPropertyValue::Exact(c) => Some(c),
224 _ => None,
225 }
226 }
227
228 #[inline]
229 pub fn get_property_owned(self) -> Option<T> {
230 match self {
231 CssPropertyValue::Exact(c) => Some(c),
232 _ => None,
233 }
234 }
235
236 #[inline]
237 pub fn is_auto(&self) -> bool {
238 match self {
239 CssPropertyValue::Auto => true,
240 _ => false,
241 }
242 }
243
244 #[inline]
245 pub fn is_none(&self) -> bool {
246 match self {
247 CssPropertyValue::None => true,
248 _ => false,
249 }
250 }
251
252 #[inline]
253 pub fn is_initial(&self) -> bool {
254 match self {
255 CssPropertyValue::Initial => true,
256 _ => false,
257 }
258 }
259
260 #[inline]
261 pub fn is_inherit(&self) -> bool {
262 match self {
263 CssPropertyValue::Inherit => true,
264 _ => false,
265 }
266 }
267}
268
269impl<T: Default> CssPropertyValue<T> {
270 #[inline]
271 pub fn get_property_or_default(self) -> Option<T> {
272 match self {
273 CssPropertyValue::Auto | CssPropertyValue::Initial => Some(T::default()),
274 CssPropertyValue::Exact(c) => Some(c),
275 CssPropertyValue::None | CssPropertyValue::Inherit => None,
276 }
277 }
278}
279
280impl<T: Default> Default for CssPropertyValue<T> {
281 #[inline]
282 fn default() -> Self {
283 CssPropertyValue::Exact(T::default())
284 }
285}
286
287impl DynamicCssProperty {
288 pub fn is_inheritable(&self) -> bool {
289 false
293 }
294
295 pub fn can_trigger_relayout(&self) -> bool {
296 self.default_value.get_type().can_trigger_relayout()
297 }
298}
299
300#[derive(Debug, Clone, PartialOrd, PartialEq)]
303#[repr(C)]
304pub struct CssRuleBlock {
305 pub path: CssPath,
307 pub declarations: CssDeclarationVec,
310}
311
312impl_vec!(
313 CssDeclaration,
314 CssDeclarationVec,
315 CssDeclarationVecDestructor
316);
317impl_vec_mut!(CssDeclaration, CssDeclarationVec);
318impl_vec_debug!(CssDeclaration, CssDeclarationVec);
319impl_vec_partialord!(CssDeclaration, CssDeclarationVec);
320impl_vec_ord!(CssDeclaration, CssDeclarationVec);
321impl_vec_clone!(
322 CssDeclaration,
323 CssDeclarationVec,
324 CssDeclarationVecDestructor
325);
326impl_vec_partialeq!(CssDeclaration, CssDeclarationVec);
327impl_vec_eq!(CssDeclaration, CssDeclarationVec);
328impl_vec_hash!(CssDeclaration, CssDeclarationVec);
329
330impl CssRuleBlock {
331 pub fn new(path: CssPath, declarations: Vec<CssDeclaration>) -> Self {
332 Self {
333 path,
334 declarations: declarations.into(),
335 }
336 }
337}
338
339pub type CssContentGroup<'a> = Vec<&'a CssPathSelector>;
340
341#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
344#[repr(C)]
345pub enum NodeTypeTag {
346 Body,
347 Div,
348 Br,
349 P,
350 Img,
351 IFrame,
352}
353
354#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
355pub enum NodeTypeTagParseError<'a> {
356 Invalid(&'a str),
357}
358
359impl<'a> fmt::Display for NodeTypeTagParseError<'a> {
360 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
361 match &self {
362 NodeTypeTagParseError::Invalid(e) => write!(f, "Invalid node type: {}", e),
363 }
364 }
365}
366
367#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
368pub enum NodeTypeTagParseErrorOwned {
369 Invalid(String),
370}
371
372impl<'a> NodeTypeTagParseError<'a> {
373 pub fn to_contained(&self) -> NodeTypeTagParseErrorOwned {
374 match self {
375 NodeTypeTagParseError::Invalid(s) => NodeTypeTagParseErrorOwned::Invalid(s.to_string()),
376 }
377 }
378}
379
380impl NodeTypeTagParseErrorOwned {
381 pub fn to_shared<'a>(&'a self) -> NodeTypeTagParseError<'a> {
382 match self {
383 NodeTypeTagParseErrorOwned::Invalid(s) => NodeTypeTagParseError::Invalid(s),
384 }
385 }
386}
387
388impl NodeTypeTag {
390 pub fn from_str(css_key: &str) -> Result<Self, NodeTypeTagParseError> {
391 match css_key {
392 "body" => Ok(NodeTypeTag::Body),
393 "div" => Ok(NodeTypeTag::Div),
394 "p" => Ok(NodeTypeTag::P),
395 "img" => Ok(NodeTypeTag::Img),
396 other => Err(NodeTypeTagParseError::Invalid(other)),
397 }
398 }
399}
400
401impl fmt::Display for NodeTypeTag {
402 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
403 match self {
404 NodeTypeTag::Body => write!(f, "body"),
405 NodeTypeTag::Div => write!(f, "div"),
406 NodeTypeTag::Br => write!(f, "br"),
407 NodeTypeTag::P => write!(f, "p"),
408 NodeTypeTag::Img => write!(f, "img"),
409 NodeTypeTag::IFrame => write!(f, "iframe"),
410 }
411 }
412}
413
414#[derive(Clone, Hash, Default, PartialEq, Eq, PartialOrd, Ord)]
426#[repr(C)]
427pub struct CssPath {
428 pub selectors: CssPathSelectorVec,
429}
430
431impl_vec!(
432 CssPathSelector,
433 CssPathSelectorVec,
434 CssPathSelectorVecDestructor
435);
436impl_vec_debug!(CssPathSelector, CssPathSelectorVec);
437impl_vec_partialord!(CssPathSelector, CssPathSelectorVec);
438impl_vec_ord!(CssPathSelector, CssPathSelectorVec);
439impl_vec_clone!(
440 CssPathSelector,
441 CssPathSelectorVec,
442 CssPathSelectorVecDestructor
443);
444impl_vec_partialeq!(CssPathSelector, CssPathSelectorVec);
445impl_vec_eq!(CssPathSelector, CssPathSelectorVec);
446impl_vec_hash!(CssPathSelector, CssPathSelectorVec);
447
448impl CssPath {
449 pub fn new(selectors: Vec<CssPathSelector>) -> Self {
450 Self {
451 selectors: selectors.into(),
452 }
453 }
454}
455
456impl fmt::Display for CssPath {
457 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
458 for selector in self.selectors.as_ref() {
459 write!(f, "{}", selector)?;
460 }
461 Ok(())
462 }
463}
464
465impl fmt::Debug for CssPath {
466 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
467 write!(f, "{}", self)
468 }
469}
470
471#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
472#[repr(C, u8)]
473pub enum CssPathSelector {
474 Global,
476 Type(NodeTypeTag),
478 Class(AzString),
480 Id(AzString),
482 PseudoSelector(CssPathPseudoSelector),
484 DirectChildren,
486 Children,
488}
489
490impl Default for CssPathSelector {
491 fn default() -> Self {
492 CssPathSelector::Global
493 }
494}
495
496impl fmt::Display for CssPathSelector {
497 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
498 use self::CssPathSelector::*;
499 match &self {
500 Global => write!(f, "*"),
501 Type(n) => write!(f, "{}", n),
502 Class(c) => write!(f, ".{}", c),
503 Id(i) => write!(f, "#{}", i),
504 PseudoSelector(p) => write!(f, ":{}", p),
505 DirectChildren => write!(f, ">"),
506 Children => write!(f, " "),
507 }
508 }
509}
510
511#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
512#[repr(C, u8)]
513pub enum CssPathPseudoSelector {
514 First,
516 Last,
518 NthChild(CssNthChildSelector),
520 Hover,
522 Active,
524 Focus,
526}
527
528#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
529#[repr(C, u8)]
530pub enum CssNthChildSelector {
531 Number(u32),
532 Even,
533 Odd,
534 Pattern(CssNthChildPattern),
535}
536
537#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
538#[repr(C)]
539pub struct CssNthChildPattern {
540 pub repeat: u32,
541 pub offset: u32,
542}
543
544impl fmt::Display for CssNthChildSelector {
545 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
546 use self::CssNthChildSelector::*;
547 match &self {
548 Number(u) => write!(f, "{}", u),
549 Even => write!(f, "even"),
550 Odd => write!(f, "odd"),
551 Pattern(p) => write!(f, "{}n + {}", p.repeat, p.offset),
552 }
553 }
554}
555
556impl fmt::Display for CssPathPseudoSelector {
557 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
558 use self::CssPathPseudoSelector::*;
559 match &self {
560 First => write!(f, "first"),
561 Last => write!(f, "last"),
562 NthChild(u) => write!(f, "nth-child({})", u),
563 Hover => write!(f, "hover"),
564 Active => write!(f, "active"),
565 Focus => write!(f, "focus"),
566 }
567 }
568}
569
570impl Css {
571 pub fn empty() -> Self {
573 Default::default()
574 }
575
576 pub fn sort_by_specificity(&mut self) {
577 self.stylesheets
578 .as_mut()
579 .iter_mut()
580 .for_each(|s| s.sort_by_specificity());
581 }
582
583 pub fn rules<'a>(&'a self) -> RuleIterator<'a> {
584 RuleIterator {
585 current_stylesheet: 0,
586 current_rule: 0,
587 css: self,
588 }
589 }
590}
591
592pub struct RuleIterator<'a> {
593 current_stylesheet: usize,
594 current_rule: usize,
595 css: &'a Css,
596}
597
598impl<'a> Iterator for RuleIterator<'a> {
599 type Item = &'a CssRuleBlock;
600 fn next(&mut self) -> Option<&'a CssRuleBlock> {
601 let current_stylesheet = self.css.stylesheets.get(self.current_stylesheet)?;
602 match current_stylesheet.rules.get(self.current_rule) {
603 Some(s) => {
604 self.current_rule += 1;
605 Some(s)
606 }
607 None => {
608 self.current_rule = 0;
609 self.current_stylesheet += 1;
610 self.next()
611 }
612 }
613 }
614}
615
616impl Stylesheet {
617 pub fn empty() -> Self {
619 Default::default()
620 }
621
622 pub fn sort_by_specificity(&mut self) {
625 self.rules
626 .as_mut()
627 .sort_by(|a, b| get_specificity(&a.path).cmp(&get_specificity(&b.path)));
628 }
629}
630
631fn get_specificity(path: &CssPath) -> (usize, usize, usize, usize) {
634 let id_count = path
635 .selectors
636 .iter()
637 .filter(|x| {
638 if let CssPathSelector::Id(_) = x {
639 true
640 } else {
641 false
642 }
643 })
644 .count();
645 let class_count = path
646 .selectors
647 .iter()
648 .filter(|x| {
649 if let CssPathSelector::Class(_) = x {
650 true
651 } else {
652 false
653 }
654 })
655 .count();
656 let div_count = path
657 .selectors
658 .iter()
659 .filter(|x| {
660 if let CssPathSelector::Type(_) = x {
661 true
662 } else {
663 false
664 }
665 })
666 .count();
667 (id_count, class_count, div_count, path.selectors.len())
668}