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 evaluated schema without $params
118    pub fn get_evaluated_schema_without_params(&mut self, skip_layout: bool) -> Value {
119        let mut schema = self.get_evaluated_schema(skip_layout);
120        if let Value::Object(ref mut map) = schema {
121            map.remove("$params");
122        }
123        schema
124    }
125
126    /// Get evaluated schema as MessagePack bytes
127    pub fn get_evaluated_schema_msgpack(&mut self, skip_layout: bool) -> Result<Vec<u8>, String> {
128        let schema = self.get_evaluated_schema(skip_layout);
129        rmp_serde::to_vec(&schema).map_err(|e| format!("MessagePack serialization failed: {}", e))
130    }
131
132    /// Get value from evaluated schema by path
133    pub fn get_evaluated_schema_by_path(&mut self, path: &str, skip_layout: bool) -> Option<Value> {
134        if !skip_layout {
135            if let Err(e) = self.resolve_layout(false) {
136                eprintln!("Warning: Layout resolution failed in get_evaluated_schema_by_path: {}", e);
137            }
138        }
139        self.get_schema_value_by_path(path)
140    }
141
142    /// Get evaluated schema parts by multiple paths
143    pub fn get_evaluated_schema_by_paths(
144        &mut self,
145        paths: &[String],
146        skip_layout: bool,
147        format: Option<ReturnFormat>,
148    ) -> Value {
149        if !skip_layout {
150            if let Err(e) = self.resolve_layout(false) {
151                eprintln!("Warning: Layout resolution failed in get_evaluated_schema_by_paths: {}", e);
152            }
153        }
154
155        match format.unwrap_or(ReturnFormat::Nested) {
156            ReturnFormat::Nested => {
157                let mut result = Value::Object(serde_json::Map::new());
158                for path in paths {
159                    if let Some(val) = self.get_schema_value_by_path(path) {
160                         // Insert into result object at proper path nesting
161                         Self::insert_at_path(&mut result, path, val);
162                    }
163                }
164                result
165            }
166            ReturnFormat::Flat => {
167                 let mut result = serde_json::Map::new();
168                 for path in paths {
169                    if let Some(val) = self.get_schema_value_by_path(path) {
170                        result.insert(path.clone(), val);
171                    }
172                }
173                Value::Object(result)
174            }
175            ReturnFormat::Array => {
176                 let mut result = Vec::new();
177                 for path in paths {
178                    if let Some(val) = self.get_schema_value_by_path(path) {
179                        result.push(val);
180                    } else {
181                        result.push(Value::Null);
182                    }
183                }
184                Value::Array(result)
185            }
186        }
187    }
188
189    /// Get original (unevaluated) schema by path
190    pub fn get_schema_by_path(&self, path: &str) -> Option<Value> {
191        let pointer_path = path_utils::dot_notation_to_schema_pointer(path);
192        self.schema.pointer(&pointer_path).cloned()
193    }
194
195    /// Get original schema by multiple paths
196    pub fn get_schema_by_paths(
197        &self,
198        paths: &[String],
199        format: Option<ReturnFormat>,
200    ) -> Value {
201        match format.unwrap_or(ReturnFormat::Nested) {
202            ReturnFormat::Nested => {
203                let mut result = Value::Object(serde_json::Map::new());
204                for path in paths {
205                    if let Some(val) = self.get_schema_by_path(path) {
206                         Self::insert_at_path(&mut result, path, val);
207                    }
208                }
209                result
210            }
211            ReturnFormat::Flat => {
212                 let mut result = serde_json::Map::new();
213                 for path in paths {
214                    if let Some(val) = self.get_schema_by_path(path) {
215                        result.insert(path.clone(), val);
216                    }
217                }
218                Value::Object(result)
219            }
220            ReturnFormat::Array => {
221                 let mut result = Vec::new();
222                 for path in paths {
223                    if let Some(val) = self.get_schema_by_path(path) {
224                        result.push(val);
225                    } else {
226                        result.push(Value::Null);
227                    }
228                }
229                Value::Array(result)
230            }
231        }
232    }
233
234    /// Helper to insert value into nested object at dotted path
235    pub(crate) fn insert_at_path(root: &mut Value, path: &str, value: Value) {
236        let parts: Vec<&str> = path.split('.').collect();
237        let mut current = root;
238        
239        for (i, part) in parts.iter().enumerate() {
240            if i == parts.len() - 1 {
241                // Last part - set value
242                if let Value::Object(map) = current {
243                    map.insert(part.to_string(), value);
244                    return; // Done
245                }
246            } else {
247                // Intermediate part - traverse or create
248                // We need to temporarily take the value or use raw pointer manipulation?
249                // serde_json pointer is read-only or requires mutable reference
250                
251                 if !current.is_object() {
252                     *current = Value::Object(serde_json::Map::new());
253                 }
254                 
255                 if let Value::Object(map) = current {
256                     if !map.contains_key(*part) {
257                         map.insert(part.to_string(), Value::Object(serde_json::Map::new()));
258                     }
259                     current = map.get_mut(*part).unwrap();
260                 }
261            }
262        }
263    }
264    
265    /// Flatten a nested object key-value pair to dotted keys
266    pub fn flatten_object(prefix: &str, value: &Value, result: &mut serde_json::Map<String, Value>) {
267        match value {
268            Value::Object(map) => {
269                for (k, v) in map {
270                     let new_key = if prefix.is_empty() {
271                         k.clone()
272                     } else {
273                         format!("{}.{}", prefix, k)
274                     };
275                     Self::flatten_object(&new_key, v, result);
276                }
277            }
278            _ => {
279                result.insert(prefix.to_string(), value.clone());
280            }
281        }
282    }
283
284    pub fn convert_to_format(value: Value, format: ReturnFormat) -> Value {
285         match format {
286             ReturnFormat::Nested => value,
287             ReturnFormat::Flat => {
288                 let mut result = serde_json::Map::new();
289                 Self::flatten_object("", &value, &mut result);
290                 Value::Object(result)
291             }
292             ReturnFormat::Array => {
293                 // Convert object values to array? Only if source was object?
294                 // Or flattened values?
295                 // Usually converting to array disregards keys.
296                 if let Value::Object(map) = value {
297                     Value::Array(map.values().cloned().collect())
298                 } else if let Value::Array(arr) = value {
299                     Value::Array(arr)
300                 } else {
301                     Value::Array(vec![value])
302                 }
303             }
304         }
305    }
306}