neutralts/bif/
mod.rs

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