elm_parser/counter/
counter_commands.rs1use 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); }
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 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 let re = Regex::new(cmd_regex).unwrap();
124
125 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 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 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