layoutcss_parser/
builder.rs

1use std::collections::{HashMap, HashSet};
2use std::str::FromStr;
3
4use crate::classes::LayoutClass;
5use crate::components::Component;
6use crate::media_query::MediaQuery;
7
8#[derive(Hash, PartialEq, Eq, Debug)]
9pub enum LayoutElement<'a> {
10    LayoutComponent(Component<'a>, Option<MediaQuery>),
11    LayoutUtility(LayoutClass<'a>, Option<MediaQuery>),
12}
13
14impl<'a> LayoutElement<'a> {
15    /// Generate the css of the layout element
16    /// and insert it inside the Hashset passed.
17    /// This method consumes the LayoutElement, because we dont need it anymore
18    /// after we get the css.
19    pub fn insert_css(
20        self,
21        harmonic_ratio: f64,
22        set: &mut HashSet<String>,
23        media_queries_rules: &mut HashMap<MediaQuery, HashSet<String>>,
24    ) {
25        //we can handle here if we should pass the hashset from the css_mq or from css
26        //to manage media queries
27        match self {
28            Self::LayoutComponent(component, None) => component.insert_css(harmonic_ratio, set),
29            //here pass the set from the hashmap
30            Self::LayoutComponent(component, Some(mq)) => {
31                let val = media_queries_rules.entry(mq).or_insert_with(HashSet::new);
32                component.insert_css(harmonic_ratio, val);
33            }
34            Self::LayoutUtility(class, None) => class.insert_css(harmonic_ratio, set),
35            //here pass the set from the hashmap
36            Self::LayoutUtility(class, Some(mq)) => {
37                let val = media_queries_rules.entry(mq).or_insert_with(HashSet::new);
38                class.insert_css(harmonic_ratio, val);
39            }
40        }
41    }
42}
43
44pub fn generate<'a>(
45    tag_name: &'a str,
46    layout_attribute: Option<&'a str>,
47    media_query: Option<MediaQuery>,
48    set: &mut HashSet<LayoutElement<'a>>,
49) {
50    let mut component = Component::from_str(tag_name);
51    if let Some(layout_attribute) = layout_attribute {
52        for class in layout_attribute.split_whitespace() {
53            let layout_class = LayoutClass::try_from(class);
54
55            // If a LayoutClass has been created from the previous step
56            if let Ok(current_class) = layout_class {
57                // In the case where a component has been created from
58                // the first line of this function
59                // here we will handle all LayoutClass attached to a component
60                // to then modify the component accordingly
61                if let Ok(comp) = &mut component {
62                    match current_class {
63                        LayoutClass::MaxWidth(v) => {
64                            match comp {
65                                Component::Box { max_width, .. } => *max_width = Some(v),
66                                Component::Center { max_width, .. } => *max_width = Some(v),
67                                _ => {}
68                            };
69                        }
70                        LayoutClass::MinCellWidth(v) => {
71                            match comp {
72                                Component::Grid { min_cell_width, .. } => *min_cell_width = Some(v),
73                                _ => {}
74                            };
75                        }
76                        LayoutClass::MinCols(v) => {
77                            match comp {
78                                Component::Grid { min_cols, .. } => *min_cols = Some(v),
79                                _ => {}
80                            };
81                        }
82                        LayoutClass::MaxCols(v) => {
83                            match comp {
84                                Component::Grid { max_cols, .. } => *max_cols = Some(v),
85                                _ => {}
86                            };
87                        }
88                        LayoutClass::Recursive => {
89                            match comp {
90                                Component::Stack { recursive, .. } => *recursive = true,
91                                Component::Center { recursive, .. } => *recursive = true,
92                                _ => {}
93                            };
94                        }
95                        LayoutClass::Reverse => {
96                            match comp {
97                                Component::Switcher { reverse, .. } => *reverse = true,
98                                Component::Sidebar { reverse, .. } => *reverse = true,
99                                _ => {}
100                            };
101                        }
102                        LayoutClass::Screen => {
103                            match comp {
104                                Component::Extender { screen, .. } => *screen = true,
105                                _ => {}
106                            };
107                        }
108                        LayoutClass::TwinWidth => {
109                            match comp {
110                                Component::Row { twin_width, .. } => *twin_width = true,
111                                _ => {}
112                            };
113                        }
114                        LayoutClass::NoWrap => {
115                            match comp {
116                                Component::Row { nowrap, .. } => *nowrap = true,
117                                _ => {}
118                            };
119                        }
120                        LayoutClass::HideBar => {
121                            match comp {
122                                Component::Slider { hide_bar, .. } => *hide_bar = true,
123                                _ => {}
124                            };
125                        }
126                        LayoutClass::Grow => {
127                            match comp {
128                                Component::Box { grow, .. } => *grow = true,
129                                _ => {}
130                            };
131                        }
132                        LayoutClass::Template(v) => {
133                            match comp {
134                                Component::Area { template, .. } => *template = Some(v),
135                                _ => {}
136                            };
137                        }
138                        LayoutClass::Row(v) => {
139                            match comp {
140                                Component::Area { rows, .. } => rows.push(v),
141                                _ => {}
142                            };
143                        }
144                        LayoutClass::Col(v) => {
145                            match comp {
146                                Component::Area { cols, .. } => cols.push(v),
147                                _ => {}
148                            };
149                        }
150                        LayoutClass::Gap(v) => {
151                            match comp {
152                                Component::Area { gap, .. } => *gap = Some(v),
153                                Component::Grid { gap, .. } => *gap = Some(v),
154                                Component::Icon { gap, .. } => *gap = Some(v),
155                                Component::Row { gap, .. } => *gap = Some(v),
156                                Component::Rack { gap, .. } => *gap = Some(v),
157                                Component::Sidebar { gap, .. } => *gap = Some(v),
158                                Component::Slider { gap, .. } => *gap = Some(v),
159                                Component::Stack { gap, .. } => *gap = Some(v),
160                                Component::Switcher { gap, .. } => *gap = Some(v),
161                                _ => {}
162                            };
163                        }
164                        LayoutClass::GapX(v) => {
165                            match comp {
166                                Component::Area { gap_x, .. } => *gap_x = Some(v),
167                                Component::Grid { gap_x, .. } => *gap_x = Some(v),
168                                Component::Row { gap_x, .. } => *gap_x = Some(v),
169                                Component::Sidebar { gap_x, .. } => *gap_x = Some(v),
170                                Component::Switcher { gap_x, .. } => *gap_x = Some(v),
171                                _ => {}
172                            };
173                        }
174                        LayoutClass::GapY(v) => {
175                            match comp {
176                                Component::Area { gap_y, .. } => *gap_y = Some(v),
177                                Component::Grid { gap_y, .. } => *gap_y = Some(v),
178                                Component::Row { gap_y, .. } => *gap_y = Some(v),
179                                Component::Sidebar { gap_y, .. } => *gap_y = Some(v),
180                                Component::Switcher { gap_y, .. } => *gap_y = Some(v),
181                                _ => {}
182                            };
183                        }
184                        LayoutClass::GapDir(v) => {
185                            match comp {
186                                Component::Icon { gap_dir, .. } => *gap_dir = Some(v),
187                                _ => {}
188                            };
189                        }
190                        LayoutClass::Scale(v) => {
191                            match comp {
192                                Component::Icon { scale, .. } => *scale = Some(v),
193                                _ => {}
194                            };
195                        }
196                        LayoutClass::Align(v) => {
197                            match comp {
198                                Component::Icon { align, .. } => *align = Some(v),
199                                Component::Row { align, .. } => *align = Some(v),
200                                _ => {}
201                            };
202                        }
203                        LayoutClass::Justify(v) => {
204                            match comp {
205                                Component::Row { justify, .. } => *justify = Some(v),
206                                _ => {}
207                            };
208                        }
209
210                        LayoutClass::Position(v) => {
211                            match comp {
212                                Component::Outsider { position, .. } => *position = Some(v),
213                                _ => {}
214                            };
215                        }
216                        LayoutClass::Top(v) => {
217                            match comp {
218                                Component::Outsider { top, .. } => *top = Some(v),
219                                _ => {}
220                            };
221                        }
222                        LayoutClass::Bottom(v) => {
223                            match comp {
224                                Component::Outsider { bottom, .. } => *bottom = Some(v),
225                                _ => {}
226                            };
227                        }
228                        LayoutClass::Left(v) => {
229                            match comp {
230                                Component::Outsider { left, .. } => *left = Some(v),
231                                _ => {}
232                            };
233                        }
234                        LayoutClass::Right(v) => {
235                            match comp {
236                                Component::Outsider { right, .. } => *right = Some(v),
237                                _ => {}
238                            };
239                        }
240
241                        LayoutClass::Height(v) => {
242                            match comp {
243                                Component::Rack { height, .. } => *height = Some(v),
244                                Component::Slider { height, .. } => *height = Some(v),
245                                _ => {}
246                            };
247                        }
248                        LayoutClass::ItemWidth(v) => {
249                            match comp {
250                                Component::Slider { item_width, .. } => *item_width = Some(v),
251                                _ => {}
252                            };
253                        }
254                        LayoutClass::MinHeight(v) => {
255                            match comp {
256                                Component::Rack { min_height, .. } => *min_height = Some(v),
257                                _ => {}
258                            };
259                        }
260                        LayoutClass::MaxHeight(v) => {
261                            match comp {
262                                Component::Rack { max_height, .. } => *max_height = Some(v),
263                                _ => {}
264                            };
265                        }
266                        LayoutClass::SideWidth(v) => {
267                            match comp {
268                                Component::Sidebar { side_width, .. } => *side_width = Some(v),
269                                _ => {}
270                            };
271                        }
272                        LayoutClass::Side(v) => {
273                            match comp {
274                                Component::Sidebar { side, .. } => *side = Some(v),
275                                _ => {}
276                            };
277                        }
278                        LayoutClass::ContentMin(v) => {
279                            match comp {
280                                Component::Sidebar { content_min, .. } => *content_min = Some(v),
281                                _ => {}
282                            };
283                        }
284                        LayoutClass::Threshold(v) => {
285                            match comp {
286                                Component::Switcher { threshold, .. } => *threshold = Some(v),
287                                _ => {}
288                            };
289                        }
290                        LayoutClass::Limit(v) => {
291                            match comp {
292                                Component::Switcher { limit, .. } => *limit = Some(v),
293                                _ => {}
294                            };
295                        }
296                        LayoutClass::KeepP => {
297                            match comp {
298                                Component::Extender { keep_p, .. } => *keep_p = true,
299                                _ => {}
300                            };
301                        }
302                        LayoutClass::KeepPL => {
303                            match comp {
304                                Component::Extender { keep_pl, .. } => *keep_pl = true,
305                                _ => {}
306                            };
307                        }
308                        LayoutClass::KeepPR => {
309                            match comp {
310                                Component::Extender { keep_pr, .. } => *keep_pr = true,
311                                _ => {}
312                            };
313                        }
314                        LayoutClass::KeepCenter => {
315                            match comp {
316                                Component::Extender { keep_center, .. } => *keep_center = true,
317                                _ => {}
318                            };
319                        }
320                        LayoutClass::Shrink => {
321                            match comp {
322                                Component::Sidebar { shrink, .. } => *shrink = true,
323                                _ => {}
324                            };
325                        }
326                        LayoutClass::AndText => {
327                            match comp {
328                                Component::Center { and_text, .. } => *and_text = true,
329                                _ => {}
330                            };
331                        }
332                        _ => {
333                            // we dont want to scope utility rules of the component outside the mq
334                            // into the mq rules so utilities in the component should be built without mq
335                            let final_mq = match media_query {
336                                Some(MediaQuery::SuperiorTo(_, _)) => None,
337                                Some(MediaQuery::InferiorOrEqualTo(_)) => media_query.clone(),
338                                _ => None,
339                            };
340                            set.insert(LayoutElement::LayoutUtility(current_class, final_mq));
341                        }
342                    };
343                } else {
344                    // we dont want to scope utility rules of the component outside the mq
345                    // into the mq rules so utilities in the component should be built without mq
346                    let final_mq = match media_query {
347                        Some(MediaQuery::SuperiorTo(_, _)) => None,
348                        Some(MediaQuery::InferiorOrEqualTo(_)) => media_query.clone(),
349                        _ => None,
350                    };
351                    set.insert(LayoutElement::LayoutUtility(current_class, final_mq));
352                }
353            }
354        }
355    }
356    if let Ok(cc) = component {
357        //TODO check if we are
358        set.insert(LayoutElement::LayoutComponent(cc, media_query));
359    }
360}
361
362#[cfg(test)]
363mod tests {
364    use super::*;
365
366
367    #[test]
368    fn media_queries_are_generated() {
369        let mut set: HashSet<String> = HashSet::new();
370        let mut mq_set: HashMap<MediaQuery, HashSet<String>> = HashMap::new();
371        let el = LayoutElement::LayoutComponent(
372            Component::Box {
373                max_width: Some("800px"),
374                grow: false,
375            },
376            Some(MediaQuery::SuperiorTo(
377                800,
378                "p:2 max-width:400px".to_string(),
379            )),
380        );
381        let el2 = LayoutElement::LayoutComponent(
382            Component::Box {
383                max_width: Some("1200px"),
384                grow: false,
385            },
386            Some(MediaQuery::SuperiorTo(
387                800,
388                "p:2 max-width:400px".to_string(),
389            )),
390        );
391
392        el.insert_css(1.618, &mut set, &mut mq_set);
393        el2.insert_css(1.618, &mut set, &mut mq_set);
394        println!("{:?}oooooooooooo", mq_set);
395        assert_eq!(4, 4)
396    }
397
398    #[test]
399    fn attribute_layout_with_component_and_utilities_append_hashset_correctly() {
400        let mut set: HashSet<LayoutElement> = HashSet::new();
401        generate(
402            "box-l",
403            Some("max-width:440px max-width:440px grow p:2 p:4 p:2"),
404            None,
405            &mut set,
406        );
407        println!("{:?}oooooooooooo", set);
408        assert_eq!(4, 4)
409    }
410}