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