layoutcss_parser/
lib.rs

1pub mod builder;
2pub mod dev;
3pub mod classes;
4pub mod reset;
5pub mod components;
6pub mod config;
7pub mod harmonic;
8pub mod media_query;
9pub mod parser;
10pub mod utilities;
11
12use builder::LayoutElement;
13use config::LayoutStyleConfig;
14use indoc::formatdoc;
15use media_query::MediaQuery;
16use std::collections::{HashMap, HashSet};
17
18pub fn generate_final_css(
19    css_set: &HashSet<String>,
20    mq_rules: &HashMap<MediaQuery, HashSet<String>>,
21) -> String {
22    // We join all the classic rules (non mq) into a single String
23    let mut final_css = css_set.iter().cloned().collect::<Vec<String>>().join("");
24
25    // Now we have to process the media queries,
26    // we get the keys in the correct order to process them to get a
27    // correct css specificty
28    let mut ordered_mq_keys: Vec<MediaQuery> = mq_rules.keys().cloned().collect();
29
30    // Sorts according to the 'cmp' method in MediaQuery
31    ordered_mq_keys.sort();
32
33    for key in ordered_mq_keys {
34        if let Some(hash_set) = mq_rules.get(&key) {
35            // replace selector in media query to increase specificity
36            // to avoid conflict
37            let rule_with_modified_selector: String = hash_set
38                .iter()
39                .map(|s| match &key {
40                    MediaQuery::InferiorOrEqualTo(breakpoint) => {
41                        s.replace("[layout", format!("[layout{breakpoint}px").as_str())
42                    }
43                    MediaQuery::SuperiorTo(breakpoint, attribute_value) => s.replace(
44                        "-l[layout",
45                        format!("-l[layout{breakpoint}px=\"{attribute_value}\"][layout")
46                            .as_str(),
47                    ),
48                })
49                .collect::<Vec<String>>()
50                .join("");
51            // wrap the previous modified rule
52            // into a media query
53            let wrapped_in_mq = match key {
54                MediaQuery::SuperiorTo(breakpoint, _) => formatdoc!(
55                    r#"
56                            @media (width > {breakpoint}px) {{
57                                {rule_with_modified_selector}
58                            }}
59                            "#
60                ),
61                MediaQuery::InferiorOrEqualTo(breakpoint) => formatdoc!(
62                    r#"
63                            @media (width <= {breakpoint}px) {{
64                                {rule_with_modified_selector}
65                            }}"#
66                ),
67            };
68            // at this rules to the final css
69            final_css.push_str(wrapped_in_mq.as_str());
70        }
71    }
72    final_css
73}
74
75/// This function can take css_set and css_mq_rules if you already have
76/// process a string before and you want to avoid to get duplicate values
77/// this way the new css will contains the previous css plus new rules from the new string
78pub fn get_css_from_string<'a>(
79    text: &'a String,
80    previous_css_rules: Option<&mut HashSet<String>>,
81    previous_css_mq_rules: Option<&mut HashMap<MediaQuery, HashSet<String>>>,
82    layout_style_config: &LayoutStyleConfig,
83) -> String {
84    let mut layout_elements: HashSet<LayoutElement> = HashSet::new();
85
86    let mut local_css_rules: HashSet<String> = HashSet::new();
87    let css_rules = match previous_css_rules {
88        Some(x) => x,
89        None => &mut local_css_rules,
90    };
91
92    css_rules.insert(reset::reset_css(&layout_style_config));
93    if layout_style_config.dev {
94        css_rules.insert(dev::DEV_CSS.to_string());
95    }
96
97    let mut local_css_mq_rules: HashMap<MediaQuery, HashSet<String>> = HashMap::new();
98    let css_mq_rules = match previous_css_mq_rules {
99        Some(x) => x,
100        None => &mut local_css_mq_rules,
101    };
102    let mut parser = parser::Parser::new(text);
103    parser.parse(&mut layout_elements);
104    for element in layout_elements.drain() {
105        element.insert_css(layout_style_config.harmonic_ratio, css_rules, css_mq_rules);
106    }
107    generate_final_css(&css_rules, &css_mq_rules)
108}