neutralts/bif/
parse_bif_obj.rs1#![doc = include_str!("../../doc/bif-obj.md")]
2
3use crate::{
4 bif::{constants::*, Bif, BifError, PythonExecutor},
5 constants::*,
6 utils::is_empty_key,
7 Value,
8};
9use std::fs;
10use std::path::Path;
11
12impl<'a> Bif<'a> {
13 pub(crate) fn parse_bif_obj(&mut self) -> Result<(), BifError> {
18 if self.mod_filter || self.mod_negate {
19 return Err(self.bif_error(BIF_ERROR_MODIFIER_NOT_ALLOWED));
20 }
21
22 let mut added_bif_code = false;
23 if !self.src.contains(BIF_CODE) {
24 self.src.push_str(BIF_CODE);
25 added_bif_code = true;
26 }
27
28 self.extract_params_code(false);
29
30 if added_bif_code {
31 self.src.truncate(self.src.len() - BIF_CODE.len());
32 }
33
34 if !self.flags.is_empty() {
35 return Err(self.bif_error(BIF_ERROR_FLAGS_NOT_ALLOWED));
36 }
37
38 let obj_raw;
39 if self.params.starts_with('{')
40 && self.params.ends_with('}')
41 && self.params.chars().nth(1) != Some(':')
42 && self.params.chars().nth(self.params.len() - 2) != Some(':')
43 {
44 obj_raw = self.params.clone();
46 } else {
47 if self.params.contains(BIF_OPEN) {
48 self.params = new_child_parse!(self, &self.params, false);
49 }
50
51 self.file_path = self.params.clone();
52
53 if self.file_path.contains(BIF_OPEN) {
55 if !self.contains_allow(&self.file_path) {
56 return Err(self.bif_error(BIF_ERROR_INSECURE_FILE_NAME));
57 }
58 self.file_path = new_child_parse!(self, &self.params, false);
59 }
60
61 if let Some(stripped) = self.file_path.strip_prefix('#') {
62 self.file_path = format!("{}{}", self.inherit.current_dir, stripped);
63 }
64
65 let path = Path::new(&self.file_path);
66 if !path.exists() {
67 return Err(self.bif_error(BIF_ERROR_FILE_NOT_FOUND));
68 }
69
70 obj_raw = fs::read_to_string(&self.file_path)
71 .map_err(|e| self.bif_error(&format!("Failed to read file: {}", e)))?;
72 }
73
74 let mut obj: Value = serde_json::from_str(obj_raw.trim())
75 .map_err(|e| self.bif_error(&format!("Failed to parse JSON: {}", e)))?;
76
77 let engine = obj["engine"].as_str().unwrap_or(DEFAULT_OBJ_ENGINE);
78 if engine.to_lowercase() != "python" {
79 return Err(self.bif_error(BIF_ERROR_ONLY_PYTHON_ENGINE));
81 }
82
83 if !self.flags.contains("|inline|") {
84 self.parse_obj_values(&mut obj, false);
85 }
86
87 let mut file_path_obj = obj["file"].as_str().unwrap_or("").to_string();
88
89 if let Some(stripped) = file_path_obj.strip_prefix('#') {
90 file_path_obj = format!("{}{}", self.inherit.current_dir, stripped);
91 }
92
93 if !Path::new(&file_path_obj).exists() {
94 return Err(self.bif_error(BIF_ERROR_OBJ_FILE_NOT_FOUND));
95 }
96
97 let mut venv_path = obj["venv"].as_str().unwrap_or("").to_string();
98
99 if !venv_path.is_empty() {
100 if let Some(stripped) = venv_path.strip_prefix('#') {
101 venv_path = format!("{}{}", self.inherit.current_dir, stripped);
102 }
103
104 if !Path::new(&venv_path).exists() {
105 return Err(self.bif_error("venv path does not exist"));
106 }
107 }
108
109 let result = PythonExecutor::exec_py(
110 &file_path_obj,
111 &obj["params"],
112 obj["callback"].as_str().unwrap_or(DEFAULT_OBJ_CALLBACK),
113 if obj.get("schema").and_then(|v| v.as_bool()).unwrap_or(false) {
114 Some(&self.shared.schema)
115 } else {
116 None
117 },
118 if venv_path.is_empty() {
119 None
120 } else {
121 Some(venv_path.as_str())
122 },
123 )?;
124
125 let mut code = String::new();
126 if !is_empty_key(&result, "data") {
127 let data = serde_json::to_string(&result).unwrap();
128 code = String::from("{:data;{:flg; inline :}>>") + &data + ":}";
129 }
130 if !is_empty_key(&obj, "template") {
131 let template = obj["template"].as_str().unwrap();
132 code = code + "{:include;" + template + ":}";
133 }
134 self.code = code + &self.code.clone();
135
136 if self.code.contains(BIF_OPEN) {
137 self.out = new_child_parse!(self, &self.code, self.mod_scope);
138 } else {
139 self.out = self.code.clone();
140 }
141
142 Ok(())
143 }
144
145 fn parse_obj_values(&mut self, value: &mut Value, is_recursive_call: bool) {
146 if let Value::Object(map) = value {
147 for (key, val) in map.iter_mut() {
148 if key == "file" || key == "template" || key == "venv" {
149 if let Value::String(s) = val {
150 if s.contains(BIF_OPEN) {
151 *val = Value::String(new_child_parse!(self, s, false));
152 }
153 }
154 } else if key == "params" || is_recursive_call {
155 if let Value::String(s) = val {
157 if s.contains(BIF_OPEN) {
158 *val = Value::String(new_child_parse!(self, s, false));
159 }
160 } else if let Value::Object(_) = val {
161 self.parse_obj_values(val, true);
162 }
163 }
164 }
165 }
166 }
167}
168
169#[cfg(test)]
170#[path = "parse_bif_obj_tests.rs"]
171mod tests;