neutralts/bif/
parse_bif_each.rs

1#![doc = include_str!("../../doc/bif-each.md")]
2
3use crate::{
4    bif::constants::*, bif::Bif, bif::BifError, json, utils::extract_blocks,
5    utils::resolve_pointer, Value,
6};
7
8impl<'a> Bif<'a> {
9    /*
10        {:each; array-name name-for-key name-for-value  >>
11            {:;name-for-key:}={:;name-for-value:}
12        :}
13    */
14    pub(crate) fn parse_bif_each(&mut self) -> Result<(), BifError> {
15        if self.mod_filter || self.mod_negate {
16            return Err(self.bif_error(BIF_ERROR_MODIFIER_NOT_ALLOWED));
17        }
18
19        self.extract_params_code(true);
20
21        if !self.flags.is_empty() {
22            return Err(self.bif_error(BIF_ERROR_FLAGS_NOT_ALLOWED));
23        }
24
25        let mut parts = self.params.split_whitespace();
26
27        let array_name = match parts.next() {
28            Some(value) => value.to_string(),
29            None => {
30                return Err(self.bif_error(BIF_ERROR_ARGUMENTS_NOT_FOUND));
31            }
32        };
33
34        let key_name = match parts.next() {
35            Some(value) => value.to_string(),
36            None => {
37                return Err(self.bif_error(BIF_ERROR_ARGS_KEY_NOT_FOUND));
38            }
39        };
40
41        let val_name = match parts.next() {
42            Some(value) => value.to_string(),
43            None => {
44                return Err(self.bif_error(BIF_ERROR_ARGS_VALUE_NOT_FOUND));
45            }
46        };
47
48        if key_name.starts_with("local::") || val_name.starts_with("local::") {
49            return Err(self.bif_error(BIF_ERROR_INSECURE_VARNAME));
50        }
51
52        let restore_key = self.shared.schema["data"][&key_name].clone();
53        let restore_val = self.shared.schema["data"][&val_name].clone();
54
55        let data_storage = if array_name.starts_with("local::") {
56            &self.shared.get_indir(&self.inherit.indir)["data"]
57        } else {
58            &self.shared.schema["data"]
59        };
60
61        let array_clean = array_name.strip_prefix("local::").unwrap_or(&array_name);
62
63        let collection = if let Some(data_value) = resolve_pointer(data_storage, array_clean) {
64            data_value.clone()
65        } else {
66            Value::Null
67        };
68
69        let blocks = match extract_blocks(&self.code) {
70            Ok(b) => b,
71            Err(p) => return Err(self.bif_error(&format!("Unmatched block at position {}", p))),
72        };
73
74        match collection {
75            Value::Object(obj) => {
76                for (key, val) in obj.iter() {
77                    self.parse_bif_each_iter(&key_name, &val_name, key, val, &blocks);
78                }
79            }
80            Value::Array(arr) => {
81                for (idx, val) in arr.iter().enumerate() {
82                    self.parse_bif_each_iter(&key_name, &val_name, &idx.to_string(), val, &blocks);
83                }
84            }
85            _ => {}
86        }
87
88        self.shared.schema["data"][&key_name] = restore_key;
89        self.shared.schema["data"][&val_name] = restore_val;
90
91        Ok(())
92    }
93
94    fn parse_bif_each_iter(
95        &mut self,
96        key_name: &str,
97        val_name: &str,
98        key: &String,
99        val: &Value,
100        blocks: &Vec<(usize, usize)>,
101    ) {
102        self.shared.schema["data"][key_name] = json!(key);
103        self.shared.schema["data"][val_name] = json!(val);
104
105        let mut child_inherit = self.inherit.clone();
106        child_inherit.alias = self.alias.clone();
107        if !self.file_path.is_empty() {
108            child_inherit.current_file = self.file_path.clone();
109        }
110        if !self.dir.is_empty() {
111            child_inherit.current_dir = self.dir.clone();
112        }
113
114        if self.mod_scope {
115            self.inherit.create_block_schema(self.shared);
116        }
117
118        let mut block_parser = crate::block_parser::BlockParser::new(self.shared, child_inherit);
119        let code = block_parser.parse_with_blocks(&self.code, blocks, self.only);
120
121        if self.mod_scope {
122            block_parser.update_indir(&self.inherit.indir);
123        }
124
125        self.out += &code;
126    }
127}
128
129#[cfg(test)]
130#[path = "parse_bif_each_tests.rs"]
131mod tests;