Skip to main content

robinpath_modules/modules/
json_mod.rs

1use robinpath::{RobinPath, Value};
2
3pub fn register(rp: &mut RobinPath) {
4    rp.register_builtin("json.parse", |args, _| {
5        let s = args.first().map(|v| v.to_display_string()).unwrap_or_default();
6        match serde_json::from_str::<serde_json::Value>(&s) {
7            Ok(v) => Ok(Value::from(v)),
8            Err(e) => Err(format!("json.parse error: {}", e)),
9        }
10    });
11
12    rp.register_builtin("json.stringify", |args, _| {
13        let val = args.first().cloned().unwrap_or(Value::Null);
14        let indent = args.get(1).map(|v| v.to_number() as usize).unwrap_or(2);
15        let json_val: serde_json::Value = val.into();
16        if indent == 0 {
17            Ok(Value::String(serde_json::to_string(&json_val).unwrap_or_default()))
18        } else {
19            let buf = Vec::new();
20            let indent_bytes = " ".repeat(indent).into_bytes();
21            let formatter = serde_json::ser::PrettyFormatter::with_indent(&indent_bytes);
22            let mut ser = serde_json::Serializer::with_formatter(buf, formatter);
23            use serde::Serialize;
24            json_val.serialize(&mut ser).unwrap();
25            Ok(Value::String(String::from_utf8(ser.into_inner()).unwrap_or_default()))
26        }
27    });
28
29    rp.register_builtin("json.get", |args, _| {
30        let obj = args.first().cloned().unwrap_or(Value::Null);
31        let path = args.get(1).map(|v| v.to_display_string()).unwrap_or_default();
32        Ok(get_nested(&obj, &path))
33    });
34
35    rp.register_builtin("json.set", |args, _| {
36        let mut obj = args.first().cloned().unwrap_or(Value::Null);
37        let path = args.get(1).map(|v| v.to_display_string()).unwrap_or_default();
38        let value = args.get(2).cloned().unwrap_or(Value::Null);
39        set_nested(&mut obj, &path, value);
40        Ok(obj)
41    });
42
43    rp.register_builtin("json.merge", |args, _| {
44        let mut result = indexmap::IndexMap::new();
45        for arg in args {
46            if let Value::Object(obj) = arg {
47                for (k, v) in obj {
48                    if let (Some(Value::Object(existing)), Value::Object(new_obj)) =
49                        (result.get(k).cloned(), v)
50                    {
51                        let mut merged = existing;
52                        for (nk, nv) in new_obj {
53                            merged.insert(nk.clone(), nv.clone());
54                        }
55                        result.insert(k.clone(), Value::Object(merged));
56                    } else {
57                        result.insert(k.clone(), v.clone());
58                    }
59                }
60            }
61        }
62        Ok(Value::Object(result))
63    });
64
65    rp.register_builtin("json.flatten", |args, _| {
66        let obj = args.first().cloned().unwrap_or(Value::Null);
67        let mut result = indexmap::IndexMap::new();
68        flatten_value(&obj, String::new(), &mut result);
69        Ok(Value::Object(result))
70    });
71
72    rp.register_builtin("json.unflatten", |args, _| {
73        let obj = args.first().cloned().unwrap_or(Value::Null);
74        if let Value::Object(flat) = &obj {
75            let mut result = Value::Object(indexmap::IndexMap::new());
76            for (path, value) in flat {
77                set_nested(&mut result, path, value.clone());
78            }
79            Ok(result)
80        } else {
81            Ok(obj)
82        }
83    });
84
85    rp.register_builtin("json.diff", |args, _| {
86        let a = args.first().cloned().unwrap_or(Value::Null);
87        let b = args.get(1).cloned().unwrap_or(Value::Null);
88        Ok(diff_values(&a, &b))
89    });
90
91    rp.register_builtin("json.clone", |args, _| {
92        let val = args.first().cloned().unwrap_or(Value::Null);
93        // Value is Clone, so this is a deep clone
94        Ok(val)
95    });
96
97    rp.register_builtin("json.isValid", |args, _| {
98        let s = args.first().map(|v| v.to_display_string()).unwrap_or_default();
99        Ok(Value::Bool(serde_json::from_str::<serde_json::Value>(&s).is_ok()))
100    });
101
102    rp.register_builtin("json.keys", |args, _| {
103        let obj = args.first().cloned().unwrap_or(Value::Null);
104        let mut keys = Vec::new();
105        collect_keys(&obj, String::new(), &mut keys);
106        Ok(Value::Array(keys))
107    });
108
109    rp.register_builtin("json.pick", |args, _| {
110        let obj = args.first().cloned().unwrap_or(Value::Null);
111        let pick_keys = args.get(1).cloned().unwrap_or(Value::Null);
112        if let (Value::Object(map), Value::Array(keys)) = (&obj, &pick_keys) {
113            let mut result = indexmap::IndexMap::new();
114            for key_val in keys {
115                let key = key_val.to_display_string();
116                if let Some(v) = map.get(&key) {
117                    result.insert(key, v.clone());
118                }
119            }
120            Ok(Value::Object(result))
121        } else {
122            Ok(Value::Object(indexmap::IndexMap::new()))
123        }
124    });
125
126    rp.register_builtin("json.omit", |args, _| {
127        let obj = args.first().cloned().unwrap_or(Value::Null);
128        let omit_keys = args.get(1).cloned().unwrap_or(Value::Null);
129        if let (Value::Object(map), Value::Array(keys)) = (&obj, &omit_keys) {
130            let omit_set: std::collections::HashSet<String> =
131                keys.iter().map(|v| v.to_display_string()).collect();
132            let mut result = indexmap::IndexMap::new();
133            for (k, v) in map {
134                if !omit_set.contains(k) {
135                    result.insert(k.clone(), v.clone());
136                }
137            }
138            Ok(Value::Object(result))
139        } else {
140            Ok(obj)
141        }
142    });
143}
144
145fn get_nested(val: &Value, path: &str) -> Value {
146    let parts: Vec<&str> = path.split('.').collect();
147    let mut current = val.clone();
148    for part in parts {
149        match &current {
150            Value::Object(obj) => {
151                current = obj.get(part).cloned().unwrap_or(Value::Null);
152            }
153            Value::Array(arr) => {
154                if let Ok(idx) = part.parse::<usize>() {
155                    current = arr.get(idx).cloned().unwrap_or(Value::Null);
156                } else {
157                    return Value::Null;
158                }
159            }
160            _ => return Value::Null,
161        }
162    }
163    current
164}
165
166fn set_nested(val: &mut Value, path: &str, new_val: Value) {
167    let parts: Vec<&str> = path.split('.').collect();
168    let mut current = val;
169    for (i, part) in parts.iter().enumerate() {
170        if i == parts.len() - 1 {
171            if let Value::Object(obj) = current {
172                obj.insert(part.to_string(), new_val);
173                return;
174            }
175        } else {
176            if let Value::Object(obj) = current {
177                if !obj.contains_key(*part) {
178                    obj.insert(part.to_string(), Value::Object(indexmap::IndexMap::new()));
179                }
180                current = obj.get_mut(*part).unwrap();
181            } else {
182                return;
183            }
184        }
185    }
186}
187
188fn flatten_value(val: &Value, prefix: String, result: &mut indexmap::IndexMap<String, Value>) {
189    match val {
190        Value::Object(obj) => {
191            for (k, v) in obj {
192                let key = if prefix.is_empty() {
193                    k.clone()
194                } else {
195                    format!("{}.{}", prefix, k)
196                };
197                flatten_value(v, key, result);
198            }
199        }
200        Value::Array(arr) => {
201            for (i, v) in arr.iter().enumerate() {
202                let key = if prefix.is_empty() {
203                    i.to_string()
204                } else {
205                    format!("{}.{}", prefix, i)
206                };
207                flatten_value(v, key, result);
208            }
209        }
210        _ => {
211            result.insert(prefix, val.clone());
212        }
213    }
214}
215
216fn collect_keys(val: &Value, prefix: String, keys: &mut Vec<Value>) {
217    if let Value::Object(obj) = val {
218        for (k, v) in obj {
219            let key = if prefix.is_empty() {
220                k.clone()
221            } else {
222                format!("{}.{}", prefix, k)
223            };
224            keys.push(Value::String(key.clone()));
225            if matches!(v, Value::Object(_)) {
226                collect_keys(v, key, keys);
227            }
228        }
229    }
230}
231
232fn diff_values(a: &Value, b: &Value) -> Value {
233    let mut changes = Vec::new();
234    match (a, b) {
235        (Value::Object(obj_a), Value::Object(obj_b)) => {
236            for (k, v_a) in obj_a {
237                match obj_b.get(k) {
238                    Some(v_b) => {
239                        if !v_a.deep_eq(v_b) {
240                            let mut change = indexmap::IndexMap::new();
241                            change.insert("key".to_string(), Value::String(k.clone()));
242                            change.insert("type".to_string(), Value::String("changed".to_string()));
243                            change.insert("from".to_string(), v_a.clone());
244                            change.insert("to".to_string(), v_b.clone());
245                            changes.push(Value::Object(change));
246                        }
247                    }
248                    None => {
249                        let mut change = indexmap::IndexMap::new();
250                        change.insert("key".to_string(), Value::String(k.clone()));
251                        change.insert("type".to_string(), Value::String("removed".to_string()));
252                        change.insert("value".to_string(), v_a.clone());
253                        changes.push(Value::Object(change));
254                    }
255                }
256            }
257            for (k, v_b) in obj_b {
258                if !obj_a.contains_key(k) {
259                    let mut change = indexmap::IndexMap::new();
260                    change.insert("key".to_string(), Value::String(k.clone()));
261                    change.insert("type".to_string(), Value::String("added".to_string()));
262                    change.insert("value".to_string(), v_b.clone());
263                    changes.push(Value::Object(change));
264                }
265            }
266        }
267        _ => {
268            if !a.deep_eq(b) {
269                let mut change = indexmap::IndexMap::new();
270                change.insert("type".to_string(), Value::String("changed".to_string()));
271                change.insert("from".to_string(), a.clone());
272                change.insert("to".to_string(), b.clone());
273                changes.push(Value::Object(change));
274            }
275        }
276    }
277    Value::Array(changes)
278}