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;
8
9
10impl JSONEval {
11    /// Get the fully evaluated schema
12    ///
13    /// # Arguments
14    ///
15    /// * `include_hidden` - If true, hidden fields are included (but marked).
16    ///   If false, hidden fields are removed from the output.
17    pub fn get_evaluated_schema(&self, include_hidden: bool) -> Value {
18        if include_hidden {
19            self.evaluated_schema.clone()
20        } else {
21            // Filter out hidden fields
22            let mut result = self.evaluated_schema.clone();
23            Self::filter_hidden_recursive(&mut result);
24            result
25        }
26    }
27
28    /// Helper to recursively remove hidden fields from schema
29    fn filter_hidden_recursive(value: &mut Value) {
30        if let Value::Object(map) = value {
31            // Check if this object itself is hidden
32            let is_hidden = if let Some(Value::Object(condition)) = map.get("condition") {
33                condition.get("hidden") == Some(&Value::Bool(true))
34            } else {
35                false
36            };
37
38            if is_hidden {
39                // If the entire object is hidden, usually the parent removes it?
40                // But if we are iterating the map, we can't remove self easily.
41                // However, this function modifies children.
42            }
43
44            // Iterate and remove hidden children
45            // Properties
46            if let Some(Value::Object(props)) = map.get_mut("properties") {
47                let keys_to_remove: Vec<String> = props
48                    .iter()
49                    .filter_map(|(k, v)| {
50                        if let Value::Object(v_map) = v {
51                            if let Some(Value::Object(condition)) = v_map.get("condition") {
52                                if condition.get("hidden") == Some(&Value::Bool(true)) {
53                                    return Some(k.clone());
54                                }
55                            }
56                        }
57                        None
58                    })
59                    .collect();
60
61                for k in keys_to_remove {
62                    props.remove(&k);
63                }
64
65                // Recurse
66                for (_, v) in props.iter_mut() {
67                    Self::filter_hidden_recursive(v);
68                }
69            }
70            
71            // Recurse into other objects (like array items)
72            for (k, v) in map.iter_mut() {
73                 if k == "items" {
74                     Self::filter_hidden_recursive(v);
75                 } else if !k.starts_with('$') && k != "properties" && v.is_object() {
76                      // Generic recursion for nested structures
77                      Self::filter_hidden_recursive(v);
78                 }
79            }
80        } else if let Value::Array(arr) = value {
81            for v in arr {
82                Self::filter_hidden_recursive(v);
83            }
84        }
85    }
86
87    /// Get evaluated schema with layout resolution
88    pub fn get_evaluated_schema_with_layout(&self, include_hidden: bool) -> Value {
89        // Since $layout resolution is complex and done during evaluation/getters,
90        // we might return evaluated_schema directly if layouts were resolved in place?
91        // But layouts are often dynamic.
92        // For now, return standard evaluated schema, as $ref resolution in layout happens lazily/on-demand?
93        // Or is it mutated in evaluated_schema?
94        // The implementation in lib.rs typically returns evaluated_schema.
95        self.get_evaluated_schema(include_hidden)
96    }
97
98    /// Get specific schema value by path
99    pub fn get_schema_value_by_path(&self, path: &str) -> Option<Value> {
100        let pointer_path = path_utils::dot_notation_to_schema_pointer(path);
101        self.evaluated_schema.pointer(&pointer_path).cloned()
102    }
103
104    /// Get all schema values (data view)
105    /// This corresponds to subform.get_schema_value() usage
106    pub fn get_schema_value(&self) -> Value {
107        self.eval_data.data().clone()
108    }
109
110    /// Get evaluated schema without $params
111    pub fn get_evaluated_schema_without_params(&self, include_hidden: bool) -> Value {
112        let mut schema = self.get_evaluated_schema(include_hidden);
113        if let Value::Object(ref mut map) = schema {
114            map.remove("$params");
115        }
116        schema
117    }
118
119    /// Get evaluated schema as MessagePack bytes
120    pub fn get_evaluated_schema_msgpack(&self, include_hidden: bool) -> Result<Vec<u8>, String> {
121        let schema = self.get_evaluated_schema(include_hidden);
122        rmp_serde::to_vec(&schema).map_err(|e| format!("MessagePack serialization failed: {}", e))
123    }
124
125    /// Get value from evaluated schema by path
126    pub fn get_evaluated_schema_by_path(&self, path: &str, _skip_layout: bool) -> Option<Value> {
127        self.get_schema_value_by_path(path)
128    }
129
130    /// Get evaluated schema parts by multiple paths
131    pub fn get_evaluated_schema_by_paths(
132        &self,
133        paths: &[String],
134        _skip_layout: bool, // Unused for now but kept for API
135        format: Option<ReturnFormat>,
136    ) -> Value {
137        // If skip_layout is true, we might want to ensure layout is not applied or filter it?
138        // But get_evaluated_schema usually returns schema which has layout resolved?
139        // Or not?
140        // For now, ignoring skip_layout.
141        match format.unwrap_or(ReturnFormat::Nested) {
142            ReturnFormat::Nested => {
143                let mut result = Value::Object(serde_json::Map::new());
144                for path in paths {
145                    if let Some(val) = self.get_schema_value_by_path(path) {
146                         // Insert into result object at proper path nesting
147                         Self::insert_at_path(&mut result, path, val);
148                    }
149                }
150                result
151            }
152            ReturnFormat::Flat => {
153                 let mut result = serde_json::Map::new();
154                 for path in paths {
155                    if let Some(val) = self.get_schema_value_by_path(path) {
156                        result.insert(path.clone(), val);
157                    }
158                }
159                Value::Object(result)
160            }
161            ReturnFormat::Array => {
162                 let mut result = Vec::new();
163                 for path in paths {
164                    if let Some(val) = self.get_schema_value_by_path(path) {
165                        result.push(val);
166                    } else {
167                        result.push(Value::Null);
168                    }
169                }
170                Value::Array(result)
171            }
172        }
173    }
174
175    /// Get original (unevaluated) schema by path
176    pub fn get_schema_by_path(&self, path: &str) -> Option<Value> {
177        let pointer_path = path_utils::dot_notation_to_schema_pointer(path);
178        self.schema.pointer(&pointer_path).cloned()
179    }
180
181    /// Get original schema by multiple paths
182    pub fn get_schema_by_paths(
183        &self,
184        paths: &[String],
185        format: Option<ReturnFormat>,
186    ) -> Value {
187        match format.unwrap_or(ReturnFormat::Nested) {
188            ReturnFormat::Nested => {
189                let mut result = Value::Object(serde_json::Map::new());
190                for path in paths {
191                    if let Some(val) = self.get_schema_by_path(path) {
192                         Self::insert_at_path(&mut result, path, val);
193                    }
194                }
195                result
196            }
197            ReturnFormat::Flat => {
198                 let mut result = serde_json::Map::new();
199                 for path in paths {
200                    if let Some(val) = self.get_schema_by_path(path) {
201                        result.insert(path.clone(), val);
202                    }
203                }
204                Value::Object(result)
205            }
206            ReturnFormat::Array => {
207                 let mut result = Vec::new();
208                 for path in paths {
209                    if let Some(val) = self.get_schema_by_path(path) {
210                        result.push(val);
211                    } else {
212                        result.push(Value::Null);
213                    }
214                }
215                Value::Array(result)
216            }
217        }
218    }
219
220    /// Helper to insert value into nested object at dotted path
221    pub(crate) fn insert_at_path(root: &mut Value, path: &str, value: Value) {
222        let parts: Vec<&str> = path.split('.').collect();
223        let mut current = root;
224        
225        for (i, part) in parts.iter().enumerate() {
226            if i == parts.len() - 1 {
227                // Last part - set value
228                if let Value::Object(map) = current {
229                    map.insert(part.to_string(), value);
230                    return; // Done
231                }
232            } else {
233                // Intermediate part - traverse or create
234                // We need to temporarily take the value or use raw pointer manipulation?
235                // serde_json pointer is read-only or requires mutable reference
236                
237                 if !current.is_object() {
238                     *current = Value::Object(serde_json::Map::new());
239                 }
240                 
241                 if let Value::Object(map) = current {
242                     if !map.contains_key(*part) {
243                         map.insert(part.to_string(), Value::Object(serde_json::Map::new()));
244                     }
245                     current = map.get_mut(*part).unwrap();
246                 }
247            }
248        }
249    }
250    
251    /// Flatten a nested object key-value pair to dotted keys
252    pub fn flatten_object(prefix: &str, value: &Value, result: &mut serde_json::Map<String, Value>) {
253        match value {
254            Value::Object(map) => {
255                for (k, v) in map {
256                     let new_key = if prefix.is_empty() {
257                         k.clone()
258                     } else {
259                         format!("{}.{}", prefix, k)
260                     };
261                     Self::flatten_object(&new_key, v, result);
262                }
263            }
264            _ => {
265                result.insert(prefix.to_string(), value.clone());
266            }
267        }
268    }
269
270    pub fn convert_to_format(value: Value, format: ReturnFormat) -> Value {
271         match format {
272             ReturnFormat::Nested => value,
273             ReturnFormat::Flat => {
274                 let mut result = serde_json::Map::new();
275                 Self::flatten_object("", &value, &mut result);
276                 Value::Object(result)
277             }
278             ReturnFormat::Array => {
279                 // Convert object values to array? Only if source was object?
280                 // Or flattened values?
281                 // Usually converting to array disregards keys.
282                 if let Value::Object(map) = value {
283                     Value::Array(map.values().cloned().collect())
284                 } else if let Value::Array(arr) = value {
285                     Value::Array(arr)
286                 } else {
287                     Value::Array(vec![value])
288                 }
289             }
290         }
291    }
292}