robinpath_modules/modules/
transform_mod.rs1use robinpath::{RobinPath, Value};
2
3pub fn register(rp: &mut RobinPath) {
4 rp.register_builtin("transform.pick", |args, _| {
6 let data = args.first().cloned().unwrap_or(Value::Null);
7 let keys = args.get(1).cloned().unwrap_or(Value::Null);
8 if let (Value::Object(obj), Value::Array(key_list)) = (&data, &keys) {
9 let mut result = indexmap::IndexMap::new();
10 for key_val in key_list {
11 let key = key_val.to_display_string();
12 if key.contains('.') {
13 let val = get_nested(&data, &key);
15 if !matches!(val, Value::Null) {
16 result.insert(key, val);
17 }
18 } else if let Some(v) = obj.get(&key) {
19 result.insert(key, v.clone());
20 }
21 }
22 Ok(Value::Object(result))
23 } else {
24 Ok(data)
25 }
26 });
27
28 rp.register_builtin("transform.omit", |args, _| {
30 let data = args.first().cloned().unwrap_or(Value::Null);
31 let keys = args.get(1).cloned().unwrap_or(Value::Null);
32 if let (Value::Object(obj), Value::Array(key_list)) = (&data, &keys) {
33 let omit_keys: Vec<String> = key_list.iter().map(|v| v.to_display_string()).collect();
34 let mut result = indexmap::IndexMap::new();
35 for (k, v) in obj {
36 if !omit_keys.contains(k) {
37 result.insert(k.clone(), v.clone());
38 }
39 }
40 Ok(Value::Object(result))
41 } else {
42 Ok(data)
43 }
44 });
45
46 rp.register_builtin("transform.rename", |args, _| {
48 let data = args.first().cloned().unwrap_or(Value::Null);
49 let mapping = args.get(1).cloned().unwrap_or(Value::Null);
50 if let (Value::Object(obj), Value::Object(map)) = (&data, &mapping) {
51 let mut result = indexmap::IndexMap::new();
52 for (k, v) in obj {
53 let new_key = map.get(k).map(|m| m.to_display_string()).unwrap_or_else(|| k.clone());
54 result.insert(new_key, v.clone());
55 }
56 Ok(Value::Object(result))
57 } else {
58 Ok(data)
59 }
60 });
61
62 rp.register_builtin("transform.mapValues", |args, _| {
64 let data = args.first().cloned().unwrap_or(Value::Null);
65 let mapping = args.get(1).cloned().unwrap_or(Value::Null);
66 if let (Value::Object(obj), Value::Object(map)) = (&data, &mapping) {
67 let mut result = indexmap::IndexMap::new();
68 for (k, v) in obj {
69 let transformed = if let Some(Value::String(transform)) = map.get(k) {
70 apply_transform(v, transform)
71 } else {
72 v.clone()
73 };
74 result.insert(k.clone(), transformed);
75 }
76 Ok(Value::Object(result))
77 } else {
78 Ok(data)
79 }
80 });
81
82 rp.register_builtin("transform.coerce", |args, _| {
84 let value = args.first().cloned().unwrap_or(Value::Null);
85 let target_type = args.get(1).map(|v| v.to_display_string()).unwrap_or_default();
86 Ok(coerce_value(&value, &target_type))
87 });
88
89 rp.register_builtin("transform.flatten", |args, _| {
91 let data = args.first().cloned().unwrap_or(Value::Null);
92 let sep = args.get(1).map(|v| v.to_display_string()).unwrap_or_else(|| ".".to_string());
93 let mut result = indexmap::IndexMap::new();
94 flatten_object(&data, "", &sep, &mut result);
95 Ok(Value::Object(result))
96 });
97
98 rp.register_builtin("transform.unflatten", |args, _| {
100 let data = args.first().cloned().unwrap_or(Value::Null);
101 let sep = args.get(1).map(|v| v.to_display_string()).unwrap_or_else(|| ".".to_string());
102 if let Value::Object(obj) = data {
103 let mut result = indexmap::IndexMap::new();
104 for (key, value) in obj {
105 set_nested_path(&mut result, &key, &sep, value);
106 }
107 Ok(Value::Object(result))
108 } else {
109 Ok(data)
110 }
111 });
112
113 rp.register_builtin("transform.merge", |args, _| {
115 let mut result = indexmap::IndexMap::new();
116 for arg in args {
117 if let Value::Object(obj) = arg {
118 deep_merge(&mut result, obj);
119 }
120 }
121 Ok(Value::Object(result))
122 });
123
124 rp.register_builtin("transform.defaults", |args, _| {
126 let data = args.first().cloned().unwrap_or(Value::Null);
127 let defaults = args.get(1).cloned().unwrap_or(Value::Null);
128 if let (Value::Object(mut obj), Value::Object(defs)) = (data, defaults) {
129 for (k, v) in defs {
130 if !obj.contains_key(&k) || matches!(obj.get(&k), Some(Value::Null)) {
131 obj.insert(k, v);
132 }
133 }
134 Ok(Value::Object(obj))
135 } else {
136 Ok(args.first().cloned().unwrap_or(Value::Null))
137 }
138 });
139
140 rp.register_builtin("transform.template", |args, _| {
142 let template = args.first().map(|v| v.to_display_string()).unwrap_or_default();
143 let data = args.get(1).cloned().unwrap_or(Value::Null);
144 let mut result = template.clone();
145 if let Value::Object(obj) = &data {
146 for (key, value) in obj {
147 let placeholder = format!("{{{{{}}}}}", key);
148 result = result.replace(&placeholder, &value.to_display_string());
149 }
150 }
151 Ok(Value::String(result))
152 });
153
154 rp.register_builtin("transform.group", |args, _| {
156 let data = args.first().cloned().unwrap_or(Value::Null);
157 let key = args.get(1).map(|v| v.to_display_string()).unwrap_or_default();
158 if let Value::Array(arr) = data {
159 let mut groups: indexmap::IndexMap<String, Vec<Value>> = indexmap::IndexMap::new();
160 for item in arr {
161 let group_val = if let Value::Object(obj) = &item {
162 obj.get(&key).map(|v| v.to_display_string()).unwrap_or_else(|| "undefined".to_string())
163 } else {
164 "undefined".to_string()
165 };
166 groups.entry(group_val).or_default().push(item);
167 }
168 let mut result = indexmap::IndexMap::new();
169 for (k, v) in groups {
170 result.insert(k, Value::Array(v));
171 }
172 Ok(Value::Object(result))
173 } else {
174 Ok(data)
175 }
176 });
177
178 rp.register_builtin("transform.mapArray", |args, _| {
180 let data = args.first().cloned().unwrap_or(Value::Null);
181 let mapping = args.get(1).cloned().unwrap_or(Value::Null);
182 if let (Value::Array(arr), Value::Object(map)) = (data, mapping) {
183 let result: Vec<Value> = arr.iter().map(|item| {
184 if let Value::Object(obj) = item {
185 let mut new_obj = indexmap::IndexMap::new();
186 for (new_key, source) in &map {
187 let source_key = source.to_display_string();
188 let val = obj.get(&source_key).cloned().unwrap_or(Value::Null);
189 new_obj.insert(new_key.clone(), val);
190 }
191 Value::Object(new_obj)
192 } else {
193 item.clone()
194 }
195 }).collect();
196 Ok(Value::Array(result))
197 } else {
198 Ok(args.first().cloned().unwrap_or(Value::Null))
199 }
200 });
201
202 rp.register_builtin("transform.filter", |args, _| {
204 let data = args.first().cloned().unwrap_or(Value::Null);
205 let conditions = args.get(1).cloned().unwrap_or(Value::Null);
206 if let (Value::Array(arr), Value::Object(conds)) = (data, conditions) {
207 let result: Vec<Value> = arr.into_iter().filter(|item| {
208 if let Value::Object(obj) = item {
209 conds.iter().all(|(k, v)| {
210 obj.get(k).map(|actual| actual.to_display_string() == v.to_display_string()).unwrap_or(false)
211 })
212 } else {
213 false
214 }
215 }).collect();
216 Ok(Value::Array(result))
217 } else {
218 Ok(args.first().cloned().unwrap_or(Value::Null))
219 }
220 });
221
222 rp.register_builtin("transform.sort", |args, _| {
224 let data = args.first().cloned().unwrap_or(Value::Null);
225 let key = args.get(1).map(|v| v.to_display_string()).unwrap_or_default();
226 let order = args.get(2).map(|v| v.to_display_string()).unwrap_or_else(|| "asc".to_string());
227 if let Value::Array(mut arr) = data {
228 arr.sort_by(|a, b| {
229 let av = if let Value::Object(obj) = a { obj.get(&key).cloned().unwrap_or(Value::Null) } else { Value::Null };
230 let bv = if let Value::Object(obj) = b { obj.get(&key).cloned().unwrap_or(Value::Null) } else { Value::Null };
231 let cmp = match (&av, &bv) {
232 (Value::Number(a), Value::Number(b)) => a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal),
233 _ => av.to_display_string().cmp(&bv.to_display_string()),
234 };
235 if order == "desc" { cmp.reverse() } else { cmp }
236 });
237 Ok(Value::Array(arr))
238 } else {
239 Ok(data)
240 }
241 });
242}
243
244fn get_nested(val: &Value, path: &str) -> Value {
245 let mut current = val.clone();
246 for part in path.split('.') {
247 if let Value::Object(obj) = ¤t {
248 current = obj.get(part).cloned().unwrap_or(Value::Null);
249 } else {
250 return Value::Null;
251 }
252 }
253 current
254}
255
256fn apply_transform(val: &Value, transform: &str) -> Value {
257 match transform {
258 "toString" => Value::String(val.to_display_string()),
259 "toNumber" => Value::Number(val.to_number()),
260 "toBoolean" => Value::Bool(match val {
261 Value::Bool(b) => *b,
262 Value::Number(n) => *n != 0.0,
263 Value::String(s) => !s.is_empty() && s != "false" && s != "0",
264 Value::Null => false,
265 _ => true,
266 }),
267 "toUpperCase" => Value::String(val.to_display_string().to_uppercase()),
268 "toLowerCase" => Value::String(val.to_display_string().to_lowercase()),
269 "trim" => Value::String(val.to_display_string().trim().to_string()),
270 _ => val.clone(),
271 }
272}
273
274fn coerce_value(val: &Value, target: &str) -> Value {
275 match target {
276 "string" => Value::String(val.to_display_string()),
277 "number" | "float" => Value::Number(val.to_number()),
278 "integer" => Value::Number((val.to_number() as i64) as f64),
279 "boolean" => Value::Bool(match val {
280 Value::Bool(b) => *b,
281 Value::Number(n) => *n != 0.0,
282 Value::String(s) => s == "true" || s == "1",
283 Value::Null => false,
284 _ => true,
285 }),
286 "array" => match val {
287 Value::Array(_) => val.clone(),
288 _ => Value::Array(vec![val.clone()]),
289 },
290 _ => val.clone(),
291 }
292}
293
294fn flatten_object(val: &Value, prefix: &str, sep: &str, result: &mut indexmap::IndexMap<String, Value>) {
295 match val {
296 Value::Object(obj) => {
297 for (k, v) in obj {
298 let key = if prefix.is_empty() { k.clone() } else { format!("{}{}{}", prefix, sep, k) };
299 flatten_object(v, &key, sep, result);
300 }
301 }
302 _ => {
303 result.insert(prefix.to_string(), val.clone());
304 }
305 }
306}
307
308fn set_nested_path(obj: &mut indexmap::IndexMap<String, Value>, path: &str, sep: &str, value: Value) {
309 let parts: Vec<&str> = path.split(sep).collect();
310 if parts.len() == 1 {
311 obj.insert(path.to_string(), value);
312 return;
313 }
314 let key = parts[0].to_string();
315 let rest = parts[1..].join(sep);
316 if !obj.contains_key(&key) {
317 obj.insert(key.clone(), Value::Object(indexmap::IndexMap::new()));
318 }
319 if let Some(Value::Object(inner)) = obj.get_mut(&key) {
320 set_nested_path(inner, &rest, sep, value);
321 }
322}
323
324fn deep_merge(target: &mut indexmap::IndexMap<String, Value>, source: &indexmap::IndexMap<String, Value>) {
325 for (k, v) in source {
326 if let (Some(Value::Object(existing)), Value::Object(incoming)) = (target.get_mut(k), v) {
327 deep_merge(existing, incoming);
328 } else {
329 target.insert(k.clone(), v.clone());
330 }
331 }
332}