elm_parser/counter/
counter_commands.rs

1use crate::datacell::{BlockChildType::*, Datacell::*};
2
3use super::counters::Counters;
4use regex::Regex;
5use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
8pub enum CommandType {
9    #[default]
10    INC,
11    DEC,
12    ASSIGN,
13    INSERT,
14}
15
16impl CommandType {
17    fn from_str(string: &str) -> Vec<Self> {
18        let mut sub_string = string;
19        if string.starts_with("<<") {
20            sub_string = &string[2..];
21        }
22
23        let mut res = match sub_string {
24            "::++" => vec![CommandType::INC, CommandType::INSERT],
25            "::--" => vec![CommandType::DEC, CommandType::INSERT],
26            "..++" => vec![CommandType::INC],
27            "..--" => vec![CommandType::DEC],
28            "::::" => vec![CommandType::INSERT],
29            _ => vec![],
30        };
31        if string.starts_with("<<") {
32            res.push(CommandType::ASSIGN); // align should be the last
33        }
34        res
35    }
36
37    fn to_tag_name(&self) -> String {
38        match self {
39            CommandType::INC => "CounterIncrement".to_string(),
40            CommandType::DEC => "CounterDecrement".to_string(),
41            CommandType::ASSIGN => "CounterAssign".to_string(),
42            CommandType::INSERT => "CounterInsert".to_string(),
43        }
44    }
45
46    pub fn from_tag_name(tag_name: &str) -> Option<Self> {
47        match tag_name {
48            "CounterIncrement" => Some(CommandType::INC),
49            "CounterDecrement" => Some(CommandType::DEC),
50            "CounterAssign" => Some(CommandType::ASSIGN),
51            "CounterInsert" => Some(CommandType::INSERT),
52            _ => None,
53        }
54    }
55}
56
57#[derive(Debug)]
58pub struct CounterCommand<'a> {
59    //commands: Vec<CommandType>,
60    pub counters: &'a mut Counters,
61    pub json_tree: String,
62}
63
64#[derive(Debug)]
65pub struct LineSplits {
66    pub content: String,
67    pub is_command: bool,
68}
69
70impl<'a> CounterCommand<'a> {
71    pub fn new(counters: &'a mut Counters, json_tree: &'a str) -> Self {
72        Self {
73            counters,
74            json_tree: json_tree.to_string(),
75        }
76    }
77
78    fn get_commands(&self, command_str: &str) -> Vec<CommandType> {
79        let command_split = command_str.split("<<").collect::<Vec<&str>>();
80
81        if command_split.len() == 1 {
82            CommandType::from_str(&command_split[0][0..4])
83        } else {
84            CommandType::from_str(&("<<".to_string() + &command_split[1][0..4]))
85        }
86    }
87
88    fn get_command_counter_name(&self, command_str: &str) -> String {
89        let command_split = command_str.split("<<").collect::<Vec<&str>>();
90
91        command_split.last().unwrap()[4..].to_string()
92    }
93    fn get_handle_name(&self, command_str: &str) -> Option<String> {
94        let command_split = command_str.split("<<").collect::<Vec<&str>>();
95
96        if command_split.len() == 1 {
97            None
98        } else {
99            Some(command_split[0].to_string())
100        }
101    }
102
103    pub fn has_counter_syntax(text: &str) -> bool {
104        let cmd_regex = r"(^|[^\\])(\w+<<)?(::|\.\.)(::|\+\+|--)\w+";
105        let re = Regex::new(cmd_regex).unwrap();
106        re.is_match(text)
107    }
108
109    pub fn has_handle_insert_syntax(text: &str) -> bool {
110        let handle_regex = r"(^|[^\\])(>>)\w+";
111        let re = Regex::new(handle_regex).unwrap();
112        re.is_match(text)
113    }
114
115    fn split_line(&self, line: &str, handle_insert_command: bool) -> Vec<LineSplits> {
116        let cmd_regex = if handle_insert_command {
117            r"(^|[^\\])(>>)\w+"
118        } else {
119            r"(^|[^\\])(\w+<<)?(::|\.\.)(::|\+\+|--)\w+"
120        };
121
122        // Create the regex object
123        let re = Regex::new(cmd_regex).unwrap();
124
125        // Find all matches and split the string accordingly
126        let mut result = Vec::new();
127        let mut start = 0;
128
129        for mat in re.find_iter(line) {
130            let match_first_char = &line[mat.start()..mat.start() + 1];
131
132            if mat.start() != start {
133                //if first char of found regex is not a space, add the previous part of the string
134                let mut string = String::from(&line[start..mat.start()]);
135                string.push_str(match_first_char);
136                result.push(LineSplits {
137                    content: string,
138                    is_command: false,
139                });
140            }
141
142            let mut string = String::new();
143            if mat.start() > 0 {
144                string.push_str(&mat.as_str()[1..]);
145            } else {
146                string.push_str(&mat.as_str());
147            }
148
149            result.push(LineSplits {
150                content: string,
151                is_command: true,
152            });
153            start = mat.end();
154        }
155
156        // Add the remaining part of the string, if any
157        if start < line.len() {
158            result.push(LineSplits {
159                content: line[start..].to_string(),
160                is_command: false,
161            });
162        }
163
164        result
165    }
166
167    pub fn run(&mut self, json: &mut DataCell) -> String {
168        if self.counters.counters_list.is_empty() {
169            return self.json_tree.clone();
170        }
171        let json_str = self.replace_counters(json, false);
172        let mut json: DataCell = serde_json::from_str(&json_str).unwrap();
173        let json_str = self.replace_counters(&mut json, true);
174
175        json_str
176    }
177
178    pub fn replace_counters(&mut self, cell: &mut DataCell, handle_insert_command: bool) -> String {
179        let cloned_cell = cell.clone();
180
181        match &mut cell.cell_type {
182            CellType::Block(block) => {
183                if (block.has_counter_commands && !handle_insert_command)
184                    || (block.has_handle_insert && handle_insert_command)
185                {
186                    block.children.iter_mut().for_each(|child| {
187                        self.handle_block_child(child, &cloned_cell, handle_insert_command)
188                    });
189                }
190            }
191            CellType::Element(el) => el.children.iter_mut().for_each(|child| {
192                self.replace_counters(child, handle_insert_command);
193            }),
194            CellType::Root(el) => el.children.iter_mut().for_each(|child| {
195                self.replace_counters(child, handle_insert_command);
196            }),
197            _ => (),
198        }
199
200        serde_json::to_string(&cell).unwrap()
201    }
202
203    fn handle_block_child(
204        &mut self,
205        block_child: &mut BlockChildType,
206        cell: &DataCell,
207        handle_insert_command: bool,
208    ) {
209        match block_child {
210            BlockChildType::Text(text) => {
211                text.content = if handle_insert_command {
212                    self.insert_handle_value(&text.content)
213                } else {
214                    self.replace_counter_value(&text.content, cell)
215                }
216            }
217            BlockChildType::Delimited(d) => {
218                d.terminal = if handle_insert_command {
219                    self.insert_handle_value(&d.terminal)
220                } else {
221                    self.replace_counter_value(&d.terminal, cell)
222                };
223            }
224        };
225    }
226
227    fn replace_counter_value(&mut self, text: &str, cell: &DataCell) -> String {
228        let splits = self.split_line(&text, false);
229        let mut res = String::new();
230
231        for split in splits {
232            if split.is_command {
233                let counter_name = &self.get_command_counter_name(split.content.as_str());
234                self.counters.check_scope(
235                    &cell,
236                    counter_name,
237                    &serde_json::from_str(&self.json_tree).unwrap(),
238                );
239
240                let commands = self.get_commands(split.content.as_str());
241
242                let handle_name = self.get_handle_name(split.content.as_str());
243
244                for command in commands {
245                    let execution = self.counters.execute(command, counter_name, &handle_name);
246                    if execution.is_some() {
247                        if &split.content.as_str()[0..1] == " " {
248                            res.push_str(" ");
249                        }
250                        res.push_str(&execution.unwrap());
251                    }
252                }
253            } else {
254                res.push_str(&split.content);
255            }
256        }
257
258        res
259    }
260
261    fn insert_handle_value(&mut self, text: &str) -> String {
262        let splits = self.split_line(&text, true);
263
264        let mut res = String::new();
265        for split in splits {
266            if split.is_command {
267                let handle = self
268                    .counters
269                    .handles_list
270                    .iter()
271                    .find(|h| h.name == &split.content[2..]);
272                if handle.is_some() {
273                    res.push_str(&handle.unwrap().value);
274                };
275            } else {
276                res.push_str(&split.content);
277            }
278        }
279
280        res
281    }
282}
283
284//create a test for replacecounter_in_line function
285// #[test]
286// fn test_replace_counter_in_line() {
287//     let mut counters = Counters::new();
288//     counters.add_counter(CounterInstance::new(
289//         "TestArabicCounter",
290//         "counter",
291//         0,
292//         None,
293//     ));
294//     counters.add_counter(CounterInstance::new(
295//         "TestRomanCounter",
296//         "roman_counter",
297//         0,
298//         None,
299//     ));
300
301//     //with text after
302//     let test1 =
303//         replace_counter_in_line("askljdklasj ::::TestArabicCounter qweqweqwe", &mut counters);
304//     assert_eq!(test1, "askljdklasj 0 qweqweqwe");
305
306//     //no text after
307//     let test2 = replace_counter_in_line("askljdklasj ::::TestArabicCounter", &mut counters);
308//     assert_eq!(test2, "askljdklasj 0");
309
310//     // 2 same type counters in 1 line
311//     let test3 = replace_counter_in_line(
312//         "askljdklasj ::::TestArabicCounter hi ::::TestArabicCounter",
313//         &mut counters,
314//     );
315//     assert_eq!(test3, "askljdklasj 0 hi 0");
316
317//     // 2 different type counters in 1 line
318//     let test4 = replace_counter_in_line(
319//         "askljdklasj ::::TestArabicCounter hi ::::TestRomanCounter",
320//         &mut counters,
321//     );
322//     assert_eq!(test4, "askljdklasj 0 hi 0");
323
324//     // test increment
325//     let test4 = replace_counter_in_line(
326//         "askljdklasj ::++TestArabicCounter hi ::++TestRomanCounter",
327//         &mut counters,
328//     );
329//     assert_eq!(test4, "askljdklasj 1 hi i");
330
331//     // test double increment than normal
332//     let test4 = replace_counter_in_line(
333//         "inc ::++TestRomanCounter another inc ::++TestRomanCounter fix ::::TestRomanCounter",
334//         &mut counters,
335//     );
336//     assert_eq!(test4, "inc ii another inc iii fix iii");
337
338//     // test double deccrement than normal
339//     let test4 = replace_counter_in_line(
340//         "dec ::--TestRomanCounter another dec ::--TestRomanCounter fix ::::TestRomanCounter dec ar ::--TestArabicCounter fix ar ::::TestArabicCounter",
341//         &mut counters,
342//     );
343//     assert_eq!(test4, "dec ii another dec i fix i dec ar 0 fix ar 0");
344
345//     // test escaped
346//     let test4 = replace_counter_in_line(r#"escaped \::--TestRomanCounter"#, &mut counters);
347//     assert_eq!(test4, r"escaped \::--TestRomanCounter");
348// }
349
350// #[test]
351// fn decrement_less_than_0() {
352//     let mut counters = Counters::new();
353//     counters.add_counter(CounterInstance::new(
354//         "TestArabicCounter",
355//         "counter",
356//         0,
357//         None,
358//     ));
359//     counters.add_counter(CounterInstance::new(
360//         "TestRomanCounter",
361//         "roman_counter",
362//         0,
363//         None,
364//     ));
365
366//     let test =
367//         replace_counter_in_line("::--TestArabicCounter ::--TestArabicCounter", &mut counters);
368//     assert_eq!(test, "- -");
369
370//     let test2 =
371//         replace_counter_in_line("::::TestRomanCounter ::--TestArabicCounter", &mut counters);
372//     assert_eq!(test2, "0 -");
373// }
374
375// #[test]
376// fn silent_operations() {
377//     let mut counters = Counters::new();
378//     counters.add_counter(CounterInstance::new(
379//         "TestArabicCounter",
380//         "counter",
381//         0,
382//         None,
383//     ));
384
385//     let test =
386//         replace_counter_in_line("::++TestArabicCounter ..--TestArabicCounter", &mut counters);
387//     assert_eq!(test, "1 ");
388
389//     let test2 = replace_counter_in_line("::::TestArabicCounter", &mut counters);
390//     assert_eq!(test2, "0");
391// }
392
393// #[test]
394// fn default_value() {
395//     let mut counters = Counters::new();
396//     counters.add_counter(CounterInstance::new(
397//         "TestArabicCounter",
398//         "counter",
399//         0,
400//         Some("10"),
401//     ));
402//     counters.add_counter(CounterInstance::new(
403//         "TestRomanCounter",
404//         "roman_counter",
405//         0,
406//         Some("ii"),
407//     ));
408
409//     let test = replace_counter_in_line("::--TestArabicCounter", &mut counters);
410//     assert_eq!(test, "9");
411
412//     let test2 = replace_counter_in_line("::::TestRomanCounter ::--TestRomanCounter", &mut counters);
413//     assert_eq!(test2, "ii i");
414// }