json_eval_rs/jsoneval/
getters.rs

1use super::JSONEval;
2use crate::jsoneval::path_utils;
3use crate::jsoneval::types::ReturnFormat;
4
5
6
7use serde_json::Value;
8use crate::time_block;
9
10
11impl JSONEval {
12    /// Get the evaluated schema with optional layout resolution.
13    ///
14    /// # Arguments
15    ///
16    /// * `skip_layout` - Whether to skip layout resolution.
17    ///
18    /// # Returns
19    ///
20    /// The evaluated schema as a JSON value.
21    pub fn get_evaluated_schema(&mut self, skip_layout: bool) -> Value {
22        time_block!("get_evaluated_schema()", {
23            if !skip_layout {
24                if let Err(e) = self.resolve_layout(false) {
25                    eprintln!("Warning: Layout resolution failed in get_evaluated_schema: {}", e);
26                }
27            }
28            self.evaluated_schema.clone()
29        })
30    }
31
32
33
34    /// Get specific schema value by path
35    pub fn get_schema_value_by_path(&self, path: &str) -> Option<Value> {
36        let pointer_path = path_utils::dot_notation_to_schema_pointer(path);
37        self.evaluated_schema.pointer(&pointer_path).cloned()
38    }
39
40    /// Get all schema values (data view)
41    /// Mutates internal data state by overriding with values from value evaluations
42    /// This corresponds to subform.get_schema_value() usage
43    pub fn get_schema_value(&mut self) -> Value {
44        // Start with current authoritative data from eval_data
45        let mut current_data = self.eval_data.data().clone();
46
47        // Ensure it's an object
48        if !current_data.is_object() {
49            current_data = Value::Object(serde_json::Map::new());
50        }
51
52        // Override data with values from value evaluations
53        // We use value_evaluations which stores the paths of fields with .value
54        for eval_key in self.value_evaluations.iter() {
55            let clean_key = eval_key.replace('#', "");
56
57            // Exclude rules.*.value, options.*.value, and $params
58            if clean_key.starts_with("/$params")
59                || (clean_key.ends_with("/value")
60                    && (clean_key.contains("/rules/") || clean_key.contains("/options/")))
61            {
62                continue;
63            }
64
65            let path = clean_key.replace("/properties", "").replace("/value", "");
66
67            // Get the value from evaluated_schema
68            let value = match self.evaluated_schema.pointer(&clean_key) {
69                Some(v) => v.clone(),
70                None => continue,
71            };
72
73            // Parse the path and create nested structure as needed
74            let path_parts: Vec<&str> = path.split('/').filter(|s| !s.is_empty()).collect();
75
76            if path_parts.is_empty() {
77                continue;
78            }
79
80            // Navigate/create nested structure
81            let mut current = &mut current_data;
82            for (i, part) in path_parts.iter().enumerate() {
83                let is_last = i == path_parts.len() - 1;
84
85                if is_last {
86                    // Set the value at the final key
87                    if let Some(obj) = current.as_object_mut() {
88                        obj.insert(part.to_string(), crate::utils::clean_float_noise(value.clone()));
89                    }
90                } else {
91                    // Ensure current is an object, then navigate/create intermediate objects
92                    if let Some(obj) = current.as_object_mut() {
93                        // Use raw entry API or standard entry if possible, but borrowing is tricky
94                        // We need to re-borrow `current` for the next iteration
95                        // Since `entry` API consumes check, we might need a different approach or careful usage
96                        
97                        // Check presence first to avoid borrow issues if simpler
98                        if !obj.contains_key(*part) {
99                            obj.insert((*part).to_string(), Value::Object(serde_json::Map::new()));
100                        }
101                        
102                        current = obj.get_mut(*part).unwrap();
103                    } else {
104                        // Skip this path if current is not an object and can't be made into one
105                        break;
106                    }
107                }
108            }
109        }
110        
111        // Update self.data to persist the view changes (matching backup behavior)
112        self.data = current_data.clone();
113        
114        crate::utils::clean_float_noise(current_data)
115    }
116
117    /// Get all schema values as array of path-value pairs
118    /// Returns [{path: "", value: ""}, ...]
119    ///
120    /// # Returns
121    ///
122    /// Array of objects containing path (dotted notation) and value pairs from value evaluations
123    pub fn get_schema_value_array(&self) -> Value {
124        let mut result = Vec::new();
125        
126        for eval_key in self.value_evaluations.iter() {
127            let clean_key = eval_key.replace('#', "");
128
129            // Exclude rules.*.value, options.*.value, and $params
130            if clean_key.starts_with("/$params")
131                || (clean_key.ends_with("/value")
132                    && (clean_key.contains("/rules/") || clean_key.contains("/options/")))
133            {
134                continue;
135            }
136
137            // Convert JSON pointer to dotted notation
138            let dotted_path = clean_key
139                .replace("/properties", "")
140                .replace("/value", "")
141                .trim_start_matches('/')
142                .replace('/', ".");
143
144            if dotted_path.is_empty() {
145                continue;
146            }
147
148            // Get the value from evaluated_schema
149            let value = match self.evaluated_schema.pointer(&clean_key) {
150                Some(v) => crate::utils::clean_float_noise(v.clone()),
151                None => continue,
152            };
153
154            // Create {path, value} object
155            let mut item = serde_json::Map::new();
156            item.insert("path".to_string(), Value::String(dotted_path));
157            item.insert("value".to_string(), value);
158            result.push(Value::Object(item));
159        }
160        
161        Value::Array(result)
162    }
163
164    /// Get all schema values as object with dotted path keys
165    /// Returns {path: value, ...}
166    ///
167    /// # Returns
168    ///
169    /// Flat object with dotted notation paths as keys and evaluated values
170    pub fn get_schema_value_object(&self) -> Value {
171        let mut result = serde_json::Map::new();
172        
173        for eval_key in self.value_evaluations.iter() {
174            let clean_key = eval_key.replace('#', "");
175
176            // Exclude rules.*.value, options.*.value, and $params
177            if clean_key.starts_with("/$params")
178                || (clean_key.ends_with("/value")
179                    && (clean_key.contains("/rules/") || clean_key.contains("/options/")))
180            {
181                continue;
182            }
183
184            // Convert JSON pointer to dotted notation
185            let dotted_path = clean_key
186                .replace("/properties", "")
187                .replace("/value", "")
188                .trim_start_matches('/')
189                .replace('/', ".");
190
191            if dotted_path.is_empty() {
192                continue;
193            }
194
195            // Get the value from evaluated_schema
196            let value = match self.evaluated_schema.pointer(&clean_key) {
197                Some(v) => crate::utils::clean_float_noise(v.clone()),
198                None => continue,
199            };
200
201            result.insert(dotted_path, value);
202        }
203        
204        Value::Object(result)
205    }
206
207    /// Get evaluated schema without $params
208    pub fn get_evaluated_schema_without_params(&mut self, skip_layout: bool) -> Value {
209        let mut schema = self.get_evaluated_schema(skip_layout);
210        if let Value::Object(ref mut map) = schema {
211            map.remove("$params");
212        }
213        schema
214    }
215
216    /// Get evaluated schema as MessagePack bytes
217    pub fn get_evaluated_schema_msgpack(&mut self, skip_layout: bool) -> Result<Vec<u8>, String> {
218        let schema = self.get_evaluated_schema(skip_layout);
219        rmp_serde::to_vec(&schema).map_err(|e| format!("MessagePack serialization failed: {}", e))
220    }
221
222    /// Get value from evaluated schema by path
223    pub fn get_evaluated_schema_by_path(&mut self, path: &str, skip_layout: bool) -> Option<Value> {
224        if !skip_layout {
225            if let Err(e) = self.resolve_layout(false) {
226                eprintln!("Warning: Layout resolution failed in get_evaluated_schema_by_path: {}", e);
227            }
228        }
229        self.get_schema_value_by_path(path)
230    }
231
232    /// Get evaluated schema parts by multiple paths
233    pub fn get_evaluated_schema_by_paths(
234        &mut self,
235        paths: &[String],
236        skip_layout: bool,
237        format: Option<ReturnFormat>,
238    ) -> Value {
239        if !skip_layout {
240            if let Err(e) = self.resolve_layout(false) {
241                eprintln!("Warning: Layout resolution failed in get_evaluated_schema_by_paths: {}", e);
242            }
243        }
244
245        match format.unwrap_or(ReturnFormat::Nested) {
246            ReturnFormat::Nested => {
247                let mut result = Value::Object(serde_json::Map::new());
248                for path in paths {
249                    if let Some(val) = self.get_schema_value_by_path(path) {
250                         // Insert into result object at proper path nesting
251                         Self::insert_at_path(&mut result, path, val);
252                    }
253                }
254                result
255            }
256            ReturnFormat::Flat => {
257                 let mut result = serde_json::Map::new();
258                 for path in paths {
259                    if let Some(val) = self.get_schema_value_by_path(path) {
260                        result.insert(path.clone(), val);
261                    }
262                }
263                Value::Object(result)
264            }
265            ReturnFormat::Array => {
266                 let mut result = Vec::new();
267                 for path in paths {
268                    if let Some(val) = self.get_schema_value_by_path(path) {
269                        result.push(val);
270                    } else {
271                        result.push(Value::Null);
272                    }
273                }
274                Value::Array(result)
275            }
276        }
277    }
278
279    /// Get original (unevaluated) schema by path
280    pub fn get_schema_by_path(&self, path: &str) -> Option<Value> {
281        let pointer_path = path_utils::dot_notation_to_schema_pointer(path);
282        self.schema.pointer(&pointer_path).cloned()
283    }
284
285    /// Get original schema by multiple paths
286    pub fn get_schema_by_paths(
287        &self,
288        paths: &[String],
289        format: Option<ReturnFormat>,
290    ) -> Value {
291        match format.unwrap_or(ReturnFormat::Nested) {
292            ReturnFormat::Nested => {
293                let mut result = Value::Object(serde_json::Map::new());
294                for path in paths {
295                    if let Some(val) = self.get_schema_by_path(path) {
296                         Self::insert_at_path(&mut result, path, val);
297                    }
298                }
299                result
300            }
301            ReturnFormat::Flat => {
302                 let mut result = serde_json::Map::new();
303                 for path in paths {
304                    if let Some(val) = self.get_schema_by_path(path) {
305                        result.insert(path.clone(), val);
306                    }
307                }
308                Value::Object(result)
309            }
310            ReturnFormat::Array => {
311                 let mut result = Vec::new();
312                 for path in paths {
313                    if let Some(val) = self.get_schema_by_path(path) {
314                        result.push(val);
315                    } else {
316                        result.push(Value::Null);
317                    }
318                }
319                Value::Array(result)
320            }
321        }
322    }
323
324    /// Helper to insert value into nested object at dotted path
325    pub(crate) fn insert_at_path(root: &mut Value, path: &str, value: Value) {
326        let parts: Vec<&str> = path.split('.').collect();
327        let mut current = root;
328        
329        for (i, part) in parts.iter().enumerate() {
330            if i == parts.len() - 1 {
331                // Last part - set value
332                if let Value::Object(map) = current {
333                    map.insert(part.to_string(), value);
334                    return; // Done
335                }
336            } else {
337                // Intermediate part - traverse or create
338                // We need to temporarily take the value or use raw pointer manipulation?
339                // serde_json pointer is read-only or requires mutable reference
340                
341                 if !current.is_object() {
342                     *current = Value::Object(serde_json::Map::new());
343                 }
344                 
345                 if let Value::Object(map) = current {
346                     if !map.contains_key(*part) {
347                         map.insert(part.to_string(), Value::Object(serde_json::Map::new()));
348                     }
349                     current = map.get_mut(*part).unwrap();
350                 }
351            }
352        }
353    }
354    
355    /// Flatten a nested object key-value pair to dotted keys
356    pub fn flatten_object(prefix: &str, value: &Value, result: &mut serde_json::Map<String, Value>) {
357        match value {
358            Value::Object(map) => {
359                for (k, v) in map {
360                     let new_key = if prefix.is_empty() {
361                         k.clone()
362                     } else {
363                         format!("{}.{}", prefix, k)
364                     };
365                     Self::flatten_object(&new_key, v, result);
366                }
367            }
368            _ => {
369                result.insert(prefix.to_string(), value.clone());
370            }
371        }
372    }
373
374    pub fn convert_to_format(value: Value, format: ReturnFormat) -> Value {
375         match format {
376             ReturnFormat::Nested => value,
377             ReturnFormat::Flat => {
378                 let mut result = serde_json::Map::new();
379                 Self::flatten_object("", &value, &mut result);
380                 Value::Object(result)
381             }
382             ReturnFormat::Array => {
383                 // Convert object values to array? Only if source was object?
384                 // Or flattened values?
385                 // Usually converting to array disregards keys.
386                 if let Value::Object(map) = value {
387                     Value::Array(map.values().cloned().collect())
388                 } else if let Value::Array(arr) = value {
389                     Value::Array(arr)
390                 } else {
391                     Value::Array(vec![value])
392                 }
393             }
394         }
395    }
396}