Skip to main content

azul_css/
css.rs

1//! Types and methods used to describe the style of an application
2use alloc::{string::String, vec::Vec};
3use core::fmt;
4
5use crate::{
6    dynamic_selector::DynamicSelectorVec,
7    props::property::{format_static_css_prop, CssProperty, CssPropertyType},
8    AzString,
9};
10
11/// Css stylesheet - contains a parsed CSS stylesheet in "rule blocks",
12/// i.e. blocks of key-value pairs associated with a selector path.
13#[derive(Debug, Default, PartialEq, PartialOrd, Clone)]
14#[repr(C)]
15pub struct Css {
16    /// One CSS stylesheet can hold more than one sub-stylesheet:
17    /// For example, when overriding native styles, the `.sort_by_specificy()` function
18    /// should not mix the two stylesheets during sorting.
19    pub stylesheets: StylesheetVec,
20}
21
22impl_vec!(
23    Stylesheet,
24    StylesheetVec,
25    StylesheetVecDestructor,
26    StylesheetVecDestructorType
27);
28impl_vec_mut!(Stylesheet, StylesheetVec);
29impl_vec_debug!(Stylesheet, StylesheetVec);
30impl_vec_partialord!(Stylesheet, StylesheetVec);
31impl_vec_clone!(Stylesheet, StylesheetVec, StylesheetVecDestructor);
32impl_vec_partialeq!(Stylesheet, StylesheetVec);
33
34impl Css {
35    pub fn is_empty(&self) -> bool {
36        self.stylesheets.iter().all(|s| s.rules.as_ref().is_empty())
37    }
38
39    pub fn new(stylesheets: Vec<Stylesheet>) -> Self {
40        Self {
41            stylesheets: stylesheets.into(),
42        }
43    }
44
45    #[cfg(feature = "parser")]
46    pub fn from_string(s: crate::AzString) -> Self {
47        crate::parser2::new_from_str(s.as_str()).0
48    }
49
50    #[cfg(feature = "parser")]
51    pub fn from_string_with_warnings(
52        s: crate::AzString,
53    ) -> (Self, Vec<crate::parser2::CssParseWarnMsgOwned>) {
54        let (css, warnings) = crate::parser2::new_from_str(s.as_str());
55        (
56            css,
57            warnings
58                .into_iter()
59                .map(|w| crate::parser2::CssParseWarnMsgOwned {
60                    warning: w.warning.to_contained(),
61                    location: w.location,
62                })
63                .collect(),
64        )
65    }
66}
67
68#[derive(Debug, Default, PartialEq, PartialOrd, Clone)]
69#[repr(C)]
70pub struct Stylesheet {
71    /// The style rules making up the document - for example, de-duplicated CSS rules
72    pub rules: CssRuleBlockVec,
73}
74
75impl_vec!(
76    CssRuleBlock,
77    CssRuleBlockVec,
78    CssRuleBlockVecDestructor,
79    CssRuleBlockVecDestructorType
80);
81impl_vec_mut!(CssRuleBlock, CssRuleBlockVec);
82impl_vec_debug!(CssRuleBlock, CssRuleBlockVec);
83impl_vec_partialord!(CssRuleBlock, CssRuleBlockVec);
84impl_vec_clone!(CssRuleBlock, CssRuleBlockVec, CssRuleBlockVecDestructor);
85impl_vec_partialeq!(CssRuleBlock, CssRuleBlockVec);
86
87impl Stylesheet {
88    pub fn new(rules: Vec<CssRuleBlock>) -> Self {
89        Self {
90            rules: rules.into(),
91        }
92    }
93}
94
95impl From<Vec<CssRuleBlock>> for Stylesheet {
96    fn from(rules: Vec<CssRuleBlock>) -> Self {
97        Self {
98            rules: rules.into(),
99        }
100    }
101}
102
103/// Contains one parsed `key: value` pair, static or dynamic
104#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
105#[repr(C, u8)]
106pub enum CssDeclaration {
107    /// Static key-value pair, such as `width: 500px`
108    Static(CssProperty),
109    /// Dynamic key-value pair with default value, such as `width: [[ my_id | 500px ]]`
110    Dynamic(DynamicCssProperty),
111}
112
113impl CssDeclaration {
114    pub const fn new_static(prop: CssProperty) -> Self {
115        CssDeclaration::Static(prop)
116    }
117
118    pub const fn new_dynamic(prop: DynamicCssProperty) -> Self {
119        CssDeclaration::Dynamic(prop)
120    }
121
122    /// Returns the type of the property (i.e. the CSS key as a typed enum)
123    pub fn get_type(&self) -> CssPropertyType {
124        use self::CssDeclaration::*;
125        match self {
126            Static(s) => s.get_type(),
127            Dynamic(d) => d.default_value.get_type(),
128        }
129    }
130
131    /// Determines if the property will be inherited (applied to the children)
132    /// during the recursive application of the style on the DOM tree
133    pub fn is_inheritable(&self) -> bool {
134        use self::CssDeclaration::*;
135        match self {
136            Static(s) => s.get_type().is_inheritable(),
137            Dynamic(d) => d.is_inheritable(),
138        }
139    }
140
141    /// Returns whether this rule affects only styling properties or layout
142    /// properties (that could trigger a re-layout)
143    pub fn can_trigger_relayout(&self) -> bool {
144        use self::CssDeclaration::*;
145        match self {
146            Static(s) => s.get_type().can_trigger_relayout(),
147            Dynamic(d) => d.can_trigger_relayout(),
148        }
149    }
150
151    pub fn to_str(&self) -> String {
152        use self::CssDeclaration::*;
153        match self {
154            Static(s) => format!("{:?}", s),
155            Dynamic(d) => format!("var(--{}, {:?})", d.dynamic_id, d.default_value),
156        }
157    }
158}
159
160/// A `DynamicCssProperty` is a type of css property that can be changed on possibly
161/// every frame by the Rust code - for example to implement an `On::Hover` behaviour.
162///
163/// The syntax for such a property looks like this:
164///
165/// ```no_run,ignore
166/// #my_div {
167///    padding: var(--my_dynamic_property_id, 400px);
168/// }
169/// ```
170///
171/// Azul will register a dynamic property with the key "my_dynamic_property_id"
172/// and the default value of 400px. If the property gets overridden during one frame,
173/// the overridden property takes precedence.
174///
175/// At runtime the style is immutable (which is a performance optimization - if we
176/// can assume that the property never changes at runtime), we can do some optimizations on it.
177/// Dynamic style properties can also be used for animations and conditional styles
178/// (i.e. `hover`, `focus`, etc.), thereby leading to cleaner code, since all of these
179/// special cases now use one single API.
180#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
181#[repr(C)]
182pub struct DynamicCssProperty {
183    /// The stringified ID of this property, i.e. the `"my_id"` in `width: var(--my_id, 500px)`.
184    pub dynamic_id: AzString,
185    /// Default values for this properties - one single value can control multiple properties!
186    pub default_value: CssProperty,
187}
188
189#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
190#[repr(C, u8)] // necessary for ABI stability
191pub enum CssPropertyValue<T> {
192    Auto,
193    None,
194    Initial,
195    Inherit,
196    Revert,
197    Unset,
198    Exact(T),
199}
200
201pub trait PrintAsCssValue {
202    fn print_as_css_value(&self) -> String;
203}
204
205impl<T: PrintAsCssValue> CssPropertyValue<T> {
206    pub fn get_css_value_fmt(&self) -> String {
207        match self {
208            CssPropertyValue::Auto => format!("auto"),
209            CssPropertyValue::None => format!("none"),
210            CssPropertyValue::Initial => format!("initial"),
211            CssPropertyValue::Inherit => format!("inherit"),
212            CssPropertyValue::Revert => format!("revert"),
213            CssPropertyValue::Unset => format!("unset"),
214            CssPropertyValue::Exact(e) => e.print_as_css_value(),
215        }
216    }
217}
218
219impl<T: fmt::Display> fmt::Display for CssPropertyValue<T> {
220    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
221        use self::CssPropertyValue::*;
222        match self {
223            Auto => write!(f, "auto"),
224            None => write!(f, "none"),
225            Initial => write!(f, "initial"),
226            Inherit => write!(f, "inherit"),
227            Revert => write!(f, "revert"),
228            Unset => write!(f, "unset"),
229            Exact(e) => write!(f, "{}", e),
230        }
231    }
232}
233
234impl<T> From<T> for CssPropertyValue<T> {
235    fn from(c: T) -> Self {
236        CssPropertyValue::Exact(c)
237    }
238}
239
240impl<T> CssPropertyValue<T> {
241    /// Transforms a `CssPropertyValue<T>` into a `CssPropertyValue<U>` by applying a mapping
242    /// function
243    #[inline]
244    pub fn map_property<F: Fn(T) -> U, U>(self, map_fn: F) -> CssPropertyValue<U> {
245        match self {
246            CssPropertyValue::Exact(c) => CssPropertyValue::Exact(map_fn(c)),
247            CssPropertyValue::Auto => CssPropertyValue::Auto,
248            CssPropertyValue::None => CssPropertyValue::None,
249            CssPropertyValue::Initial => CssPropertyValue::Initial,
250            CssPropertyValue::Inherit => CssPropertyValue::Inherit,
251            CssPropertyValue::Revert => CssPropertyValue::Revert,
252            CssPropertyValue::Unset => CssPropertyValue::Unset,
253        }
254    }
255
256    #[inline]
257    pub fn get_property(&self) -> Option<&T> {
258        match self {
259            CssPropertyValue::Exact(c) => Some(c),
260            _ => None,
261        }
262    }
263
264    #[inline]
265    pub fn get_property_owned(self) -> Option<T> {
266        match self {
267            CssPropertyValue::Exact(c) => Some(c),
268            _ => None,
269        }
270    }
271
272    #[inline]
273    pub fn is_auto(&self) -> bool {
274        match self {
275            CssPropertyValue::Auto => true,
276            _ => false,
277        }
278    }
279
280    #[inline]
281    pub fn is_none(&self) -> bool {
282        match self {
283            CssPropertyValue::None => true,
284            _ => false,
285        }
286    }
287
288    #[inline]
289    pub fn is_initial(&self) -> bool {
290        match self {
291            CssPropertyValue::Initial => true,
292            _ => false,
293        }
294    }
295
296    #[inline]
297    pub fn is_inherit(&self) -> bool {
298        match self {
299            CssPropertyValue::Inherit => true,
300            _ => false,
301        }
302    }
303
304    #[inline]
305    pub fn is_revert(&self) -> bool {
306        match self {
307            CssPropertyValue::Revert => true,
308            _ => false,
309        }
310    }
311
312    #[inline]
313    pub fn is_unset(&self) -> bool {
314        match self {
315            CssPropertyValue::Unset => true,
316            _ => false,
317        }
318    }
319}
320
321impl<T: Default> CssPropertyValue<T> {
322    #[inline]
323    pub fn get_property_or_default(self) -> Option<T> {
324        match self {
325            CssPropertyValue::Auto | CssPropertyValue::Initial => Some(T::default()),
326            CssPropertyValue::Exact(c) => Some(c),
327            CssPropertyValue::None
328            | CssPropertyValue::Inherit
329            | CssPropertyValue::Revert
330            | CssPropertyValue::Unset => None,
331        }
332    }
333}
334
335impl<T: Default> Default for CssPropertyValue<T> {
336    #[inline]
337    fn default() -> Self {
338        CssPropertyValue::Exact(T::default())
339    }
340}
341
342impl DynamicCssProperty {
343    pub fn is_inheritable(&self) -> bool {
344        // Dynamic style properties should not be inheritable,
345        // since that could lead to bugs - you set a property in Rust, suddenly
346        // the wrong UI component starts to react because it was inherited.
347        false
348    }
349
350    pub fn can_trigger_relayout(&self) -> bool {
351        self.default_value.get_type().can_trigger_relayout()
352    }
353}
354
355/// One block of rules that applies a bunch of rules to a "path" in the style, i.e.
356/// `div#myid.myclass -> { ("justify-content", "center") }`
357///
358/// The `conditions` field contains @media/@lang/etc. conditions that must ALL be
359/// satisfied for this rule block to apply (from enclosing @-rule blocks).
360#[derive(Debug, Clone, PartialEq)]
361#[repr(C)]
362pub struct CssRuleBlock {
363    /// The css path (full selector) of the style ruleset
364    pub path: CssPath,
365    /// `"justify-content: center"` =>
366    /// `CssDeclaration::Static(CssProperty::JustifyContent(LayoutJustifyContent::Center))`
367    pub declarations: CssDeclarationVec,
368    /// Conditions from enclosing @-rules (@media, @lang, etc.) that must ALL be
369    /// satisfied for this rule block to apply. Empty = unconditional.
370    pub conditions: DynamicSelectorVec,
371}
372
373impl PartialOrd for CssRuleBlock {
374    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
375        // Compare by path and declarations only, conditions are not ordered
376        match self.path.partial_cmp(&other.path) {
377            Some(core::cmp::Ordering::Equal) => self.declarations.partial_cmp(&other.declarations),
378            ord => ord,
379        }
380    }
381}
382
383impl_vec!(
384    CssDeclaration,
385    CssDeclarationVec,
386    CssDeclarationVecDestructor,
387    CssDeclarationVecDestructorType
388);
389impl_vec_mut!(CssDeclaration, CssDeclarationVec);
390impl_vec_debug!(CssDeclaration, CssDeclarationVec);
391impl_vec_partialord!(CssDeclaration, CssDeclarationVec);
392impl_vec_ord!(CssDeclaration, CssDeclarationVec);
393impl_vec_clone!(
394    CssDeclaration,
395    CssDeclarationVec,
396    CssDeclarationVecDestructor
397);
398impl_vec_partialeq!(CssDeclaration, CssDeclarationVec);
399impl_vec_eq!(CssDeclaration, CssDeclarationVec);
400impl_vec_hash!(CssDeclaration, CssDeclarationVec);
401
402impl CssRuleBlock {
403    pub fn new(path: CssPath, declarations: Vec<CssDeclaration>) -> Self {
404        Self {
405            path,
406            declarations: declarations.into(),
407            conditions: DynamicSelectorVec::from_const_slice(&[]),
408        }
409    }
410
411    pub fn with_conditions(
412        path: CssPath,
413        declarations: Vec<CssDeclaration>,
414        conditions: Vec<crate::dynamic_selector::DynamicSelector>,
415    ) -> Self {
416        Self {
417            path,
418            declarations: declarations.into(),
419            conditions: conditions.into(),
420        }
421    }
422}
423
424pub type CssContentGroup<'a> = Vec<&'a CssPathSelector>;
425
426/// Signifies the type of a DOM node without carrying any associated data
427#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
428#[repr(C)]
429pub enum NodeTypeTag {
430    // Document structure
431    Html,
432    Head,
433    Body,
434
435    // Block-level elements
436    Div,
437    P,
438    Article,
439    Section,
440    Nav,
441    Aside,
442    Header,
443    Footer,
444    Main,
445    Figure,
446    FigCaption,
447
448    // Headings
449    H1,
450    H2,
451    H3,
452    H4,
453    H5,
454    H6,
455
456    // Inline text
457    Br,
458    Hr,
459    Pre,
460    BlockQuote,
461    Address,
462    Details,
463    Summary,
464    Dialog,
465
466    // Lists
467    Ul,
468    Ol,
469    Li,
470    Dl,
471    Dt,
472    Dd,
473    Menu,
474    MenuItem,
475    Dir,
476
477    // Tables
478    Table,
479    Caption,
480    THead,
481    TBody,
482    TFoot,
483    Tr,
484    Th,
485    Td,
486    ColGroup,
487    Col,
488
489    // Forms
490    Form,
491    FieldSet,
492    Legend,
493    Label,
494    Input,
495    Button,
496    Select,
497    OptGroup,
498    SelectOption,
499    TextArea,
500    Output,
501    Progress,
502    Meter,
503    DataList,
504
505    // Inline elements
506    Span,
507    A,
508    Em,
509    Strong,
510    B,
511    I,
512    U,
513    S,
514    Mark,
515    Del,
516    Ins,
517    Code,
518    Samp,
519    Kbd,
520    Var,
521    Cite,
522    Dfn,
523    Abbr,
524    Acronym,
525    Q,
526    Time,
527    Sub,
528    Sup,
529    Small,
530    Big,
531    Bdo,
532    Bdi,
533    Wbr,
534    Ruby,
535    Rt,
536    Rtc,
537    Rp,
538    Data,
539
540    // Embedded content
541    Canvas,
542    Object,
543    Param,
544    Embed,
545    Audio,
546    Video,
547    Source,
548    Track,
549    Map,
550    Area,
551    Svg,
552
553    // Metadata
554    Title,
555    Meta,
556    Link,
557    Script,
558    Style,
559    Base,
560
561    // Special
562    Text,
563    Img,
564    IFrame,
565    /// Icon element - resolved to actual content by IconProvider
566    Icon,
567
568    // Pseudo-elements
569    Before,
570    After,
571    Marker,
572    Placeholder,
573}
574
575#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
576pub enum NodeTypeTagParseError<'a> {
577    Invalid(&'a str),
578}
579
580impl<'a> fmt::Display for NodeTypeTagParseError<'a> {
581    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
582        match &self {
583            NodeTypeTagParseError::Invalid(e) => write!(f, "Invalid node type: {}", e),
584        }
585    }
586}
587
588#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
589pub enum NodeTypeTagParseErrorOwned {
590    Invalid(String),
591}
592
593impl<'a> NodeTypeTagParseError<'a> {
594    pub fn to_contained(&self) -> NodeTypeTagParseErrorOwned {
595        match self {
596            NodeTypeTagParseError::Invalid(s) => NodeTypeTagParseErrorOwned::Invalid(s.to_string()),
597        }
598    }
599}
600
601impl NodeTypeTagParseErrorOwned {
602    pub fn to_shared<'a>(&'a self) -> NodeTypeTagParseError<'a> {
603        match self {
604            NodeTypeTagParseErrorOwned::Invalid(s) => NodeTypeTagParseError::Invalid(s),
605        }
606    }
607}
608
609/// Parses the node type from a CSS string such as `"div"` => `NodeTypeTag::Div`
610impl NodeTypeTag {
611    pub fn from_str(css_key: &str) -> Result<Self, NodeTypeTagParseError> {
612        match css_key {
613            // Document structure
614            "html" => Ok(NodeTypeTag::Html),
615            "head" => Ok(NodeTypeTag::Head),
616            "body" => Ok(NodeTypeTag::Body),
617
618            // Block-level elements
619            "div" => Ok(NodeTypeTag::Div),
620            "p" => Ok(NodeTypeTag::P),
621            "article" => Ok(NodeTypeTag::Article),
622            "section" => Ok(NodeTypeTag::Section),
623            "nav" => Ok(NodeTypeTag::Nav),
624            "aside" => Ok(NodeTypeTag::Aside),
625            "header" => Ok(NodeTypeTag::Header),
626            "footer" => Ok(NodeTypeTag::Footer),
627            "main" => Ok(NodeTypeTag::Main),
628            "figure" => Ok(NodeTypeTag::Figure),
629            "figcaption" => Ok(NodeTypeTag::FigCaption),
630
631            // Headings
632            "h1" => Ok(NodeTypeTag::H1),
633            "h2" => Ok(NodeTypeTag::H2),
634            "h3" => Ok(NodeTypeTag::H3),
635            "h4" => Ok(NodeTypeTag::H4),
636            "h5" => Ok(NodeTypeTag::H5),
637            "h6" => Ok(NodeTypeTag::H6),
638
639            // Inline text
640            "br" => Ok(NodeTypeTag::Br),
641            "hr" => Ok(NodeTypeTag::Hr),
642            "pre" => Ok(NodeTypeTag::Pre),
643            "blockquote" => Ok(NodeTypeTag::BlockQuote),
644            "address" => Ok(NodeTypeTag::Address),
645            "details" => Ok(NodeTypeTag::Details),
646            "summary" => Ok(NodeTypeTag::Summary),
647            "dialog" => Ok(NodeTypeTag::Dialog),
648
649            // Lists
650            "ul" => Ok(NodeTypeTag::Ul),
651            "ol" => Ok(NodeTypeTag::Ol),
652            "li" => Ok(NodeTypeTag::Li),
653            "dl" => Ok(NodeTypeTag::Dl),
654            "dt" => Ok(NodeTypeTag::Dt),
655            "dd" => Ok(NodeTypeTag::Dd),
656            "menu" => Ok(NodeTypeTag::Menu),
657            "menuitem" => Ok(NodeTypeTag::MenuItem),
658            "dir" => Ok(NodeTypeTag::Dir),
659
660            // Tables
661            "table" => Ok(NodeTypeTag::Table),
662            "caption" => Ok(NodeTypeTag::Caption),
663            "thead" => Ok(NodeTypeTag::THead),
664            "tbody" => Ok(NodeTypeTag::TBody),
665            "tfoot" => Ok(NodeTypeTag::TFoot),
666            "tr" => Ok(NodeTypeTag::Tr),
667            "th" => Ok(NodeTypeTag::Th),
668            "td" => Ok(NodeTypeTag::Td),
669            "colgroup" => Ok(NodeTypeTag::ColGroup),
670            "col" => Ok(NodeTypeTag::Col),
671
672            // Forms
673            "form" => Ok(NodeTypeTag::Form),
674            "fieldset" => Ok(NodeTypeTag::FieldSet),
675            "legend" => Ok(NodeTypeTag::Legend),
676            "label" => Ok(NodeTypeTag::Label),
677            "input" => Ok(NodeTypeTag::Input),
678            "button" => Ok(NodeTypeTag::Button),
679            "select" => Ok(NodeTypeTag::Select),
680            "optgroup" => Ok(NodeTypeTag::OptGroup),
681            "option" => Ok(NodeTypeTag::SelectOption),
682            "textarea" => Ok(NodeTypeTag::TextArea),
683            "output" => Ok(NodeTypeTag::Output),
684            "progress" => Ok(NodeTypeTag::Progress),
685            "meter" => Ok(NodeTypeTag::Meter),
686            "datalist" => Ok(NodeTypeTag::DataList),
687
688            // Inline elements
689            "span" => Ok(NodeTypeTag::Span),
690            "a" => Ok(NodeTypeTag::A),
691            "em" => Ok(NodeTypeTag::Em),
692            "strong" => Ok(NodeTypeTag::Strong),
693            "b" => Ok(NodeTypeTag::B),
694            "i" => Ok(NodeTypeTag::I),
695            "u" => Ok(NodeTypeTag::U),
696            "s" => Ok(NodeTypeTag::S),
697            "mark" => Ok(NodeTypeTag::Mark),
698            "del" => Ok(NodeTypeTag::Del),
699            "ins" => Ok(NodeTypeTag::Ins),
700            "code" => Ok(NodeTypeTag::Code),
701            "samp" => Ok(NodeTypeTag::Samp),
702            "kbd" => Ok(NodeTypeTag::Kbd),
703            "var" => Ok(NodeTypeTag::Var),
704            "cite" => Ok(NodeTypeTag::Cite),
705            "dfn" => Ok(NodeTypeTag::Dfn),
706            "abbr" => Ok(NodeTypeTag::Abbr),
707            "acronym" => Ok(NodeTypeTag::Acronym),
708            "q" => Ok(NodeTypeTag::Q),
709            "time" => Ok(NodeTypeTag::Time),
710            "sub" => Ok(NodeTypeTag::Sub),
711            "sup" => Ok(NodeTypeTag::Sup),
712            "small" => Ok(NodeTypeTag::Small),
713            "big" => Ok(NodeTypeTag::Big),
714            "bdo" => Ok(NodeTypeTag::Bdo),
715            "bdi" => Ok(NodeTypeTag::Bdi),
716            "wbr" => Ok(NodeTypeTag::Wbr),
717            "ruby" => Ok(NodeTypeTag::Ruby),
718            "rt" => Ok(NodeTypeTag::Rt),
719            "rtc" => Ok(NodeTypeTag::Rtc),
720            "rp" => Ok(NodeTypeTag::Rp),
721            "data" => Ok(NodeTypeTag::Data),
722
723            // Embedded content
724            "canvas" => Ok(NodeTypeTag::Canvas),
725            "object" => Ok(NodeTypeTag::Object),
726            "param" => Ok(NodeTypeTag::Param),
727            "embed" => Ok(NodeTypeTag::Embed),
728            "audio" => Ok(NodeTypeTag::Audio),
729            "video" => Ok(NodeTypeTag::Video),
730            "source" => Ok(NodeTypeTag::Source),
731            "track" => Ok(NodeTypeTag::Track),
732            "map" => Ok(NodeTypeTag::Map),
733            "area" => Ok(NodeTypeTag::Area),
734            "svg" => Ok(NodeTypeTag::Svg),
735
736            // Metadata
737            "title" => Ok(NodeTypeTag::Title),
738            "meta" => Ok(NodeTypeTag::Meta),
739            "link" => Ok(NodeTypeTag::Link),
740            "script" => Ok(NodeTypeTag::Script),
741            "style" => Ok(NodeTypeTag::Style),
742            "base" => Ok(NodeTypeTag::Base),
743
744            // Special
745            "img" => Ok(NodeTypeTag::Img),
746            "iframe" => Ok(NodeTypeTag::IFrame),
747            "icon" => Ok(NodeTypeTag::Icon),
748
749            // Pseudo-elements (usually prefixed with ::)
750            "before" | "::before" => Ok(NodeTypeTag::Before),
751            "after" | "::after" => Ok(NodeTypeTag::After),
752            "marker" | "::marker" => Ok(NodeTypeTag::Marker),
753            "placeholder" | "::placeholder" => Ok(NodeTypeTag::Placeholder),
754
755            other => Err(NodeTypeTagParseError::Invalid(other)),
756        }
757    }
758}
759
760impl fmt::Display for NodeTypeTag {
761    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
762        match self {
763            // Document structure
764            NodeTypeTag::Html => write!(f, "html"),
765            NodeTypeTag::Head => write!(f, "head"),
766            NodeTypeTag::Body => write!(f, "body"),
767
768            // Block elements
769            NodeTypeTag::Div => write!(f, "div"),
770            NodeTypeTag::P => write!(f, "p"),
771            NodeTypeTag::Article => write!(f, "article"),
772            NodeTypeTag::Section => write!(f, "section"),
773            NodeTypeTag::Nav => write!(f, "nav"),
774            NodeTypeTag::Aside => write!(f, "aside"),
775            NodeTypeTag::Header => write!(f, "header"),
776            NodeTypeTag::Footer => write!(f, "footer"),
777            NodeTypeTag::Main => write!(f, "main"),
778            NodeTypeTag::Figure => write!(f, "figure"),
779            NodeTypeTag::FigCaption => write!(f, "figcaption"),
780
781            // Headings
782            NodeTypeTag::H1 => write!(f, "h1"),
783            NodeTypeTag::H2 => write!(f, "h2"),
784            NodeTypeTag::H3 => write!(f, "h3"),
785            NodeTypeTag::H4 => write!(f, "h4"),
786            NodeTypeTag::H5 => write!(f, "h5"),
787            NodeTypeTag::H6 => write!(f, "h6"),
788
789            // Text formatting
790            NodeTypeTag::Br => write!(f, "br"),
791            NodeTypeTag::Hr => write!(f, "hr"),
792            NodeTypeTag::Pre => write!(f, "pre"),
793            NodeTypeTag::BlockQuote => write!(f, "blockquote"),
794            NodeTypeTag::Address => write!(f, "address"),
795            NodeTypeTag::Details => write!(f, "details"),
796            NodeTypeTag::Summary => write!(f, "summary"),
797            NodeTypeTag::Dialog => write!(f, "dialog"),
798
799            // List elements
800            NodeTypeTag::Ul => write!(f, "ul"),
801            NodeTypeTag::Ol => write!(f, "ol"),
802            NodeTypeTag::Li => write!(f, "li"),
803            NodeTypeTag::Dl => write!(f, "dl"),
804            NodeTypeTag::Dt => write!(f, "dt"),
805            NodeTypeTag::Dd => write!(f, "dd"),
806            NodeTypeTag::Menu => write!(f, "menu"),
807            NodeTypeTag::MenuItem => write!(f, "menuitem"),
808            NodeTypeTag::Dir => write!(f, "dir"),
809
810            // Table elements
811            NodeTypeTag::Table => write!(f, "table"),
812            NodeTypeTag::Caption => write!(f, "caption"),
813            NodeTypeTag::THead => write!(f, "thead"),
814            NodeTypeTag::TBody => write!(f, "tbody"),
815            NodeTypeTag::TFoot => write!(f, "tfoot"),
816            NodeTypeTag::Tr => write!(f, "tr"),
817            NodeTypeTag::Th => write!(f, "th"),
818            NodeTypeTag::Td => write!(f, "td"),
819            NodeTypeTag::ColGroup => write!(f, "colgroup"),
820            NodeTypeTag::Col => write!(f, "col"),
821
822            // Form elements
823            NodeTypeTag::Form => write!(f, "form"),
824            NodeTypeTag::FieldSet => write!(f, "fieldset"),
825            NodeTypeTag::Legend => write!(f, "legend"),
826            NodeTypeTag::Label => write!(f, "label"),
827            NodeTypeTag::Input => write!(f, "input"),
828            NodeTypeTag::Button => write!(f, "button"),
829            NodeTypeTag::Select => write!(f, "select"),
830            NodeTypeTag::OptGroup => write!(f, "optgroup"),
831            NodeTypeTag::SelectOption => write!(f, "option"),
832            NodeTypeTag::TextArea => write!(f, "textarea"),
833            NodeTypeTag::Output => write!(f, "output"),
834            NodeTypeTag::Progress => write!(f, "progress"),
835            NodeTypeTag::Meter => write!(f, "meter"),
836            NodeTypeTag::DataList => write!(f, "datalist"),
837
838            // Inline elements
839            NodeTypeTag::Span => write!(f, "span"),
840            NodeTypeTag::A => write!(f, "a"),
841            NodeTypeTag::Em => write!(f, "em"),
842            NodeTypeTag::Strong => write!(f, "strong"),
843            NodeTypeTag::B => write!(f, "b"),
844            NodeTypeTag::I => write!(f, "i"),
845            NodeTypeTag::U => write!(f, "u"),
846            NodeTypeTag::S => write!(f, "s"),
847            NodeTypeTag::Mark => write!(f, "mark"),
848            NodeTypeTag::Del => write!(f, "del"),
849            NodeTypeTag::Ins => write!(f, "ins"),
850            NodeTypeTag::Code => write!(f, "code"),
851            NodeTypeTag::Samp => write!(f, "samp"),
852            NodeTypeTag::Kbd => write!(f, "kbd"),
853            NodeTypeTag::Var => write!(f, "var"),
854            NodeTypeTag::Cite => write!(f, "cite"),
855            NodeTypeTag::Dfn => write!(f, "dfn"),
856            NodeTypeTag::Abbr => write!(f, "abbr"),
857            NodeTypeTag::Acronym => write!(f, "acronym"),
858            NodeTypeTag::Q => write!(f, "q"),
859            NodeTypeTag::Time => write!(f, "time"),
860            NodeTypeTag::Sub => write!(f, "sub"),
861            NodeTypeTag::Sup => write!(f, "sup"),
862            NodeTypeTag::Small => write!(f, "small"),
863            NodeTypeTag::Big => write!(f, "big"),
864            NodeTypeTag::Bdo => write!(f, "bdo"),
865            NodeTypeTag::Bdi => write!(f, "bdi"),
866            NodeTypeTag::Wbr => write!(f, "wbr"),
867            NodeTypeTag::Ruby => write!(f, "ruby"),
868            NodeTypeTag::Rt => write!(f, "rt"),
869            NodeTypeTag::Rtc => write!(f, "rtc"),
870            NodeTypeTag::Rp => write!(f, "rp"),
871            NodeTypeTag::Data => write!(f, "data"),
872
873            // Embedded content
874            NodeTypeTag::Canvas => write!(f, "canvas"),
875            NodeTypeTag::Object => write!(f, "object"),
876            NodeTypeTag::Param => write!(f, "param"),
877            NodeTypeTag::Embed => write!(f, "embed"),
878            NodeTypeTag::Audio => write!(f, "audio"),
879            NodeTypeTag::Video => write!(f, "video"),
880            NodeTypeTag::Source => write!(f, "source"),
881            NodeTypeTag::Track => write!(f, "track"),
882            NodeTypeTag::Map => write!(f, "map"),
883            NodeTypeTag::Area => write!(f, "area"),
884            NodeTypeTag::Svg => write!(f, "svg"),
885
886            // Metadata
887            NodeTypeTag::Title => write!(f, "title"),
888            NodeTypeTag::Meta => write!(f, "meta"),
889            NodeTypeTag::Link => write!(f, "link"),
890            NodeTypeTag::Script => write!(f, "script"),
891            NodeTypeTag::Style => write!(f, "style"),
892            NodeTypeTag::Base => write!(f, "base"),
893
894            // Content elements
895            NodeTypeTag::Text => write!(f, "text"),
896            NodeTypeTag::Img => write!(f, "img"),
897            NodeTypeTag::IFrame => write!(f, "iframe"),
898            NodeTypeTag::Icon => write!(f, "icon"),
899
900            // Pseudo-elements
901            NodeTypeTag::Before => write!(f, "::before"),
902            NodeTypeTag::After => write!(f, "::after"),
903            NodeTypeTag::Marker => write!(f, "::marker"),
904            NodeTypeTag::Placeholder => write!(f, "::placeholder"),
905        }
906    }
907}
908
909/// Represents a full CSS path (i.e. the "div#id.class" selector belonging to
910///  a CSS "content group" (the following key-value block)).
911///
912/// ```no_run,ignore
913/// "#div > .my_class:focus" ==
914/// [
915///   CssPathSelector::Type(NodeTypeTag::Div),
916///   CssPathSelector::PseudoSelector(CssPathPseudoSelector::LimitChildren),
917///   CssPathSelector::Class("my_class"),
918///   CssPathSelector::PseudoSelector(CssPathPseudoSelector::Focus),
919/// ]
920#[derive(Clone, Hash, Default, PartialEq, Eq, PartialOrd, Ord)]
921#[repr(C)]
922pub struct CssPath {
923    pub selectors: CssPathSelectorVec,
924}
925
926impl_vec!(
927    CssPathSelector,
928    CssPathSelectorVec,
929    CssPathSelectorVecDestructor,
930    CssPathSelectorVecDestructorType
931);
932impl_vec_debug!(CssPathSelector, CssPathSelectorVec);
933impl_vec_partialord!(CssPathSelector, CssPathSelectorVec);
934impl_vec_ord!(CssPathSelector, CssPathSelectorVec);
935impl_vec_clone!(
936    CssPathSelector,
937    CssPathSelectorVec,
938    CssPathSelectorVecDestructor
939);
940impl_vec_partialeq!(CssPathSelector, CssPathSelectorVec);
941impl_vec_eq!(CssPathSelector, CssPathSelectorVec);
942impl_vec_hash!(CssPathSelector, CssPathSelectorVec);
943
944impl CssPath {
945    pub fn new(selectors: Vec<CssPathSelector>) -> Self {
946        Self {
947            selectors: selectors.into(),
948        }
949    }
950}
951
952impl fmt::Display for CssPath {
953    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
954        for selector in self.selectors.as_ref() {
955            write!(f, "{}", selector)?;
956        }
957        Ok(())
958    }
959}
960
961impl fmt::Debug for CssPath {
962    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
963        write!(f, "{}", self)
964    }
965}
966
967#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
968#[repr(C, u8)]
969pub enum CssPathSelector {
970    /// Represents the `*` selector
971    Global,
972    /// `div`, `p`, etc.
973    Type(NodeTypeTag),
974    /// `.something`
975    Class(AzString),
976    /// `#something`
977    Id(AzString),
978    /// `:something`
979    PseudoSelector(CssPathPseudoSelector),
980    /// Represents the `>` selector (direct child)
981    DirectChildren,
982    /// Represents the ` ` selector (descendant)
983    Children,
984    /// Represents the `+` selector (adjacent sibling)
985    AdjacentSibling,
986    /// Represents the `~` selector (general sibling)
987    GeneralSibling,
988}
989
990impl Default for CssPathSelector {
991    fn default() -> Self {
992        CssPathSelector::Global
993    }
994}
995
996impl fmt::Display for CssPathSelector {
997    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
998        use self::CssPathSelector::*;
999        match &self {
1000            Global => write!(f, "*"),
1001            Type(n) => write!(f, "{}", n),
1002            Class(c) => write!(f, ".{}", c),
1003            Id(i) => write!(f, "#{}", i),
1004            PseudoSelector(p) => write!(f, ":{}", p),
1005            DirectChildren => write!(f, ">"),
1006            Children => write!(f, " "),
1007            AdjacentSibling => write!(f, "+"),
1008            GeneralSibling => write!(f, "~"),
1009        }
1010    }
1011}
1012
1013#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
1014#[repr(C, u8)]
1015pub enum CssPathPseudoSelector {
1016    /// `:first`
1017    First,
1018    /// `:last`
1019    Last,
1020    /// `:nth-child`
1021    NthChild(CssNthChildSelector),
1022    /// `:hover` - mouse is over element
1023    Hover,
1024    /// `:active` - mouse is pressed and over element
1025    Active,
1026    /// `:focus` - element has received focus
1027    Focus,
1028    /// `:lang(de)` - element matches language
1029    Lang(AzString),
1030}
1031
1032#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
1033#[repr(C, u8)]
1034pub enum CssNthChildSelector {
1035    Number(u32),
1036    Even,
1037    Odd,
1038    Pattern(CssNthChildPattern),
1039}
1040
1041#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
1042#[repr(C)]
1043pub struct CssNthChildPattern {
1044    pub pattern_repeat: u32,
1045    pub offset: u32,
1046}
1047
1048impl fmt::Display for CssNthChildSelector {
1049    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1050        use self::CssNthChildSelector::*;
1051        match &self {
1052            Number(u) => write!(f, "{}", u),
1053            Even => write!(f, "even"),
1054            Odd => write!(f, "odd"),
1055            Pattern(p) => write!(f, "{}n + {}", p.pattern_repeat, p.offset),
1056        }
1057    }
1058}
1059
1060impl fmt::Display for CssPathPseudoSelector {
1061    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1062        use self::CssPathPseudoSelector::*;
1063        match &self {
1064            First => write!(f, "first"),
1065            Last => write!(f, "last"),
1066            NthChild(u) => write!(f, "nth-child({})", u),
1067            Hover => write!(f, "hover"),
1068            Active => write!(f, "active"),
1069            Focus => write!(f, "focus"),
1070            Lang(lang) => write!(f, "lang({})", lang.as_str()),
1071        }
1072    }
1073}
1074
1075impl Css {
1076    /// Creates a new, empty CSS with no stylesheets
1077    pub fn empty() -> Self {
1078        Default::default()
1079    }
1080
1081    pub fn sort_by_specificity(&mut self) {
1082        self.stylesheets
1083            .as_mut()
1084            .iter_mut()
1085            .for_each(|s| s.sort_by_specificity());
1086    }
1087
1088    pub fn rules<'a>(&'a self) -> RuleIterator<'a> {
1089        RuleIterator {
1090            current_stylesheet: 0,
1091            current_rule: 0,
1092            css: self,
1093        }
1094    }
1095}
1096
1097pub struct RuleIterator<'a> {
1098    current_stylesheet: usize,
1099    current_rule: usize,
1100    css: &'a Css,
1101}
1102
1103impl<'a> Iterator for RuleIterator<'a> {
1104    type Item = &'a CssRuleBlock;
1105    fn next(&mut self) -> Option<&'a CssRuleBlock> {
1106        let current_stylesheet = self.css.stylesheets.get(self.current_stylesheet)?;
1107        match current_stylesheet.rules.get(self.current_rule) {
1108            Some(s) => {
1109                self.current_rule += 1;
1110                Some(s)
1111            }
1112            None => {
1113                self.current_rule = 0;
1114                self.current_stylesheet += 1;
1115                self.next()
1116            }
1117        }
1118    }
1119}
1120
1121impl Stylesheet {
1122    /// Creates a new stylesheet with no style rules.
1123    pub fn empty() -> Self {
1124        Default::default()
1125    }
1126
1127    /// Sort the style rules by their weight, so that the rules are applied in the correct order.
1128    /// Should always be called when a new style is loaded from an external source.
1129    pub fn sort_by_specificity(&mut self) {
1130        self.rules
1131            .as_mut()
1132            .sort_by(|a, b| get_specificity(&a.path).cmp(&get_specificity(&b.path)));
1133    }
1134}
1135
1136/// Returns specificity of the given css path. Further information can be found on
1137/// [the w3 website](http://www.w3.org/TR/selectors/#specificity).
1138pub fn get_specificity(path: &CssPath) -> (usize, usize, usize, usize) {
1139    let id_count = path
1140        .selectors
1141        .iter()
1142        .filter(|x| {
1143            if let CssPathSelector::Id(_) = x {
1144                true
1145            } else {
1146                false
1147            }
1148        })
1149        .count();
1150    let class_count = path
1151        .selectors
1152        .iter()
1153        .filter(|x| {
1154            if let CssPathSelector::Class(_) = x {
1155                true
1156            } else {
1157                false
1158            }
1159        })
1160        .count();
1161    let div_count = path
1162        .selectors
1163        .iter()
1164        .filter(|x| {
1165            if let CssPathSelector::Type(_) = x {
1166                true
1167            } else {
1168                false
1169            }
1170        })
1171        .count();
1172    (id_count, class_count, div_count, path.selectors.len())
1173}
1174
1175// --- Formatting ---
1176
1177// High-level CSS to Rust code generation
1178
1179pub fn css_to_rust_code(css: &Css) -> String {
1180    let mut output = String::new();
1181
1182    output.push_str("const CSS: Css = Css {\r\n");
1183    output.push_str("\tstylesheets: [\r\n");
1184
1185    for stylesheet in css.stylesheets.iter() {
1186        output.push_str("\t\tStylesheet {\r\n");
1187        output.push_str("\t\t\trules: [\r\n");
1188
1189        for block in stylesheet.rules.iter() {
1190            output.push_str("\t\t\t\tCssRuleBlock: {\r\n");
1191            output.push_str(&format!(
1192                "\t\t\t\t\tpath: {},\r\n",
1193                print_block_path(&block.path, 5)
1194            ));
1195
1196            output.push_str("\t\t\t\t\tdeclarations: [\r\n");
1197
1198            for declaration in block.declarations.iter() {
1199                output.push_str(&format!(
1200                    "\t\t\t\t\t\t{},\r\n",
1201                    print_declaration(declaration, 6)
1202                ));
1203            }
1204
1205            output.push_str("\t\t\t\t\t]\r\n");
1206
1207            output.push_str("\t\t\t\t},\r\n");
1208        }
1209
1210        output.push_str("\t\t\t]\r\n");
1211        output.push_str("\t\t},\r\n");
1212    }
1213
1214    output.push_str("\t]\r\n");
1215    output.push_str("};");
1216
1217    let output = output.replace("\t", "    ");
1218
1219    output
1220}
1221
1222pub fn format_node_type(n: &NodeTypeTag) -> &'static str {
1223    match n {
1224        // Document structure
1225        NodeTypeTag::Html => "NodeTypeTag::Html",
1226        NodeTypeTag::Head => "NodeTypeTag::Head",
1227        NodeTypeTag::Body => "NodeTypeTag::Body",
1228
1229        // Block elements
1230        NodeTypeTag::Div => "NodeTypeTag::Div",
1231        NodeTypeTag::P => "NodeTypeTag::P",
1232        NodeTypeTag::Article => "NodeTypeTag::Article",
1233        NodeTypeTag::Section => "NodeTypeTag::Section",
1234        NodeTypeTag::Nav => "NodeTypeTag::Nav",
1235        NodeTypeTag::Aside => "NodeTypeTag::Aside",
1236        NodeTypeTag::Header => "NodeTypeTag::Header",
1237        NodeTypeTag::Footer => "NodeTypeTag::Footer",
1238        NodeTypeTag::Main => "NodeTypeTag::Main",
1239        NodeTypeTag::Figure => "NodeTypeTag::Figure",
1240        NodeTypeTag::FigCaption => "NodeTypeTag::FigCaption",
1241
1242        // Headings
1243        NodeTypeTag::H1 => "NodeTypeTag::H1",
1244        NodeTypeTag::H2 => "NodeTypeTag::H2",
1245        NodeTypeTag::H3 => "NodeTypeTag::H3",
1246        NodeTypeTag::H4 => "NodeTypeTag::H4",
1247        NodeTypeTag::H5 => "NodeTypeTag::H5",
1248        NodeTypeTag::H6 => "NodeTypeTag::H6",
1249
1250        // Text formatting
1251        NodeTypeTag::Br => "NodeTypeTag::Br",
1252        NodeTypeTag::Hr => "NodeTypeTag::Hr",
1253        NodeTypeTag::Pre => "NodeTypeTag::Pre",
1254        NodeTypeTag::BlockQuote => "NodeTypeTag::BlockQuote",
1255        NodeTypeTag::Address => "NodeTypeTag::Address",
1256        NodeTypeTag::Details => "NodeTypeTag::Details",
1257        NodeTypeTag::Summary => "NodeTypeTag::Summary",
1258        NodeTypeTag::Dialog => "NodeTypeTag::Dialog",
1259
1260        // List elements
1261        NodeTypeTag::Ul => "NodeTypeTag::Ul",
1262        NodeTypeTag::Ol => "NodeTypeTag::Ol",
1263        NodeTypeTag::Li => "NodeTypeTag::Li",
1264        NodeTypeTag::Dl => "NodeTypeTag::Dl",
1265        NodeTypeTag::Dt => "NodeTypeTag::Dt",
1266        NodeTypeTag::Dd => "NodeTypeTag::Dd",
1267        NodeTypeTag::Menu => "NodeTypeTag::Menu",
1268        NodeTypeTag::MenuItem => "NodeTypeTag::MenuItem",
1269        NodeTypeTag::Dir => "NodeTypeTag::Dir",
1270
1271        // Table elements
1272        NodeTypeTag::Table => "NodeTypeTag::Table",
1273        NodeTypeTag::Caption => "NodeTypeTag::Caption",
1274        NodeTypeTag::THead => "NodeTypeTag::THead",
1275        NodeTypeTag::TBody => "NodeTypeTag::TBody",
1276        NodeTypeTag::TFoot => "NodeTypeTag::TFoot",
1277        NodeTypeTag::Tr => "NodeTypeTag::Tr",
1278        NodeTypeTag::Th => "NodeTypeTag::Th",
1279        NodeTypeTag::Td => "NodeTypeTag::Td",
1280        NodeTypeTag::ColGroup => "NodeTypeTag::ColGroup",
1281        NodeTypeTag::Col => "NodeTypeTag::Col",
1282
1283        // Form elements
1284        NodeTypeTag::Form => "NodeTypeTag::Form",
1285        NodeTypeTag::FieldSet => "NodeTypeTag::FieldSet",
1286        NodeTypeTag::Legend => "NodeTypeTag::Legend",
1287        NodeTypeTag::Label => "NodeTypeTag::Label",
1288        NodeTypeTag::Input => "NodeTypeTag::Input",
1289        NodeTypeTag::Button => "NodeTypeTag::Button",
1290        NodeTypeTag::Select => "NodeTypeTag::Select",
1291        NodeTypeTag::OptGroup => "NodeTypeTag::OptGroup",
1292        NodeTypeTag::SelectOption => "NodeTypeTag::SelectOption",
1293        NodeTypeTag::TextArea => "NodeTypeTag::TextArea",
1294        NodeTypeTag::Output => "NodeTypeTag::Output",
1295        NodeTypeTag::Progress => "NodeTypeTag::Progress",
1296        NodeTypeTag::Meter => "NodeTypeTag::Meter",
1297        NodeTypeTag::DataList => "NodeTypeTag::DataList",
1298
1299        // Inline elements
1300        NodeTypeTag::Span => "NodeTypeTag::Span",
1301        NodeTypeTag::A => "NodeTypeTag::A",
1302        NodeTypeTag::Em => "NodeTypeTag::Em",
1303        NodeTypeTag::Strong => "NodeTypeTag::Strong",
1304        NodeTypeTag::B => "NodeTypeTag::B",
1305        NodeTypeTag::I => "NodeTypeTag::I",
1306        NodeTypeTag::U => "NodeTypeTag::U",
1307        NodeTypeTag::S => "NodeTypeTag::S",
1308        NodeTypeTag::Mark => "NodeTypeTag::Mark",
1309        NodeTypeTag::Del => "NodeTypeTag::Del",
1310        NodeTypeTag::Ins => "NodeTypeTag::Ins",
1311        NodeTypeTag::Code => "NodeTypeTag::Code",
1312        NodeTypeTag::Samp => "NodeTypeTag::Samp",
1313        NodeTypeTag::Kbd => "NodeTypeTag::Kbd",
1314        NodeTypeTag::Var => "NodeTypeTag::Var",
1315        NodeTypeTag::Cite => "NodeTypeTag::Cite",
1316        NodeTypeTag::Dfn => "NodeTypeTag::Dfn",
1317        NodeTypeTag::Abbr => "NodeTypeTag::Abbr",
1318        NodeTypeTag::Acronym => "NodeTypeTag::Acronym",
1319        NodeTypeTag::Q => "NodeTypeTag::Q",
1320        NodeTypeTag::Time => "NodeTypeTag::Time",
1321        NodeTypeTag::Sub => "NodeTypeTag::Sub",
1322        NodeTypeTag::Sup => "NodeTypeTag::Sup",
1323        NodeTypeTag::Small => "NodeTypeTag::Small",
1324        NodeTypeTag::Big => "NodeTypeTag::Big",
1325        NodeTypeTag::Bdo => "NodeTypeTag::Bdo",
1326        NodeTypeTag::Bdi => "NodeTypeTag::Bdi",
1327        NodeTypeTag::Wbr => "NodeTypeTag::Wbr",
1328        NodeTypeTag::Ruby => "NodeTypeTag::Ruby",
1329        NodeTypeTag::Rt => "NodeTypeTag::Rt",
1330        NodeTypeTag::Rtc => "NodeTypeTag::Rtc",
1331        NodeTypeTag::Rp => "NodeTypeTag::Rp",
1332        NodeTypeTag::Data => "NodeTypeTag::Data",
1333
1334        // Embedded content
1335        NodeTypeTag::Canvas => "NodeTypeTag::Canvas",
1336        NodeTypeTag::Object => "NodeTypeTag::Object",
1337        NodeTypeTag::Param => "NodeTypeTag::Param",
1338        NodeTypeTag::Embed => "NodeTypeTag::Embed",
1339        NodeTypeTag::Audio => "NodeTypeTag::Audio",
1340        NodeTypeTag::Video => "NodeTypeTag::Video",
1341        NodeTypeTag::Source => "NodeTypeTag::Source",
1342        NodeTypeTag::Track => "NodeTypeTag::Track",
1343        NodeTypeTag::Map => "NodeTypeTag::Map",
1344        NodeTypeTag::Area => "NodeTypeTag::Area",
1345        NodeTypeTag::Svg => "NodeTypeTag::Svg",
1346
1347        // Metadata
1348        NodeTypeTag::Title => "NodeTypeTag::Title",
1349        NodeTypeTag::Meta => "NodeTypeTag::Meta",
1350        NodeTypeTag::Link => "NodeTypeTag::Link",
1351        NodeTypeTag::Script => "NodeTypeTag::Script",
1352        NodeTypeTag::Style => "NodeTypeTag::Style",
1353        NodeTypeTag::Base => "NodeTypeTag::Base",
1354
1355        // Content elements
1356        NodeTypeTag::Text => "NodeTypeTag::Text",
1357        NodeTypeTag::Img => "NodeTypeTag::Img",
1358        NodeTypeTag::IFrame => "NodeTypeTag::IFrame",
1359        NodeTypeTag::Icon => "NodeTypeTag::Icon",
1360
1361        // Pseudo-elements
1362        NodeTypeTag::Before => "NodeTypeTag::Before",
1363        NodeTypeTag::After => "NodeTypeTag::After",
1364        NodeTypeTag::Marker => "NodeTypeTag::Marker",
1365        NodeTypeTag::Placeholder => "NodeTypeTag::Placeholder",
1366    }
1367}
1368
1369pub fn print_block_path(path: &CssPath, tabs: usize) -> String {
1370    let t = String::from("    ").repeat(tabs);
1371    let t1 = String::from("    ").repeat(tabs + 1);
1372
1373    format!(
1374        "CssPath {{\r\n{}selectors: {}\r\n{}}}",
1375        t1,
1376        format_selectors(path.selectors.as_ref(), tabs + 1),
1377        t
1378    )
1379}
1380
1381pub fn format_selectors(selectors: &[CssPathSelector], tabs: usize) -> String {
1382    let t = String::from("    ").repeat(tabs);
1383    let t1 = String::from("    ").repeat(tabs + 1);
1384
1385    let selectors_formatted = selectors
1386        .iter()
1387        .map(|s| format!("{}{},", t1, format_single_selector(s, tabs + 1)))
1388        .collect::<Vec<String>>()
1389        .join("\r\n");
1390
1391    format!("vec![\r\n{}\r\n{}].into()", selectors_formatted, t)
1392}
1393
1394pub fn format_single_selector(p: &CssPathSelector, _tabs: usize) -> String {
1395    match p {
1396        CssPathSelector::Global => format!("CssPathSelector::Global"),
1397        CssPathSelector::Type(ntp) => format!("CssPathSelector::Type({})", format_node_type(ntp)),
1398        CssPathSelector::Class(class) => {
1399            format!("CssPathSelector::Class(String::from({:?}))", class)
1400        }
1401        CssPathSelector::Id(id) => format!("CssPathSelector::Id(String::from({:?}))", id),
1402        CssPathSelector::PseudoSelector(cps) => format!(
1403            "CssPathSelector::PseudoSelector({})",
1404            format_pseudo_selector_type(cps)
1405        ),
1406        CssPathSelector::DirectChildren => format!("CssPathSelector::DirectChildren"),
1407        CssPathSelector::Children => format!("CssPathSelector::Children"),
1408        CssPathSelector::AdjacentSibling => format!("CssPathSelector::AdjacentSibling"),
1409        CssPathSelector::GeneralSibling => format!("CssPathSelector::GeneralSibling"),
1410    }
1411}
1412
1413pub fn format_pseudo_selector_type(p: &CssPathPseudoSelector) -> String {
1414    match p {
1415        CssPathPseudoSelector::First => format!("CssPathPseudoSelector::First"),
1416        CssPathPseudoSelector::Last => format!("CssPathPseudoSelector::Last"),
1417        CssPathPseudoSelector::NthChild(n) => format!(
1418            "CssPathPseudoSelector::NthChild({})",
1419            format_nth_child_selector(n)
1420        ),
1421        CssPathPseudoSelector::Hover => format!("CssPathPseudoSelector::Hover"),
1422        CssPathPseudoSelector::Active => format!("CssPathPseudoSelector::Active"),
1423        CssPathPseudoSelector::Focus => format!("CssPathPseudoSelector::Focus"),
1424        CssPathPseudoSelector::Lang(lang) => format!(
1425            "CssPathPseudoSelector::Lang(AzString::from_const_str(\"{}\"))",
1426            lang.as_str()
1427        ),
1428    }
1429}
1430
1431pub fn format_nth_child_selector(n: &CssNthChildSelector) -> String {
1432    match n {
1433        CssNthChildSelector::Number(num) => format!("CssNthChildSelector::Number({})", num),
1434        CssNthChildSelector::Even => format!("CssNthChildSelector::Even"),
1435        CssNthChildSelector::Odd => format!("CssNthChildSelector::Odd"),
1436        CssNthChildSelector::Pattern(CssNthChildPattern {
1437            pattern_repeat,
1438            offset,
1439        }) => format!(
1440            "CssNthChildSelector::Pattern(CssNthChildPattern {{ pattern_repeat: {}, offset: {} }})",
1441            pattern_repeat, offset
1442        ),
1443    }
1444}
1445
1446pub fn print_declaration(decl: &CssDeclaration, tabs: usize) -> String {
1447    match decl {
1448        CssDeclaration::Static(s) => format!(
1449            "CssDeclaration::Static({})",
1450            format_static_css_prop(s, tabs)
1451        ),
1452        CssDeclaration::Dynamic(d) => format!(
1453            "CssDeclaration::Dynamic({})",
1454            format_dynamic_css_prop(d, tabs)
1455        ),
1456    }
1457}
1458
1459pub fn format_dynamic_css_prop(decl: &DynamicCssProperty, tabs: usize) -> String {
1460    let t = String::from("    ").repeat(tabs);
1461    format!(
1462        "DynamicCssProperty {{\r\n{}    dynamic_id: {:?},\r\n{}    default_value: {},\r\n{}}}",
1463        t,
1464        decl.dynamic_id,
1465        t,
1466        format_static_css_prop(&decl.default_value, tabs + 1),
1467        t
1468    )
1469}