elm_parser/
desugarer.rs

1use nom::bytes::complete::tag;
2use regex::Regex;
3use syn::ext;
4
5use crate::datacell::{
6    BlockCell::BlockCell, BlockChildType::*, CellTrait::Cell, Datacell::*, ElementCell::ElementCell,
7};
8
9pub struct Desugarer {
10    pub json: String,
11    pub last_id: usize,
12}
13
14pub struct ParagraphIndentOptions {
15    pub tags_before_non_indents: Vec<&'static str>,
16    pub tags_with_non_indent_first_child: Vec<&'static str>,
17}
18
19#[derive(Clone, Debug)]
20pub enum AttachToEnum {
21    BEFORE,
22    AFTER,
23    BOTH,
24    NONE,
25}
26
27#[derive(Clone, Debug)]
28pub struct IgnoreOptions {
29    pub element: &'static str,
30    pub attach_to: AttachToEnum,
31}
32
33
34impl Desugarer {
35    pub fn new(json: &str, last_id: usize) -> Desugarer {
36        Desugarer {
37            json: json.to_string(),
38            last_id,
39        }
40    }
41
42    fn count_element(&mut self, root: &DataCell, tag_name: &str, count: &mut usize) {
43        match &root.cell_type {
44            CellType::Element(el) => {
45                if el.name == tag_name {
46                    *count += 1;
47                } else {
48                    el.children
49                        .iter()
50                        .for_each(|child| self.count_element(child, tag_name, count))
51                }
52            }
53            CellType::Root(el) => el
54                .children
55                .iter()
56                .for_each(|child| self.count_element(child, tag_name, count)),
57
58            _ => (),
59        }
60    }
61
62    fn find_cell<'a>(
63        &self,
64        root: &'a DataCell,
65        tag_patterns: &Vec<&str>, // regex patterns
66        cells: &mut Vec<&'a DataCell>,
67    ) {
68        match &root.cell_type {
69            CellType::Element(el) => {
70                let element_found = tag_patterns.is_empty() || tag_patterns.iter().any(|pattern|{
71                    let re = Regex::new(&format!("^{}$", pattern)).expect("Unvalid pattern {pattern}");
72                    re.is_match(&el.name)
73                });
74                if element_found {
75                    cells.push(root)
76                }
77                el.children
78                    .iter()
79                    .for_each(|child| self.find_cell(child, tag_patterns, cells))
80            }
81            CellType::Root(el) => el
82                .children
83                .iter()
84                .for_each(|child| self.find_cell(child, tag_patterns, cells)),
85            _ => (),
86        }
87    }
88
89    fn find_cell_and_mark_article<'a>(
90        &self,
91        root: &'a DataCell,
92        tag_names: &Vec<&str>,
93        cells: &mut Vec<(usize, &'a DataCell)>, //usize is the article id
94        mut article: Option<usize>,
95        article_types: &Vec<String>,
96    ) {
97        match &root.cell_type {
98            CellType::Element(el) => {
99                if tag_names.is_empty() || tag_names.contains(&el.name.as_str()) {
100                    if let Some(article_id) = article {
101                        cells.push((article_id, root))
102                    }
103                }
104                if article_types.contains(&el.name.chars().filter(|c| !c.is_numeric()).collect()) {
105                    article = Some(root.id)
106                }
107
108                el.children.iter().for_each(|child| {
109                    self.find_cell_and_mark_article(
110                        child,
111                        tag_names,
112                        cells,
113                        article,
114                        &article_types,
115                    )
116                })
117            }
118            CellType::Root(el) => el.children.iter().for_each(|child| {
119                self.find_cell_and_mark_article(child, tag_names, cells, article, &article_types)
120            }),
121            _ => (),
122        }
123    }
124
125    pub fn pre_process_exercises(&mut self) -> Desugarer {
126        let mut root: DataCell = serde_json::from_str(&self.json).unwrap();
127        let mut exercises: Vec<&DataCell> = Vec::new();
128        let binding = root.clone();
129        self.find_cell(&binding, &vec!["Exercises"], &mut exercises);
130
131        for exercises_cell in exercises.iter() {
132            let mut count: usize = 0;
133            self.count_element(exercises_cell, "Exercise", &mut count);
134            let mut prop_line = "labels vec![\"0\"".to_string();
135            if count > 0 {
136                for i in 1..=count - 1 {
137                    prop_line += &format!(",\"{}\"", i);
138                }
139                prop_line += "]";
140
141                //exercises_cell
142
143                ElementCell::add_attribute(&mut root, exercises_cell.id, prop_line.as_str());
144            }
145        }
146
147        Desugarer {
148            json: serde_json::to_string_pretty(&root).unwrap(),
149            last_id: self.last_id,
150        }
151    }
152
153    pub fn add_increamental_attr(
154        &mut self,
155        tags_attributes: Vec<(&str, &str)>,
156        article_types: &Vec<String>,
157    ) -> Desugarer {
158        let mut root: DataCell = serde_json::from_str(&self.json).unwrap();
159        let mut elements: Vec<(usize, &DataCell)> = Vec::new();
160        let binding = root.clone();
161        let tag_names: Vec<&str> = tags_attributes.iter().map(|x| x.0).collect();
162        let attribute_names: Vec<&str> = tags_attributes.iter().map(|x| x.1).collect();
163        self.find_cell_and_mark_article(&binding, &tag_names, &mut elements, None, article_types);
164
165        let mut counters = vec![0; tag_names.len()];
166
167        for (i, (article_id, solution_cell)) in elements.iter().enumerate() {
168            //get element position in tag_names
169            if let CellType::Element(el) = &solution_cell.cell_type {
170                let (tag_index, _) = tag_names
171                    .iter()
172                    .enumerate()
173                    .find(|x| x.1 == &el.name)
174                    .unwrap();
175
176                // reset counter on new article
177                if i > 0 && elements[i - 1].0 != *article_id {
178                    counters[tag_index] = 0;
179                }
180
181                let prop_line = format!("{} {}", attribute_names[tag_index], counters[tag_index]);
182
183                counters[tag_index] += 1;
184
185                ElementCell::add_attribute(&mut root, solution_cell.id, prop_line.as_str());
186            }
187        }
188
189        Desugarer {
190            json: serde_json::to_string_pretty(&root).unwrap(),
191            last_id: self.last_id,
192        }
193    }
194
195    pub fn auto_increamental_title(
196        &mut self,
197        tag_name: &str,
198        title_label: &str,
199        article_types: &Vec<String>,
200    ) -> Desugarer {
201        let mut root: DataCell = serde_json::from_str(&self.json).unwrap();
202        let mut elements: Vec<(usize, &DataCell)> = Vec::new();
203        let binding = root.clone();
204        self.find_cell_and_mark_article(
205            &binding,
206            &vec![tag_name],
207            &mut elements,
208            None,
209            article_types,
210        );
211
212        for (_, (article_id, element)) in elements.clone().iter_mut().enumerate() {
213            if let CellType::Element(el) = &element.cell_type {
214                // counter prop to parent if it doesn't have it
215
216                ElementCell::add_attribute(
217                    &mut root,
218                    *article_id,
219                    &format!("counter {}{}_counter", title_label, article_id),
220                );
221
222                let handle = el.props.iter().find(|x| x.key == "handle");
223
224                let command = format!(
225                    "{} {}::++{}{}_counter.",
226                    title_label,
227                    if handle.is_some() {
228                        handle.unwrap().value.to_owned() + "<<"
229                    } else {
230                        "".to_string()
231                    },
232                    title_label,
233                    article_id
234                );
235
236                let new_block_child = BlockChildType::Delimited(DelimitedCell {
237                    open_delimeter: "*".to_string(),
238                    close_delimeter: "*".to_string(),
239                    terminal: command,
240                    display_type: DelimitedDisplayType::INLINE,
241                    wrapped_with: None,
242                });
243
244                // add space to element text ( first block )
245                BlockChildType::insert_text_to_first_block_child_text(&mut root, element.id, " ");
246
247                BlockChildType::add_block_at_first(
248                    &mut root,
249                    element.id,
250                    &new_block_child,
251                    Some(&BlockCell {
252                        has_counter_commands: true,
253                        ..Default::default()
254                    }),
255                );
256            }
257        }
258
259        Desugarer {
260            json: serde_json::to_string_pretty(&root).unwrap(),
261            last_id: self.last_id,
262        }
263    }
264
265    pub fn wrap_children<'a>(
266        &mut self,
267        elements: Vec<&'static str>, // regex patterns
268        wrap_with: &str,
269        ignore_elements: &Option<Vec<IgnoreOptions>>,
270    ) -> Desugarer {
271        // elements are what we want to wrap their children with wrap_with
272
273        let mut root: DataCell = serde_json::from_str(&self.json).unwrap();
274        let binding = root.clone();
275        let mut _elements: Vec<&DataCell> = Vec::new();
276      
277        self.find_cell(&binding, &elements, &mut _elements);
278        
279        // element theme selves will also be ignored 
280        let mut extra_ignored_elements = elements.iter().map(|el|{
281            IgnoreOptions{
282                element: el,
283                attach_to: AttachToEnum::NONE
284            }
285        }).collect::<Vec<IgnoreOptions>>();
286
287        if let Some(ignored) = ignore_elements {
288            ignored.iter().for_each(|i|{
289                extra_ignored_elements.push(i.clone());
290            });
291        }
292        
293        for element in _elements {
294            //let prop_line = format!("solution_number {}", i);
295            self.wrap_element_children(&mut root, element, &extra_ignored_elements, wrap_with);
296        }
297
298        Desugarer {
299            json: serde_json::to_string_pretty(&root).unwrap(),
300            last_id: self.last_id,
301        }
302
303     }
304
305     fn wrap_element_children<T: FlatElement>(&mut self, root: &mut DataCell, el: &T, ignore_elements: &Vec<IgnoreOptions>, wrap_with: &str) {
306                let mut include_prev_child = false;
307                let mut include_in_prev_wrapper = false;
308                let mut add_wrapper = true;
309                let mut no_wrap = false;
310
311                el.children().unwrap().iter().enumerate().for_each(|(idx, child)| {
312                    let mut element_ignored = None;
313                    ignore_elements.iter().any(|option| {
314                        if let CellType::Element(child_el) = &child.cell_type {
315                            if option.element == child_el.name {
316                                element_ignored = Some(option);
317                                add_wrapper = false;
318                                return true;
319                            }
320                        }
321                        add_wrapper = true;
322                        false
323                    });
324
325                    if let Some(el_ignored) = element_ignored {
326                        match el_ignored.attach_to {
327                            AttachToEnum::BEFORE => {
328                                // move to previous added wrapper if there is one
329                                if idx == 0 {
330                                    add_wrapper = true;
331                                } else {
332                                    ElementCell::move_cell(
333                                        root,
334                                        (el.id(), child.id),
335                                        self.last_id,
336                                    );
337                                }
338                            }
339                            AttachToEnum::AFTER => {
340                                // move to next wrapper
341                                include_prev_child = true
342                            }
343                            AttachToEnum::BOTH => {
344                                // this and next block should be in previous added wrapper
345                                if idx == 0 {
346                                    self.last_id += 1;
347                                    ElementCell::add_cell(
348                                        root,
349                                        el.id(),
350                                        self.last_id,
351                                        wrap_with,
352                                    );
353                                }
354                                ElementCell::move_cell(
355                                    // current child to previous wrapper
356                                    root,
357                                    (el.id(), child.id),
358                                    self.last_id,
359                                );
360                                include_in_prev_wrapper = true // next child to previous wrapper
361                            }
362                            AttachToEnum::NONE => {
363                                // Do nothing for these elements . just move to last so ordering doesn't change .
364                                //no_wrap = true;
365                                add_wrapper = false;
366                                include_in_prev_wrapper = false;
367                                ElementCell::move_cell(
368                                    root,
369                                    (el.id(), child.id),
370                                    el.id(),
371                                );
372                            }
373                        }
374                    } else if include_in_prev_wrapper {
375                        ElementCell::move_cell(root, (el.id(), child.id), self.last_id);
376                        include_in_prev_wrapper = false;
377
378                        add_wrapper = false
379                    }
380
381                    if add_wrapper {
382                        self.last_id += 1;
383                        ElementCell::add_cell(root, el.id(), self.last_id, wrap_with);
384
385                        if include_prev_child {
386                            ElementCell::move_cell(
387                                root,
388                                (el.id(), el.children().unwrap()[idx - 1].id),
389                                self.last_id,
390                            );
391                            include_prev_child = false
392                        }
393                        ElementCell::move_cell(root, (el.id(), child.id), self.last_id);
394                    }
395                    // if no_wrap {
396
397                    // }
398                });
399        }
400
401    pub fn wrap_block_delimited(&self, wrap_with: &str) -> Desugarer {
402        let mut root: DataCell = serde_json::from_str(&self.json).unwrap();
403        self.wrap_recursive(&mut root, wrap_with);
404
405        Desugarer {
406            json: serde_json::to_string_pretty(&root).unwrap(),
407            last_id: self.last_id,
408        }
409    }
410
411    pub fn wrap_recursive<'a>(&self, root: &'a mut DataCell, wrap_with: &str) -> Desugarer {
412        match &mut root.cell_type {
413            CellType::Block(block) => {
414                block
415                    .children
416                    .iter_mut()
417                    .enumerate()
418                    .for_each(|(i, child)| {
419                        if let BlockChildType::Delimited(dl) = child {
420                            if i > 0 && dl.display_type == DelimitedDisplayType::BLOCK {
421                                dl.wrapped_with = Some(wrap_with.to_string());
422                            }
423                        }
424                    });
425            }
426            CellType::Element(el) => el.children.iter_mut().for_each(|child| {
427                self.wrap_recursive(child, wrap_with);
428            }),
429            CellType::Root(el) => el.children.iter_mut().for_each(|child| {
430                self.wrap_recursive(child, wrap_with);
431            }),
432            _ => (),
433        }
434
435        Desugarer {
436            json: serde_json::to_string_pretty(&root).unwrap(),
437            last_id: self.last_id,
438        }
439    }
440
441    pub fn add_indent(&mut self, paragraph_tags: &Vec<&str>) -> Desugarer {
442        // A paragraph has an indent by default except:
443
444        let mut root: DataCell = serde_json::from_str(&self.json).unwrap();
445        let mut _elements: Vec<&DataCell> = Vec::new();
446        let binding = root.clone();
447
448        self.find_cell(&binding, paragraph_tags, &mut _elements);
449
450        let mut elements_to_indent: Vec<&DataCell> = Vec::new();
451
452        for element in _elements.iter() {
453            let parent: Option<&mut DataCell> =
454                DataCell::get_cell_by_id(&mut root, element.parent_id);
455
456            // an indent appears if this paragraph is preceded by a paragraph that has a non-block delimiter last child
457            if Self::paragraph_first_child_is_text(&element)
458                && Self::prev_is_non_block_delimiter(element.id, &parent, paragraph_tags)
459            {
460                elements_to_indent.push(element);
461            }
462        }
463
464        for element in elements_to_indent.iter() {
465            self.last_id += 1;
466            ElementCell::add_cell(&mut root, element.id, self.last_id, "Indent");
467            ElementCell::move_children(&mut root, element.id, self.last_id);
468        }
469
470        Desugarer {
471            json: serde_json::to_string_pretty(&root).unwrap(),
472            last_id: self.last_id,
473        }
474    }
475
476    pub fn paragraph_first_child_is_text(element: &DataCell) -> bool {
477        if let CellType::Element(el) = &element.cell_type {
478            return el.children.first().is_some_and(|c| {
479                if let CellType::Block(block) = &c.cell_type {
480                    return block.children.first().is_some_and(|block_child| {
481                        if let BlockChildType::Text(_) = &block_child {
482                            return true;
483                        }
484                        false
485                    });
486                }
487                false
488            });
489        }
490        false
491    }
492
493    pub fn is_first_child(
494        element_id: usize,
495        parent: &Option<&mut DataCell>,
496        options: &ParagraphIndentOptions,
497    ) -> bool {
498        if let Some(parent) = parent {
499            if let CellType::Element(parent) = &parent.cell_type {
500                if options
501                    .tags_with_non_indent_first_child
502                    .contains(&parent.name.as_str())
503                    && parent.children.first().is_some_and(|c| c.id == element_id)
504                {
505                    return true;
506                }
507            }
508        }
509        false
510    }
511
512    fn prev_is_non_block_delimiter(
513        element_id: usize,
514        parent: &Option<&mut DataCell>,
515        paragraph_tags: &Vec<&str>,
516    ) -> bool {
517        if let Some(parent) = parent {
518            if let CellType::Element(parent_el) = &parent.cell_type {
519                let mut prev_el: Option<&DataCell> = None;
520                for child in &parent_el.children {
521                    if child.id == element_id {
522                        break;
523                    }
524                    prev_el = Some(&child)
525                }
526                return prev_el.is_some_and(|p| {
527                    if let CellType::Element(el) = &p.cell_type {
528                        if !paragraph_tags.contains(&el.name.as_str()) {
529                            return false;
530                        }
531                        if let Some(block) = el.children.last() {
532                            if let CellType::Block(block) = &block.cell_type {
533                                if let Some(block) = block.children.last() {
534                                    if let BlockChildType::Delimited(b) = &block {
535                                        return b.display_type != DelimitedDisplayType::BLOCK
536                                            && b.open_delimeter != "*";
537                                    }
538                                    if let BlockChildType::Text(_) = &block {
539                                        return true;
540                                    }
541                                }
542                            }
543                        }
544                    }
545                    false
546                });
547            }
548        }
549        false
550    }
551
552    pub fn add_attribute(&mut self, tag_names: Vec<&str>, attribute: (&str, &str)) -> Desugarer {
553        // usefull for adding attributes for custom wrappers added by wrap_children fn
554
555        let mut root: DataCell = serde_json::from_str(&self.json).unwrap();
556        let mut _elements: Vec<&DataCell> = Vec::new();
557        let binding = root.clone();
558        self.find_cell(&binding, &tag_names, &mut _elements);
559
560        for (_, element) in _elements.iter().enumerate() {
561            if let CellType::Element(_) = &element.cell_type {
562                ElementCell::add_attribute(
563                    &mut root,
564                    element.parent_id,
565                    &format!("{} {}", attribute.0, attribute.1),
566                )
567            }
568        }
569
570        Desugarer {
571            json: serde_json::to_string_pretty(&root).unwrap(),
572            last_id: self.last_id,
573        }
574    }
575
576    pub fn auto_convert_to_float(&mut self, attrs: Vec<&str>) -> Desugarer {
577        fn convert_recusive(root: &mut DataCell, attrs: &Vec<&str>) {
578            match &mut root.cell_type {
579                CellType::Element(el) => {
580                    el.props.iter_mut().for_each(|prop| {
581                        if attrs.contains(&prop.key.as_str()) && !prop.value.contains('.') {
582                            prop.value = format!("{}.0", prop.value);
583                        }
584                    });
585                    el.children
586                        .iter_mut()
587                        .for_each(|child| convert_recusive(child, attrs))
588                }
589                CellType::Root(el) => el
590                    .children
591                    .iter_mut()
592                    .for_each(|child| convert_recusive(child, attrs)),
593                _ => (),
594            }
595        }
596
597        let mut root: DataCell = serde_json::from_str(&self.json).unwrap();
598        convert_recusive(&mut root, &attrs);
599        Desugarer {
600            json: serde_json::to_string_pretty(&root).unwrap(),
601            last_id: self.last_id,
602        }
603    }
604}
605