robinpath_modules/modules/
json_mod.rs1use 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 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 ¤t {
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}