neutralts/bif/
mod.rs

1//
2
3use chrono::Local;
4use crate::{
5    json, Value,
6    block_parser::BlockInherit,
7    constants::*,
8    shared::Shared,
9    utils::*,
10};
11
12mod constants;
13mod parse_bif_allow;
14mod parse_bif_array;
15mod parse_bif_bool;
16mod parse_bif_cache;
17mod parse_bif_coalesce;
18mod parse_bif_code;
19mod parse_bif_contains;
20mod parse_bif_count;
21mod parse_bif_data;
22mod parse_bif_date;
23mod parse_bif_declare;
24mod parse_bif_defined;
25mod parse_bif_each;
26mod parse_bif_else;
27mod parse_bif_eval;
28mod parse_bif_exit;
29mod parse_bif_fetch;
30mod parse_bif_filled;
31mod parse_bif_flg;
32mod parse_bif_for;
33mod parse_bif_hash;
34mod parse_bif_include;
35mod parse_bif_join;
36mod parse_bif_lang;
37mod parse_bif_locale;
38mod parse_bif_moveto;
39mod parse_bif_neutral;
40mod parse_bif_param;
41mod parse_bif_rand;
42mod parse_bif_redirect;
43mod parse_bif_replace;
44mod parse_bif_same;
45mod parse_bif_snippet;
46mod parse_bif_sum;
47mod parse_bif_trans;
48mod parse_bif_unknown;
49mod parse_bif_var;
50mod parse_bif_obj;
51mod parse_bif_debug;
52
53mod exec_python;
54pub use exec_python::PythonExecutor;
55
56pub(crate) struct BifError {
57    pub(crate) msg: String,
58    pub(crate) file: String,
59    pub(crate) name: String,
60    pub(crate) src: String,
61}
62
63pub(crate) struct Bif<'a> {
64    pub(crate) raw: &'a str,
65    pub(crate) shared: &'a mut Shared,
66    pub(crate) inherit: &'a mut BlockInherit,
67    pub(crate) src: String,
68    pub(crate) name: String,
69    pub(crate) alias: String,
70    pub(crate) code: String,
71    pub(crate) params: String,
72    pub(crate) flags: String,
73    pub(crate) mod_filter: bool,
74    pub(crate) mod_negate: bool,
75    pub(crate) mod_upline: bool,
76    pub(crate) mod_scope: bool,
77    pub(crate) file_path: String,
78    pub(crate) dir: String,
79    pub(crate) out: String,
80    pub(crate) only: &'a str,
81    _none: &'a str,
82}
83
84impl<'a> Bif<'a> {
85    pub(crate) fn new(
86        raw_source: &'a str,
87        shared: &'a mut Shared,
88        inherit: &'a mut BlockInherit,
89        only: &'a str,
90    ) -> Self {
91        shared.bisf_count += 1;
92        let count = shared.bisf_count;
93        inherit.bif_count = shared.bisf_count;
94
95        if count > shared.bisf_max {
96            panic!(
97                "Infinite loop? {} bifs of {} max have been created.",
98                shared.bisf_max, count
99            );
100        }
101
102        Bif {
103            raw: raw_source, // should not be modified
104            shared,
105            inherit,
106            src: String::new(),
107            name: String::new(),
108            alias: String::new(),
109            code: String::new(),
110            params: String::new(),
111            flags: String::new(),
112            mod_filter: false,
113            mod_negate: false,
114            mod_upline: false,
115            mod_scope: false,
116            file_path: String::new(),
117            dir: String::new(),
118            out: String::new(),
119            only,
120            _none: "",
121        }
122    }
123
124    // Divides the bif into its parts and executes the bif parse function.
125    pub(crate) fn parse(&mut self) -> String {
126        let bif = strip_prefix_suffix(self.raw, BIF_OPEN, BIF_CLOSE);
127        let result;
128
129        if let Some((name, src)) = bif.split_once(BIF_NAME) {
130            self.name = name.to_string();
131            self.src = src.trim().to_string();
132        } else {
133            if !self.only.is_empty() {
134                return self.raw.to_string();
135            }
136
137            let show_error = self.shared.schema["config"]["error"]["show"]
138                .as_bool()
139                .unwrap();
140            let error_line = format!("The delimiter was not found: {}", self.raw);
141            let error_line = error_line.replace(['\n', '\r'], " ");
142
143            if let Some(Value::Array(errors)) = self.shared.schema.get_mut("__error") {
144                errors.push(json!(error_line));
145            }
146
147            if show_error {
148                eprintln!("{}", error_line);
149            }
150
151            self.shared.has_error = true;
152
153            return EMPTY_STRING;
154        }
155
156        if !self.only.is_empty() && !self.name.contains(self.only) && !self.inherit.in_only {
157            return self.raw.to_string();
158        }
159
160        self.name = self.set_modifiers();
161        self.alias = self.name.clone();
162        self.inherit.in_only = true;
163
164        // exec the function of each bif
165        match &self.name[..] {
166            "" => result = self.parse_bif_var(),
167            "allow" => result = self.parse_bif_allow(),
168            "array" => result = self.parse_bif_array(),
169            "bool" => result = self.parse_bif_bool(),
170            "cache" => result = self.parse_bif_cache(),
171            "coalesce" => result = self.parse_bif_coalesce(),
172            "code" => result = self.parse_bif_code(),
173            "contains" => result = self.parse_bif_contains(),
174            "count" => result = self.parse_bif_count(),
175            "data" => result = self.parse_bif_data(),
176            "date" => result = self.parse_bif_date(),
177            "declare" => result = self.parse_bif_declare(),
178            "defined" => result = self.parse_bif_defined(),
179            "each" => result = self.parse_bif_each(),
180            "else" => result = self.parse_bif_else(),
181            "eval" => result = self.parse_bif_eval(),
182            "exit" => result = self.parse_bif_exit(),
183            "fetch" => result = self.parse_bif_fetch(),
184            "filled" => result = self.parse_bif_filled(),
185            "flg" => result = self.parse_bif_flg(),
186            "for" => result = self.parse_bif_for(),
187            "hash" => result = self.parse_bif_hash(),
188            "include" => result = self.parse_bif_include(),
189            "join" => result = self.parse_bif_join(),
190            "lang" => result = self.parse_bif_lang(),
191            "locale" => result = self.parse_bif_locale(),
192            "moveto" => result = self.parse_bif_moveto(),
193            "neutral" => result = self.parse_bif_neutral(),
194            "param" => result = self.parse_bif_param(),
195            "rand" => result = self.parse_bif_rand(),
196            "redirect" => result = self.parse_bif_redirect(),
197            "replace" => result = self.parse_bif_replace(),
198            "same" => result = self.parse_bif_same(),
199            "snippet" => result = self.parse_bif_snippet(),
200            "snip" => result = self.parse_bif_snippet(),
201            "sum" => result = self.parse_bif_sum(),
202            "trans" => result = self.parse_bif_trans(),
203            "obj" => result = self.parse_bif_obj(),
204            "debug" => result = self.parse_bif_debug(),
205            _ => result = self.parse_bif_unknown(),
206        }
207
208        match result {
209            Ok(()) => (),
210            Err(e) => {
211                let show_error = self.shared.schema["config"]["error"]["show"]
212                    .as_bool()
213                    .unwrap();
214
215                let datetime = Local::now().format("%Y-%m-%d %H:%M:%S").to_string();
216
217                let error_line = format!(
218                    "[{}] Error ({}) {} file~: ({}) src: {}",
219                    datetime,
220                    e.name,
221                    e.msg,
222                    e.file,
223                    e.src
224                );
225
226                let error_line = error_line.replace(['\n', '\r'], " ");
227
228                if let Some(Value::Array(errors)) = self.shared.schema.get_mut("__error") {
229                    errors.push(json!(error_line));
230                }
231
232                if show_error {
233                    eprintln!("{}", error_line);
234                }
235
236                self.shared.has_error = true;
237            }
238        }
239
240        self.inherit.last_bif_out = !self.out.is_empty();
241        self.inherit.last_coalesce_out = self.inherit.last_bif_out;
242
243        if self.mod_upline {
244            self.out = BACKSPACE.to_string() + &self.out;
245            self.out.trim().to_string()
246        } else {
247            self.out.trim().to_string()
248        }
249    }
250
251    //  Determines which modifiers are being used
252    //
253    //    .------ modifier
254    //    |
255    //    v
256    //  {:!snippet; ...
257    //
258    pub(crate) fn set_modifiers(&mut self) -> String {
259        let mut index = 0;
260        while index < self.name.len() {
261            let start = &self.name[index..index + 1];
262            if start == BIF_MOD_FILTER
263                || start == BIF_MOD_NEGATE
264                || start == BIF_MOD_UPLINE
265                || start == BIF_MOD_SCOPE
266            {
267                match start {
268                    BIF_MOD_FILTER => self.mod_filter = true,
269                    BIF_MOD_NEGATE => self.mod_negate = true,
270                    BIF_MOD_UPLINE => self.mod_upline = true,
271                    BIF_MOD_SCOPE => self.mod_scope = true,
272                    _ => unreachable!(),
273                }
274                index += 1;
275            } else {
276                break;
277            }
278        }
279
280        self.name[index..].to_string()
281    }
282
283    // Get key from schema data o local data
284    //
285    // {
286    //     "config": {},
287    //     "inherit": {},
288    //     "data": {}  <------------ schema data get from
289    //     "__indir": {
290    //          "X": {
291    //             "data": {} <----- local data get from
292    //     ...
293    // }
294    pub(crate) fn get_data(&self, name: &str) -> String {
295        if name.starts_with("local::") {
296            let local_name = name.strip_prefix("local::").unwrap_or(name);
297            get_from_key(
298                &self.shared.schema["__indir"][&self.inherit.indir]["data"],
299                local_name,
300            )
301        } else {
302            get_from_key(&self.shared.schema["data"], name)
303        }
304    }
305
306    // Set key to schema data
307    //
308    // {
309    //     "config": {},
310    //     "inherit": {},
311    //     "data": {}  <-------- set to
312    // }
313    pub(crate) fn set_data(&mut self, name: &str, value: &str) {
314        self.shared.schema["data"][name] = json!(value);
315    }
316
317    // Get key from schema locale, an indirection is used instead of its initial position
318    // {
319    //     "config": {},
320    //     "inherit": {
321    //     "locale": { ------------------.
322    //        "current": "en",           |
323    //        "trans": {                 |
324    //           "es": {}                |
325    //         }                         | moved on init Template
326    //     },                            |
327    //     "data": {},                   |
328    //     "__indir": {                  |
329    //          "X": {                   |
330    //             "locale": { <---------ยท
331    //                 "trans": {
332    //                     "es": {} <----- get from
333    //     ...
334    // }
335    pub(crate) fn get_trans(&self, text: &str) -> String {
336        get_from_key(
337            &self.shared.schema["__indir"][&self.inherit.indir]["locale"]["trans"]
338                [&self.shared.lang],
339            text,
340        )
341    }
342
343    /*
344        dynamic evaluation
345
346        This is not allowed: {:;{:;refvarname:}:}
347        Use instead: {:; {:allow; allowed >> {:;refvarname:} :} :}
348    */
349    pub(crate) fn contains_allow(&self, source: &str) -> bool {
350        for allow in BIF_ALLOWED {
351            if source.contains(allow) {
352                return true;
353            }
354        }
355
356        let source = &remove_comments(source);
357        !(source.starts_with(BIF_VAR) && source.ends_with(BIF_CLOSE))
358    }
359
360    // Split params/code and parse params if parse is true.
361    // It is possible that it has no parameters, in which case
362    // it is all code and the parameters are an empty string.
363    // To set flags, parameters are required.
364    //
365    //                   .------------------------------> params
366    //                   |       .----------------------> separator
367    //                   |       |
368    //                   |       |                 .----> code
369    //                   |       |                 |
370    //                   v       v                 v
371    //              ------------ -- ------------------------------
372    //  {:!snippet; snippet_name >> <div>... {:* ... *:} ...</div> :}
373    pub(crate) fn extract_params_code(&mut self, parse: bool) -> bool {
374        let position = get_code_position(&self.src);
375        let has_code: bool = position.is_some();
376
377        if has_code {
378            let code_pos = position.unwrap();
379            self.params = self.src[0..code_pos].trim().to_string();
380            self.code = self.src[code_pos + BIF_CODE.len()..].trim().to_string();
381        } else {
382            self.params = EMPTY_STRING;
383            self.code = self.src.trim().to_string();
384        }
385
386        if parse && self.params.contains(BIF_OPEN) {
387            self.shared.flags = EMPTY_STRING;
388            self.params = new_child_parse!(self, &self.params, false);
389            self.flags = self.shared.flags.clone();
390        }
391
392        has_code
393    }
394
395    // Extract bif arguments.
396    //
397    //          .-- arg 0 empty string
398    //          | .-- arg 1
399    //          | |    .-- arg 2
400    //          | |    |     .-- arg n
401    //          | |    |     |
402    //          v v    v     v
403    // {:fetch; |url|event| ... >> ... :}
404    // result: ["", "url", "event", ""]
405    pub(crate) fn extract_args(&mut self) -> Vec<String> {
406        let mut result: Vec<String> = Vec::new();
407
408        let delim;
409        if let Some(first_char) = self.params.chars().next() {
410            delim = first_char;
411        } else {
412            return vec!["".to_string()];
413        }
414
415        let mut parts = self.params.split(delim);
416
417        while let Some(ref mut part) = parts.next() {
418            let mut arg = part.to_string();
419
420            if arg.contains(BIF_OPEN) {
421                arg = new_child_parse!(self, &arg, false);
422            }
423
424            result.push(arg);
425        }
426
427        result
428    }
429
430    pub(crate) fn bif_error(&self, msg: &str) -> BifError {
431        BifError {
432            msg: msg.to_string(),
433            name: self.alias.clone(),
434            file: self.inherit.current_file.clone(),
435            src: self.raw.to_string(),
436        }
437    }
438
439}