Skip to main content

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