1use std::collections::HashSet;
4
5use heck::{
6 ToKebabCase, ToLowerCamelCase, ToShoutyKebabCase, ToShoutySnakeCase, ToSnakeCase, ToTrainCase,
7 ToUpperCamelCase,
8};
9use serde_json::{Map, Number, Value};
10
11use crate::functions::{Function, custom_error, number_value};
12use crate::interpreter::SearchResult;
13use crate::registry::register_if_enabled;
14use crate::{Context, Runtime, arg, defn};
15
16pub fn register_filtered(runtime: &mut Runtime, enabled: &HashSet<&str>) {
18 register_if_enabled(runtime, "items", enabled, Box::new(EntriesFn::new()));
19 register_if_enabled(
20 runtime,
21 "from_items",
22 enabled,
23 Box::new(FromEntriesFn::new()),
24 );
25 register_if_enabled(
26 runtime,
27 "from_entries",
28 enabled,
29 Box::new(FromEntriesFn::new()),
30 );
31 register_if_enabled(
32 runtime,
33 "with_entries",
34 enabled,
35 Box::new(WithEntriesFn::new()),
36 );
37 register_if_enabled(runtime, "pick", enabled, Box::new(PickFn::new()));
38 register_if_enabled(runtime, "omit", enabled, Box::new(OmitFn::new()));
39 register_if_enabled(runtime, "invert", enabled, Box::new(InvertFn::new()));
40 register_if_enabled(
41 runtime,
42 "rename_keys",
43 enabled,
44 Box::new(RenameKeysFn::new()),
45 );
46 register_if_enabled(
47 runtime,
48 "flatten_keys",
49 enabled,
50 Box::new(FlattenKeysFn::new()),
51 );
52 register_if_enabled(runtime, "flatten", enabled, Box::new(FlattenKeysFn::new()));
53 register_if_enabled(
54 runtime,
55 "unflatten_keys",
56 enabled,
57 Box::new(UnflattenKeysFn::new()),
58 );
59 register_if_enabled(
60 runtime,
61 "unflatten",
62 enabled,
63 Box::new(UnflattenKeysFn::new()),
64 );
65 register_if_enabled(
66 runtime,
67 "flatten_array",
68 enabled,
69 Box::new(FlattenArrayFn::new()),
70 );
71 register_if_enabled(runtime, "deep_merge", enabled, Box::new(DeepMergeFn::new()));
72 register_if_enabled(
73 runtime,
74 "deep_equals",
75 enabled,
76 Box::new(DeepEqualsFn::new()),
77 );
78 register_if_enabled(runtime, "deep_diff", enabled, Box::new(DeepDiffFn::new()));
79 register_if_enabled(runtime, "get", enabled, Box::new(GetFn::new()));
80 register_if_enabled(runtime, "get_path", enabled, Box::new(GetFn::new()));
81 register_if_enabled(runtime, "has", enabled, Box::new(HasFn::new()));
82 register_if_enabled(runtime, "has_path", enabled, Box::new(HasFn::new()));
83 register_if_enabled(runtime, "defaults", enabled, Box::new(DefaultsFn::new()));
84 register_if_enabled(
85 runtime,
86 "defaults_deep",
87 enabled,
88 Box::new(DefaultsDeepFn::new()),
89 );
90 register_if_enabled(runtime, "set_path", enabled, Box::new(SetPathFn::new()));
91 register_if_enabled(
92 runtime,
93 "delete_path",
94 enabled,
95 Box::new(DeletePathFn::new()),
96 );
97 register_if_enabled(runtime, "paths", enabled, Box::new(PathsFn::new()));
98 register_if_enabled(runtime, "leaves", enabled, Box::new(LeavesFn::new()));
99 register_if_enabled(
100 runtime,
101 "leaves_with_paths",
102 enabled,
103 Box::new(LeavesWithPathsFn::new()),
104 );
105 register_if_enabled(
106 runtime,
107 "remove_nulls",
108 enabled,
109 Box::new(RemoveNullsFn::new()),
110 );
111 register_if_enabled(
112 runtime,
113 "remove_empty",
114 enabled,
115 Box::new(RemoveEmptyFn::new()),
116 );
117 register_if_enabled(
118 runtime,
119 "remove_empty_strings",
120 enabled,
121 Box::new(RemoveEmptyStringsFn::new()),
122 );
123 register_if_enabled(
124 runtime,
125 "compact_deep",
126 enabled,
127 Box::new(CompactDeepFn::new()),
128 );
129 register_if_enabled(
130 runtime,
131 "completeness",
132 enabled,
133 Box::new(CompletenessFn::new()),
134 );
135 register_if_enabled(
136 runtime,
137 "type_consistency",
138 enabled,
139 Box::new(TypeConsistencyFn::new()),
140 );
141 register_if_enabled(
142 runtime,
143 "data_quality_score",
144 enabled,
145 Box::new(DataQualityScoreFn::new()),
146 );
147 register_if_enabled(runtime, "redact", enabled, Box::new(RedactFn::new()));
148 register_if_enabled(
149 runtime,
150 "redact_keys",
151 enabled,
152 Box::new(RedactKeysFn::new()),
153 );
154 register_if_enabled(runtime, "mask", enabled, Box::new(MaskFn::new()));
155 register_if_enabled(runtime, "pluck_deep", enabled, Box::new(PluckDeepFn::new()));
156 register_if_enabled(runtime, "paths_to", enabled, Box::new(PathsToFn::new()));
157 register_if_enabled(runtime, "snake_keys", enabled, Box::new(SnakeKeysFn::new()));
158 register_if_enabled(runtime, "camel_keys", enabled, Box::new(CamelKeysFn::new()));
159 register_if_enabled(runtime, "kebab_keys", enabled, Box::new(KebabKeysFn::new()));
160 register_if_enabled(
161 runtime,
162 "pascal_keys",
163 enabled,
164 Box::new(PascalKeysFn::new()),
165 );
166 register_if_enabled(
167 runtime,
168 "shouty_snake_keys",
169 enabled,
170 Box::new(ShoutySnakeKeysFn::new()),
171 );
172 register_if_enabled(
173 runtime,
174 "shouty_kebab_keys",
175 enabled,
176 Box::new(ShoutyKebabKeysFn::new()),
177 );
178 register_if_enabled(runtime, "train_keys", enabled, Box::new(TrainKeysFn::new()));
179 register_if_enabled(
180 runtime,
181 "structural_diff",
182 enabled,
183 Box::new(StructuralDiffFn::new()),
184 );
185 register_if_enabled(
186 runtime,
187 "has_same_shape",
188 enabled,
189 Box::new(HasSameShapeFn::new()),
190 );
191 register_if_enabled(
192 runtime,
193 "infer_schema",
194 enabled,
195 Box::new(InferSchemaFn::new()),
196 );
197 register_if_enabled(
198 runtime,
199 "chunk_by_size",
200 enabled,
201 Box::new(ChunkBySizeFn::new()),
202 );
203 register_if_enabled(runtime, "paginate", enabled, Box::new(PaginateFn::new()));
204 register_if_enabled(
205 runtime,
206 "estimate_size",
207 enabled,
208 Box::new(EstimateSizeFn::new()),
209 );
210 register_if_enabled(
211 runtime,
212 "truncate_to_size",
213 enabled,
214 Box::new(TruncateToSizeFn::new()),
215 );
216 register_if_enabled(runtime, "template", enabled, Box::new(TemplateFn::new()));
217 register_if_enabled(
218 runtime,
219 "template_strict",
220 enabled,
221 Box::new(TemplateStrictFn::new()),
222 );
223}
224
225defn!(EntriesFn, vec![arg!(object)], None);
230
231impl Function for EntriesFn {
232 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
233 self.signature.validate(args, ctx)?;
234
235 let obj = args[0]
236 .as_object()
237 .ok_or_else(|| custom_error(ctx, "Expected object argument"))?;
238
239 let entries: Vec<Value> = obj
240 .iter()
241 .map(|(k, v)| {
242 let pair = vec![Value::String(k.clone()), v.clone()];
243 Value::Array(pair)
244 })
245 .collect();
246
247 Ok(Value::Array(entries))
248 }
249}
250
251defn!(FromEntriesFn, vec![arg!(array)], None);
256
257impl Function for FromEntriesFn {
258 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
259 self.signature.validate(args, ctx)?;
260
261 let arr = args[0]
262 .as_array()
263 .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
264
265 let mut result = Map::new();
266
267 for item in arr {
268 if let Some(pair) = item.as_array()
269 && pair.len() >= 2
270 && let Some(key_str) = pair[0].as_str()
271 {
272 result.insert(key_str.to_string(), pair[1].clone());
273 }
274 }
275
276 Ok(Value::Object(result))
277 }
278}
279
280defn!(WithEntriesFn, vec![arg!(object), arg!(string)], None);
285
286impl Function for WithEntriesFn {
287 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
288 self.signature.validate(args, ctx)?;
289
290 let obj = args[0]
291 .as_object()
292 .ok_or_else(|| custom_error(ctx, "Expected object argument"))?;
293
294 let expr_str = args[1]
295 .as_str()
296 .ok_or_else(|| custom_error(ctx, "Expected expression string"))?;
297
298 let compiled = ctx
299 .runtime
300 .compile(expr_str)
301 .map_err(|_| custom_error(ctx, "Invalid expression in with_entries"))?;
302
303 let mut result = Map::new();
304
305 for (key, value) in obj.iter() {
306 let entry = Value::Array(vec![Value::String(key.clone()), value.clone()]);
307
308 let transformed = compiled
309 .search(&entry)
310 .map_err(|_| custom_error(ctx, "Expression error in with_entries"))?;
311
312 if transformed.is_null() {
313 continue;
314 }
315
316 if let Some(pair) = transformed.as_array()
317 && pair.len() >= 2
318 && let Some(new_key) = pair[0].as_str()
319 {
320 result.insert(new_key.to_string(), pair[1].clone());
321 }
322 }
323
324 Ok(Value::Object(result))
325 }
326}
327
328defn!(PickFn, vec![arg!(object), arg!(array)], None);
333
334impl Function for PickFn {
335 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
336 self.signature.validate(args, ctx)?;
337
338 let obj = args[0]
339 .as_object()
340 .ok_or_else(|| custom_error(ctx, "Expected object argument"))?;
341
342 let keys_arr = args[1]
343 .as_array()
344 .ok_or_else(|| custom_error(ctx, "Expected array of keys"))?;
345
346 let keys: HashSet<String> = keys_arr
347 .iter()
348 .filter_map(|k| k.as_str().map(|s| s.to_string()))
349 .collect();
350
351 let result: Map<String, Value> = obj
352 .iter()
353 .filter(|(k, _)| keys.contains(k.as_str()))
354 .map(|(k, v)| (k.clone(), v.clone()))
355 .collect();
356
357 Ok(Value::Object(result))
358 }
359}
360
361defn!(OmitFn, vec![arg!(object), arg!(array)], None);
366
367impl Function for OmitFn {
368 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
369 self.signature.validate(args, ctx)?;
370
371 let obj = args[0]
372 .as_object()
373 .ok_or_else(|| custom_error(ctx, "Expected object argument"))?;
374
375 let keys_arr = args[1]
376 .as_array()
377 .ok_or_else(|| custom_error(ctx, "Expected array of keys"))?;
378
379 let keys: HashSet<String> = keys_arr
380 .iter()
381 .filter_map(|k| k.as_str().map(|s| s.to_string()))
382 .collect();
383
384 let result: Map<String, Value> = obj
385 .iter()
386 .filter(|(k, _)| !keys.contains(k.as_str()))
387 .map(|(k, v)| (k.clone(), v.clone()))
388 .collect();
389
390 Ok(Value::Object(result))
391 }
392}
393
394defn!(InvertFn, vec![arg!(object)], None);
399
400impl Function for InvertFn {
401 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
402 self.signature.validate(args, ctx)?;
403
404 let obj = args[0]
405 .as_object()
406 .ok_or_else(|| custom_error(ctx, "Expected object argument"))?;
407
408 let mut result = Map::new();
409
410 for (k, v) in obj.iter() {
411 let new_key = match v {
412 Value::String(s) => s.clone(),
413 Value::Number(n) => n.to_string(),
414 Value::Bool(b) => b.to_string(),
415 Value::Null => "null".to_string(),
416 _ => continue,
417 };
418 result.insert(new_key, Value::String(k.clone()));
419 }
420
421 Ok(Value::Object(result))
422 }
423}
424
425defn!(RenameKeysFn, vec![arg!(object), arg!(object)], None);
430
431impl Function for RenameKeysFn {
432 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
433 self.signature.validate(args, ctx)?;
434
435 let obj = args[0]
436 .as_object()
437 .ok_or_else(|| custom_error(ctx, "Expected object argument"))?;
438
439 let mapping = args[1]
440 .as_object()
441 .ok_or_else(|| custom_error(ctx, "Expected mapping object"))?;
442
443 let rename_map: std::collections::HashMap<String, String> = mapping
444 .iter()
445 .filter_map(|(k, v)| v.as_str().map(|s| (k.clone(), s.to_string())))
446 .collect();
447
448 let result: Map<String, Value> = obj
449 .iter()
450 .map(|(k, v)| {
451 let new_key = rename_map.get(k).cloned().unwrap_or_else(|| k.clone());
452 (new_key, v.clone())
453 })
454 .collect();
455
456 Ok(Value::Object(result))
457 }
458}
459
460defn!(FlattenKeysFn, vec![arg!(object)], Some(arg!(string)));
465
466fn flatten_object(
467 obj: &Map<String, Value>,
468 prefix: &str,
469 separator: &str,
470 result: &mut Map<String, Value>,
471) {
472 for (k, v) in obj.iter() {
473 let new_key = if prefix.is_empty() {
474 k.clone()
475 } else {
476 format!("{}{}{}", prefix, separator, k)
477 };
478
479 if let Some(nested) = v.as_object() {
480 flatten_object(nested, &new_key, separator, result);
481 } else {
482 result.insert(new_key, v.clone());
483 }
484 }
485}
486
487impl Function for FlattenKeysFn {
488 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
489 self.signature.validate(args, ctx)?;
490
491 let obj = args[0]
492 .as_object()
493 .ok_or_else(|| custom_error(ctx, "Expected object argument"))?;
494
495 let default_sep = ".".to_string();
496 let separator = args
497 .get(1)
498 .and_then(|s| s.as_str().map(|s| s.to_string()))
499 .unwrap_or(default_sep);
500
501 let mut result = Map::new();
502 flatten_object(obj, "", &separator, &mut result);
503
504 Ok(Value::Object(result))
505 }
506}
507
508defn!(UnflattenKeysFn, vec![arg!(object)], Some(arg!(string)));
513
514fn insert_nested(obj: &mut Map<String, Value>, parts: &[&str], value: Value) {
515 if parts.is_empty() {
516 return;
517 }
518
519 if parts.len() == 1 {
520 obj.insert(parts[0].to_string(), value);
521 return;
522 }
523
524 let key = parts[0].to_string();
525 let rest = &parts[1..];
526
527 let nested = obj
528 .entry(key.clone())
529 .or_insert_with(|| Value::Object(Map::new()));
530
531 if let Some(nested_obj) = nested.as_object() {
532 let mut new_obj = nested_obj.clone();
533 insert_nested(&mut new_obj, rest, value);
534 *obj.get_mut(&key).unwrap() = Value::Object(new_obj);
535 }
536}
537
538impl Function for UnflattenKeysFn {
539 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
540 self.signature.validate(args, ctx)?;
541
542 let obj = args[0]
543 .as_object()
544 .ok_or_else(|| custom_error(ctx, "Expected object argument"))?;
545
546 let default_sep = ".".to_string();
547 let separator = args
548 .get(1)
549 .and_then(|s| s.as_str().map(|s| s.to_string()))
550 .unwrap_or(default_sep);
551
552 let mut result = Map::new();
553
554 for (key, value) in obj.iter() {
555 let parts: Vec<&str> = key.split(&separator).collect();
556 insert_nested(&mut result, &parts, value.clone());
557 }
558
559 Ok(Value::Object(result))
560 }
561}
562
563defn!(FlattenArrayFn, vec![arg!(any)], Some(arg!(string)));
569
570fn flatten_value(value: &Value, prefix: &str, separator: &str, result: &mut Map<String, Value>) {
571 match value {
572 Value::Object(obj) => {
573 if obj.is_empty() {
574 if !prefix.is_empty() {
575 result.insert(prefix.to_string(), Value::Object(obj.clone()));
576 }
577 } else {
578 for (k, v) in obj.iter() {
579 let new_key = if prefix.is_empty() {
580 k.clone()
581 } else {
582 format!("{}{}{}", prefix, separator, k)
583 };
584 flatten_value(v, &new_key, separator, result);
585 }
586 }
587 }
588 Value::Array(arr) => {
589 if arr.is_empty() {
590 if !prefix.is_empty() {
591 result.insert(prefix.to_string(), Value::Array(arr.clone()));
592 }
593 } else {
594 for (idx, v) in arr.iter().enumerate() {
595 let new_key = if prefix.is_empty() {
596 idx.to_string()
597 } else {
598 format!("{}{}{}", prefix, separator, idx)
599 };
600 flatten_value(v, &new_key, separator, result);
601 }
602 }
603 }
604 _ => {
605 if !prefix.is_empty() {
606 result.insert(prefix.to_string(), value.clone());
607 } else {
608 result.insert(String::new(), value.clone());
609 }
610 }
611 }
612}
613
614impl Function for FlattenArrayFn {
615 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
616 self.signature.validate(args, ctx)?;
617
618 let default_sep = ".".to_string();
619 let separator = args
620 .get(1)
621 .and_then(|s| s.as_str().map(|s| s.to_string()))
622 .unwrap_or(default_sep);
623
624 let mut result = Map::new();
625 flatten_value(&args[0], "", &separator, &mut result);
626
627 Ok(Value::Object(result))
628 }
629}
630
631defn!(DeepMergeFn, vec![arg!(object), arg!(object)], None);
636
637fn deep_merge_objects(
638 base: &Map<String, Value>,
639 overlay: &Map<String, Value>,
640) -> Map<String, Value> {
641 let mut result = base.clone();
642
643 for (key, overlay_value) in overlay {
644 if let Some(base_value) = result.get(key) {
645 if let (Some(base_obj), Some(overlay_obj)) =
646 (base_value.as_object(), overlay_value.as_object())
647 {
648 let merged = deep_merge_objects(base_obj, overlay_obj);
649 result.insert(key.clone(), Value::Object(merged));
650 } else {
651 result.insert(key.clone(), overlay_value.clone());
652 }
653 } else {
654 result.insert(key.clone(), overlay_value.clone());
655 }
656 }
657
658 result
659}
660
661impl Function for DeepMergeFn {
662 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
663 self.signature.validate(args, ctx)?;
664
665 let obj1 = args[0]
666 .as_object()
667 .ok_or_else(|| custom_error(ctx, "Expected object argument"))?;
668
669 let obj2 = args[1]
670 .as_object()
671 .ok_or_else(|| custom_error(ctx, "Expected object argument"))?;
672
673 let merged = deep_merge_objects(obj1, obj2);
674 Ok(Value::Object(merged))
675 }
676}
677
678defn!(DeepEqualsFn, vec![arg!(any), arg!(any)], None);
683
684impl Function for DeepEqualsFn {
685 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
686 self.signature.validate(args, ctx)?;
687
688 let a_json = serde_json::to_string(&args[0]).unwrap_or_default();
689 let b_json = serde_json::to_string(&args[1]).unwrap_or_default();
690
691 Ok(Value::Bool(a_json == b_json))
692 }
693}
694
695defn!(DeepDiffFn, vec![arg!(object), arg!(object)], None);
700
701fn compute_deep_diff(a: &Map<String, Value>, b: &Map<String, Value>) -> Map<String, Value> {
702 let mut added = Map::new();
703 let mut removed = Map::new();
704 let mut changed = Map::new();
705
706 for (key, a_value) in a.iter() {
707 match b.get(key) {
708 None => {
709 removed.insert(key.clone(), a_value.clone());
710 }
711 Some(b_value) => {
712 let a_json = serde_json::to_string(a_value).unwrap_or_default();
713 let b_json = serde_json::to_string(b_value).unwrap_or_default();
714
715 if a_json != b_json {
716 if let (Some(a_obj), Some(b_obj)) = (a_value.as_object(), b_value.as_object()) {
717 let nested_diff = compute_deep_diff(a_obj, b_obj);
718 changed.insert(key.clone(), Value::Object(nested_diff));
719 } else {
720 let mut change_obj = Map::new();
721 change_obj.insert("from".to_string(), a_value.clone());
722 change_obj.insert("to".to_string(), b_value.clone());
723 changed.insert(key.clone(), Value::Object(change_obj));
724 }
725 }
726 }
727 }
728 }
729
730 for (key, b_value) in b.iter() {
731 if !a.contains_key(key) {
732 added.insert(key.clone(), b_value.clone());
733 }
734 }
735
736 let mut result = Map::new();
737 result.insert("added".to_string(), Value::Object(added));
738 result.insert("removed".to_string(), Value::Object(removed));
739 result.insert("changed".to_string(), Value::Object(changed));
740
741 result
742}
743
744impl Function for DeepDiffFn {
745 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
746 self.signature.validate(args, ctx)?;
747
748 let obj_a = args[0]
749 .as_object()
750 .ok_or_else(|| custom_error(ctx, "Expected object argument"))?;
751
752 let obj_b = args[1]
753 .as_object()
754 .ok_or_else(|| custom_error(ctx, "Expected object argument"))?;
755
756 let diff = compute_deep_diff(obj_a, obj_b);
757 Ok(Value::Object(diff))
758 }
759}
760
761defn!(GetFn, vec![arg!(any), arg!(string)], Some(arg!(any)));
766
767fn get_at_path(value: &Value, path: &str) -> Option<Value> {
768 if path.is_empty() {
769 return Some(value.clone());
770 }
771
772 let mut current = value.clone();
773
774 let parts = parse_path_parts(path);
775
776 for part in parts {
777 if let Some(idx) = part.strip_prefix('[').and_then(|s| s.strip_suffix(']')) {
778 if let Ok(index) = idx.parse::<usize>() {
779 if let Some(arr) = current.as_array() {
780 if index < arr.len() {
781 current = arr[index].clone();
782 } else {
783 return None;
784 }
785 } else {
786 return None;
787 }
788 } else {
789 return None;
790 }
791 } else if let Ok(index) = part.parse::<usize>() {
792 if let Some(arr) = current.as_array() {
793 if index < arr.len() {
794 current = arr[index].clone();
795 } else {
796 return None;
797 }
798 } else if let Some(obj) = current.as_object() {
799 if let Some(val) = obj.get(&part) {
800 current = val.clone();
801 } else {
802 return None;
803 }
804 } else {
805 return None;
806 }
807 } else if let Some(obj) = current.as_object() {
808 if let Some(val) = obj.get(&part) {
809 current = val.clone();
810 } else {
811 return None;
812 }
813 } else {
814 return None;
815 }
816 }
817
818 Some(current)
819}
820
821fn parse_path_parts(path: &str) -> Vec<String> {
822 let mut parts = Vec::new();
823 let mut current = String::new();
824 let mut chars = path.chars().peekable();
825
826 while let Some(c) = chars.next() {
827 match c {
828 '.' => {
829 if !current.is_empty() {
830 parts.push(current.clone());
831 current.clear();
832 }
833 }
834 '[' => {
835 if !current.is_empty() {
836 parts.push(current.clone());
837 current.clear();
838 }
839 let mut bracket = String::from("[");
840 while let Some(&next) = chars.peek() {
841 bracket.push(chars.next().unwrap());
842 if next == ']' {
843 break;
844 }
845 }
846 parts.push(bracket);
847 }
848 _ => {
849 current.push(c);
850 }
851 }
852 }
853
854 if !current.is_empty() {
855 parts.push(current);
856 }
857
858 parts
859}
860
861impl Function for GetFn {
862 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
863 self.signature.validate(args, ctx)?;
864
865 let path = args[1]
866 .as_str()
867 .ok_or_else(|| custom_error(ctx, "Expected string path argument"))?;
868
869 let default_val = if args.len() > 2 {
870 args[2].clone()
871 } else {
872 Value::Null
873 };
874
875 match get_at_path(&args[0], path) {
876 Some(val) => Ok(val),
877 None => Ok(default_val),
878 }
879 }
880}
881
882defn!(HasFn, vec![arg!(any), arg!(string)], None);
887
888impl Function for HasFn {
889 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
890 self.signature.validate(args, ctx)?;
891
892 let path = args[1]
893 .as_str()
894 .ok_or_else(|| custom_error(ctx, "Expected string path argument"))?;
895
896 let exists = get_at_path(&args[0], path).is_some();
897 Ok(Value::Bool(exists))
898 }
899}
900
901defn!(DefaultsFn, vec![arg!(object), arg!(object)], None);
906
907impl Function for DefaultsFn {
908 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
909 self.signature.validate(args, ctx)?;
910
911 let obj = args[0]
912 .as_object()
913 .ok_or_else(|| custom_error(ctx, "Expected object argument"))?;
914
915 let defaults = args[1]
916 .as_object()
917 .ok_or_else(|| custom_error(ctx, "Expected object argument"))?;
918
919 let mut result = obj.clone();
920
921 for (key, value) in defaults.iter() {
922 if !result.contains_key(key) {
923 result.insert(key.clone(), value.clone());
924 }
925 }
926
927 Ok(Value::Object(result))
928 }
929}
930
931defn!(DefaultsDeepFn, vec![arg!(object), arg!(object)], None);
936
937fn apply_defaults_deep(
938 obj: &Map<String, Value>,
939 defaults: &Map<String, Value>,
940) -> Map<String, Value> {
941 let mut result = obj.clone();
942
943 for (key, default_value) in defaults.iter() {
944 if let Some(existing) = result.get(key) {
945 if let (Some(existing_obj), Some(default_obj)) =
946 (existing.as_object(), default_value.as_object())
947 {
948 let merged = apply_defaults_deep(existing_obj, default_obj);
949 result.insert(key.clone(), Value::Object(merged));
950 }
951 } else {
952 result.insert(key.clone(), default_value.clone());
953 }
954 }
955
956 result
957}
958
959impl Function for DefaultsDeepFn {
960 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
961 self.signature.validate(args, ctx)?;
962
963 let obj = args[0]
964 .as_object()
965 .ok_or_else(|| custom_error(ctx, "Expected object argument"))?;
966
967 let defaults = args[1]
968 .as_object()
969 .ok_or_else(|| custom_error(ctx, "Expected object argument"))?;
970
971 let result = apply_defaults_deep(obj, defaults);
972 Ok(Value::Object(result))
973 }
974}
975
976defn!(SetPathFn, vec![arg!(any), arg!(string), arg!(any)], None);
981
982impl Function for SetPathFn {
983 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
984 self.signature.validate(args, ctx)?;
985
986 let path = args[1]
987 .as_str()
988 .ok_or_else(|| custom_error(ctx, "Expected string path argument"))?;
989
990 let value = args[2].clone();
991
992 let parts = parse_path_for_mutation(path);
993 if parts.is_empty() {
994 return Ok(value);
995 }
996
997 let result = set_at_path(&args[0], &parts, value);
998 Ok(result)
999 }
1000}
1001
1002fn parse_path_for_mutation(path: &str) -> Vec<String> {
1003 if path.is_empty() {
1004 return vec![];
1005 }
1006
1007 if path.starts_with('/') {
1008 parse_json_pointer(path)
1009 } else {
1010 parse_path_parts_for_mutation(path)
1011 }
1012}
1013
1014fn parse_path_parts_for_mutation(path: &str) -> Vec<String> {
1015 let mut parts = Vec::new();
1016 let mut current = String::new();
1017 let mut chars = path.chars().peekable();
1018
1019 while let Some(c) = chars.next() {
1020 match c {
1021 '.' => {
1022 if !current.is_empty() {
1023 parts.push(current.clone());
1024 current.clear();
1025 }
1026 }
1027 '[' => {
1028 if !current.is_empty() {
1029 parts.push(current.clone());
1030 current.clear();
1031 }
1032 let mut index = String::new();
1033 while let Some(&next) = chars.peek() {
1034 if next == ']' {
1035 chars.next();
1036 break;
1037 }
1038 index.push(chars.next().unwrap());
1039 }
1040 parts.push(index);
1041 }
1042 _ => {
1043 current.push(c);
1044 }
1045 }
1046 }
1047
1048 if !current.is_empty() {
1049 parts.push(current);
1050 }
1051
1052 parts
1053}
1054
1055fn parse_json_pointer(path: &str) -> Vec<String> {
1056 if path.is_empty() {
1057 return vec![];
1058 }
1059
1060 let path = path.strip_prefix('/').unwrap_or(path);
1061
1062 if path.is_empty() {
1063 return vec![];
1064 }
1065
1066 path.split('/')
1067 .map(|s| s.replace("~1", "/").replace("~0", "~"))
1068 .collect()
1069}
1070
1071fn set_at_path(value: &Value, parts: &[String], new_value: Value) -> Value {
1072 if parts.is_empty() {
1073 return new_value;
1074 }
1075
1076 let key = &parts[0];
1077 let remaining = &parts[1..];
1078
1079 match value {
1080 Value::Object(obj) => {
1081 let mut new_obj = obj.clone();
1082 if remaining.is_empty() {
1083 new_obj.insert(key.clone(), new_value);
1084 } else {
1085 let existing = obj.get(key).cloned().unwrap_or(Value::Null);
1086 new_obj.insert(key.clone(), set_at_path(&existing, remaining, new_value));
1087 }
1088 Value::Object(new_obj)
1089 }
1090 Value::Array(arr) => {
1091 if let Ok(idx) = key.parse::<usize>() {
1092 let mut new_arr = arr.clone();
1093 while new_arr.len() <= idx {
1094 new_arr.push(Value::Null);
1095 }
1096 if remaining.is_empty() {
1097 new_arr[idx] = new_value;
1098 } else {
1099 new_arr[idx] = set_at_path(
1100 &arr.get(idx).cloned().unwrap_or(Value::Null),
1101 remaining,
1102 new_value,
1103 );
1104 }
1105 Value::Array(new_arr)
1106 } else {
1107 value.clone()
1108 }
1109 }
1110 _ => {
1111 if remaining.is_empty() {
1112 let mut new_obj = Map::new();
1113 new_obj.insert(key.clone(), new_value);
1114 Value::Object(new_obj)
1115 } else {
1116 let mut new_obj = Map::new();
1117 new_obj.insert(key.clone(), set_at_path(&Value::Null, remaining, new_value));
1118 Value::Object(new_obj)
1119 }
1120 }
1121 }
1122}
1123
1124defn!(DeletePathFn, vec![arg!(any), arg!(string)], None);
1129
1130impl Function for DeletePathFn {
1131 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1132 self.signature.validate(args, ctx)?;
1133
1134 let path = args[1]
1135 .as_str()
1136 .ok_or_else(|| custom_error(ctx, "Expected string path argument"))?;
1137
1138 let parts = parse_path_for_mutation(path);
1139 if parts.is_empty() {
1140 return Ok(Value::Null);
1141 }
1142
1143 let result = delete_at_path(&args[0], &parts);
1144 Ok(result)
1145 }
1146}
1147
1148fn delete_at_path(value: &Value, parts: &[String]) -> Value {
1149 if parts.is_empty() {
1150 return Value::Null;
1151 }
1152
1153 let key = &parts[0];
1154 let remaining = &parts[1..];
1155
1156 match value {
1157 Value::Object(obj) => {
1158 let mut new_obj = obj.clone();
1159 if remaining.is_empty() {
1160 new_obj.remove(key);
1161 } else if let Some(existing) = obj.get(key) {
1162 new_obj.insert(key.clone(), delete_at_path(existing, remaining));
1163 }
1164 Value::Object(new_obj)
1165 }
1166 Value::Array(arr) => {
1167 if let Ok(idx) = key.parse::<usize>() {
1168 if idx < arr.len() {
1169 let mut new_arr = arr.clone();
1170 if remaining.is_empty() {
1171 new_arr.remove(idx);
1172 } else {
1173 new_arr[idx] = delete_at_path(&arr[idx], remaining);
1174 }
1175 Value::Array(new_arr)
1176 } else {
1177 value.clone()
1178 }
1179 } else {
1180 value.clone()
1181 }
1182 }
1183 _ => value.clone(),
1184 }
1185}
1186
1187defn!(PathsFn, vec![arg!(any)], None);
1192
1193impl Function for PathsFn {
1194 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1195 self.signature.validate(args, ctx)?;
1196
1197 let mut paths = Vec::new();
1198 collect_paths(&args[0], String::new(), &mut paths);
1199
1200 let result: Vec<Value> = paths.into_iter().map(Value::String).collect();
1201
1202 Ok(Value::Array(result))
1203 }
1204}
1205
1206fn collect_paths(value: &Value, current_path: String, paths: &mut Vec<String>) {
1207 match value {
1208 Value::Object(obj) => {
1209 if !current_path.is_empty() {
1210 paths.push(current_path.clone());
1211 }
1212 for (key, val) in obj.iter() {
1213 let escaped_key = key.replace('~', "~0").replace('/', "~1");
1214 let new_path = format!("{}/{}", current_path, escaped_key);
1215 collect_paths(val, new_path, paths);
1216 }
1217 }
1218 Value::Array(arr) => {
1219 if !current_path.is_empty() {
1220 paths.push(current_path.clone());
1221 }
1222 for (idx, val) in arr.iter().enumerate() {
1223 let new_path = format!("{}/{}", current_path, idx);
1224 collect_paths(val, new_path, paths);
1225 }
1226 }
1227 _ => {
1228 if !current_path.is_empty() {
1229 paths.push(current_path);
1230 }
1231 }
1232 }
1233}
1234
1235defn!(LeavesFn, vec![arg!(any)], None);
1240
1241impl Function for LeavesFn {
1242 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1243 self.signature.validate(args, ctx)?;
1244
1245 let mut leaves = Vec::new();
1246 collect_leaves(&args[0], &mut leaves);
1247
1248 Ok(Value::Array(leaves))
1249 }
1250}
1251
1252fn collect_leaves(value: &Value, leaves: &mut Vec<Value>) {
1253 match value {
1254 Value::Object(obj) => {
1255 for (_, val) in obj.iter() {
1256 collect_leaves(val, leaves);
1257 }
1258 }
1259 Value::Array(arr) => {
1260 for val in arr.iter() {
1261 collect_leaves(val, leaves);
1262 }
1263 }
1264 _ => {
1265 leaves.push(value.clone());
1266 }
1267 }
1268}
1269
1270defn!(LeavesWithPathsFn, vec![arg!(any)], None);
1275
1276impl Function for LeavesWithPathsFn {
1277 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1278 self.signature.validate(args, ctx)?;
1279
1280 let mut leaves = Vec::new();
1281 collect_leaves_with_paths(&args[0], String::new(), &mut leaves);
1282
1283 let result: Vec<Value> = leaves
1284 .into_iter()
1285 .map(|(path, value)| {
1286 let mut obj = Map::new();
1287 obj.insert("path".to_string(), Value::String(path));
1288 obj.insert("value".to_string(), value);
1289 Value::Object(obj)
1290 })
1291 .collect();
1292
1293 Ok(Value::Array(result))
1294 }
1295}
1296
1297fn collect_leaves_with_paths(
1298 value: &Value,
1299 current_path: String,
1300 leaves: &mut Vec<(String, Value)>,
1301) {
1302 match value {
1303 Value::Object(obj) => {
1304 if obj.is_empty() && !current_path.is_empty() {
1305 leaves.push((current_path, value.clone()));
1306 } else {
1307 for (key, val) in obj.iter() {
1308 let escaped_key = key.replace('~', "~0").replace('/', "~1");
1309 let new_path = format!("{}/{}", current_path, escaped_key);
1310 collect_leaves_with_paths(val, new_path, leaves);
1311 }
1312 }
1313 }
1314 Value::Array(arr) => {
1315 if arr.is_empty() && !current_path.is_empty() {
1316 leaves.push((current_path, value.clone()));
1317 } else {
1318 for (idx, val) in arr.iter().enumerate() {
1319 let new_path = format!("{}/{}", current_path, idx);
1320 collect_leaves_with_paths(val, new_path, leaves);
1321 }
1322 }
1323 }
1324 _ => {
1325 let path = if current_path.is_empty() {
1326 "/".to_string()
1327 } else {
1328 current_path
1329 };
1330 leaves.push((path, value.clone()));
1331 }
1332 }
1333}
1334
1335defn!(RemoveNullsFn, vec![arg!(any)], None);
1340
1341impl Function for RemoveNullsFn {
1342 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1343 self.signature.validate(args, ctx)?;
1344 Ok(remove_nulls_recursive(&args[0]))
1345 }
1346}
1347
1348fn is_null_value(value: &Value) -> bool {
1349 value.is_null()
1350}
1351
1352fn remove_nulls_recursive(value: &Value) -> Value {
1353 match value {
1354 Value::Object(obj) => {
1355 let cleaned: Map<String, Value> = obj
1356 .iter()
1357 .filter(|(_, v)| !is_null_value(v))
1358 .map(|(k, v)| (k.clone(), remove_nulls_recursive(v)))
1359 .collect();
1360 Value::Object(cleaned)
1361 }
1362 Value::Array(arr) => {
1363 let cleaned: Vec<Value> = arr
1364 .iter()
1365 .filter(|v| !is_null_value(v))
1366 .map(remove_nulls_recursive)
1367 .collect();
1368 Value::Array(cleaned)
1369 }
1370 _ => value.clone(),
1371 }
1372}
1373
1374defn!(RemoveEmptyFn, vec![arg!(any)], None);
1379
1380impl Function for RemoveEmptyFn {
1381 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1382 self.signature.validate(args, ctx)?;
1383 Ok(remove_empty_recursive(&args[0]))
1384 }
1385}
1386
1387fn is_empty_value(value: &Value) -> bool {
1388 match value {
1389 Value::Null => true,
1390 Value::String(s) => s.is_empty(),
1391 Value::Array(arr) => arr.is_empty(),
1392 Value::Object(obj) => obj.is_empty(),
1393 _ => false,
1394 }
1395}
1396
1397fn remove_empty_recursive(value: &Value) -> Value {
1398 match value {
1399 Value::Object(obj) => {
1400 let cleaned: Map<String, Value> = obj
1401 .iter()
1402 .map(|(k, v)| (k.clone(), remove_empty_recursive(v)))
1403 .filter(|(_, v)| !is_empty_value(v))
1404 .collect();
1405 Value::Object(cleaned)
1406 }
1407 Value::Array(arr) => {
1408 let cleaned: Vec<Value> = arr
1409 .iter()
1410 .map(remove_empty_recursive)
1411 .filter(|v| !is_empty_value(v))
1412 .collect();
1413 Value::Array(cleaned)
1414 }
1415 _ => value.clone(),
1416 }
1417}
1418
1419defn!(RemoveEmptyStringsFn, vec![arg!(any)], None);
1424
1425impl Function for RemoveEmptyStringsFn {
1426 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1427 self.signature.validate(args, ctx)?;
1428 Ok(remove_empty_strings_recursive(&args[0]))
1429 }
1430}
1431
1432fn is_empty_string(value: &Value) -> bool {
1433 matches!(value, Value::String(s) if s.is_empty())
1434}
1435
1436fn remove_empty_strings_recursive(value: &Value) -> Value {
1437 match value {
1438 Value::Object(obj) => {
1439 let cleaned: Map<String, Value> = obj
1440 .iter()
1441 .filter(|(_, v)| !is_empty_string(v))
1442 .map(|(k, v)| (k.clone(), remove_empty_strings_recursive(v)))
1443 .collect();
1444 Value::Object(cleaned)
1445 }
1446 Value::Array(arr) => {
1447 let cleaned: Vec<Value> = arr
1448 .iter()
1449 .filter(|v| !is_empty_string(v))
1450 .map(remove_empty_strings_recursive)
1451 .collect();
1452 Value::Array(cleaned)
1453 }
1454 _ => value.clone(),
1455 }
1456}
1457
1458defn!(CompactDeepFn, vec![arg!(array)], None);
1463
1464impl Function for CompactDeepFn {
1465 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1466 self.signature.validate(args, ctx)?;
1467 Ok(compact_deep_recursive(&args[0]))
1468 }
1469}
1470
1471fn compact_deep_recursive(value: &Value) -> Value {
1472 match value {
1473 Value::Array(arr) => {
1474 let cleaned: Vec<Value> = arr
1475 .iter()
1476 .filter(|v| !is_null_value(v))
1477 .map(compact_deep_recursive)
1478 .collect();
1479 Value::Array(cleaned)
1480 }
1481 Value::Object(obj) => {
1482 let cleaned: Map<String, Value> = obj
1483 .iter()
1484 .map(|(k, v)| (k.clone(), compact_deep_recursive(v)))
1485 .collect();
1486 Value::Object(cleaned)
1487 }
1488 _ => value.clone(),
1489 }
1490}
1491
1492defn!(CompletenessFn, vec![arg!(object)], None);
1497
1498impl Function for CompletenessFn {
1499 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1500 self.signature.validate(args, ctx)?;
1501 let obj = args[0]
1502 .as_object()
1503 .ok_or_else(|| custom_error(ctx, "completeness: expected object"))?;
1504
1505 if obj.is_empty() {
1506 return Ok(number_value(100.0));
1507 }
1508
1509 let mut total_fields = 0;
1510 let mut non_null_fields = 0;
1511
1512 count_completeness(&args[0], &mut total_fields, &mut non_null_fields);
1513
1514 let score = if total_fields > 0 {
1515 (non_null_fields as f64 / total_fields as f64) * 100.0
1516 } else {
1517 100.0
1518 };
1519
1520 Ok(number_value(score))
1521 }
1522}
1523
1524fn count_completeness(value: &Value, total: &mut usize, non_null: &mut usize) {
1525 match value {
1526 Value::Object(obj) => {
1527 for (_, v) in obj.iter() {
1528 *total += 1;
1529 if !v.is_null() {
1530 *non_null += 1;
1531 }
1532 count_completeness(v, total, non_null);
1533 }
1534 }
1535 Value::Array(arr) => {
1536 for item in arr.iter() {
1537 count_completeness(item, total, non_null);
1538 }
1539 }
1540 _ => {}
1541 }
1542}
1543
1544defn!(TypeConsistencyFn, vec![arg!(array)], None);
1549
1550impl Function for TypeConsistencyFn {
1551 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1552 self.signature.validate(args, ctx)?;
1553 let arr = args[0]
1554 .as_array()
1555 .ok_or_else(|| custom_error(ctx, "type_consistency: expected array"))?;
1556
1557 if arr.is_empty() {
1558 let mut result = Map::new();
1559 result.insert("consistent".to_string(), Value::Bool(true));
1560 result.insert("types".to_string(), Value::Array(vec![]));
1561 result.insert("inconsistencies".to_string(), Value::Array(vec![]));
1562 return Ok(Value::Object(result));
1563 }
1564
1565 let first_element = &arr[0];
1566 if let Some(first_obj) = first_element.as_object() {
1567 return check_object_array_consistency(arr, first_obj);
1568 }
1569
1570 let mut type_counts: std::collections::BTreeMap<String, usize> =
1571 std::collections::BTreeMap::new();
1572 for item in arr.iter() {
1573 let type_name = get_type_name(item);
1574 *type_counts.entry(type_name).or_insert(0) += 1;
1575 }
1576
1577 let types: Vec<Value> = type_counts
1578 .keys()
1579 .map(|t| Value::String(t.clone()))
1580 .collect();
1581
1582 let consistent = type_counts.len() == 1;
1583
1584 let mut result = Map::new();
1585 result.insert("consistent".to_string(), Value::Bool(consistent));
1586 result.insert("types".to_string(), Value::Array(types));
1587 result.insert("inconsistencies".to_string(), Value::Array(vec![]));
1588
1589 Ok(Value::Object(result))
1590 }
1591}
1592
1593fn check_object_array_consistency(arr: &[Value], first_obj: &Map<String, Value>) -> SearchResult {
1594 let mut expected_types: std::collections::BTreeMap<String, String> =
1595 std::collections::BTreeMap::new();
1596 for (key, val) in first_obj.iter() {
1597 expected_types.insert(key.clone(), get_type_name(val));
1598 }
1599
1600 let mut inconsistencies: Vec<Value> = Vec::new();
1601
1602 for (idx, item) in arr.iter().enumerate().skip(1) {
1603 if let Some(obj) = item.as_object() {
1604 for (key, val) in obj.iter() {
1605 let actual_type = get_type_name(val);
1606 if let Some(expected) = expected_types.get(key)
1607 && &actual_type != expected
1608 && actual_type != "null"
1609 && expected != "null"
1610 {
1611 let mut issue = Map::new();
1612 issue.insert("index".to_string(), Value::Number(Number::from(idx as i64)));
1613 issue.insert("field".to_string(), Value::String(key.clone()));
1614 issue.insert("expected".to_string(), Value::String(expected.clone()));
1615 issue.insert("got".to_string(), Value::String(actual_type));
1616 inconsistencies.push(Value::Object(issue));
1617 }
1618 }
1619 }
1620 }
1621
1622 let types: Vec<Value> = expected_types
1623 .iter()
1624 .map(|(k, v)| {
1625 let mut obj = Map::new();
1626 obj.insert("field".to_string(), Value::String(k.clone()));
1627 obj.insert("type".to_string(), Value::String(v.clone()));
1628 Value::Object(obj)
1629 })
1630 .collect();
1631
1632 let mut result = Map::new();
1633 result.insert(
1634 "consistent".to_string(),
1635 Value::Bool(inconsistencies.is_empty()),
1636 );
1637 result.insert("types".to_string(), Value::Array(types));
1638 result.insert("inconsistencies".to_string(), Value::Array(inconsistencies));
1639
1640 Ok(Value::Object(result))
1641}
1642
1643fn get_type_name(value: &Value) -> String {
1644 match value {
1645 Value::Null => "null".to_string(),
1646 Value::Bool(_) => "boolean".to_string(),
1647 Value::Number(_) => "number".to_string(),
1648 Value::String(_) => "string".to_string(),
1649 Value::Array(_) => "array".to_string(),
1650 Value::Object(_) => "object".to_string(),
1651 }
1652}
1653
1654defn!(DataQualityScoreFn, vec![arg!(any)], None);
1659
1660impl Function for DataQualityScoreFn {
1661 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1662 self.signature.validate(args, ctx)?;
1663 let value = &args[0];
1664
1665 let mut stats = QualityStats::default();
1666 analyze_quality(value, String::new(), &mut stats);
1667
1668 let total_issues = stats.null_count + stats.empty_string_count + stats.type_issues.len();
1669 let score = if stats.total_fields == 0 {
1670 100.0
1671 } else {
1672 let issue_ratio = total_issues as f64 / stats.total_fields as f64;
1673 (100.0 * (1.0 - issue_ratio)).max(0.0)
1674 };
1675
1676 let mut issues: Vec<Value> = Vec::new();
1677
1678 for path in &stats.null_paths {
1679 let mut issue = Map::new();
1680 issue.insert("path".to_string(), Value::String(path.clone()));
1681 issue.insert("issue".to_string(), Value::String("null".to_string()));
1682 issues.push(Value::Object(issue));
1683 }
1684
1685 for path in &stats.empty_string_paths {
1686 let mut issue = Map::new();
1687 issue.insert("path".to_string(), Value::String(path.clone()));
1688 issue.insert(
1689 "issue".to_string(),
1690 Value::String("empty_string".to_string()),
1691 );
1692 issues.push(Value::Object(issue));
1693 }
1694
1695 for ti in &stats.type_issues {
1696 let mut issue = Map::new();
1697 issue.insert("path".to_string(), Value::String(ti.path.clone()));
1698 issue.insert(
1699 "issue".to_string(),
1700 Value::String("type_mismatch".to_string()),
1701 );
1702 issue.insert("expected".to_string(), Value::String(ti.expected.clone()));
1703 issue.insert("got".to_string(), Value::String(ti.got.clone()));
1704 issues.push(Value::Object(issue));
1705 }
1706
1707 let mut result = Map::new();
1708 result.insert("score".to_string(), number_value(score));
1709 result.insert(
1710 "total_fields".to_string(),
1711 Value::Number(Number::from(stats.total_fields as i64)),
1712 );
1713 result.insert(
1714 "null_count".to_string(),
1715 Value::Number(Number::from(stats.null_count as i64)),
1716 );
1717 result.insert(
1718 "empty_string_count".to_string(),
1719 Value::Number(Number::from(stats.empty_string_count as i64)),
1720 );
1721 result.insert(
1722 "type_inconsistencies".to_string(),
1723 Value::Number(Number::from(stats.type_issues.len() as i64)),
1724 );
1725 result.insert("issues".to_string(), Value::Array(issues));
1726
1727 Ok(Value::Object(result))
1728 }
1729}
1730
1731#[derive(Default)]
1732struct QualityStats {
1733 total_fields: usize,
1734 null_count: usize,
1735 empty_string_count: usize,
1736 null_paths: Vec<String>,
1737 empty_string_paths: Vec<String>,
1738 type_issues: Vec<TypeIssue>,
1739}
1740
1741struct TypeIssue {
1742 path: String,
1743 expected: String,
1744 got: String,
1745}
1746
1747fn analyze_quality(value: &Value, path: String, stats: &mut QualityStats) {
1748 match value {
1749 Value::Object(obj) => {
1750 for (key, val) in obj.iter() {
1751 let field_path = if path.is_empty() {
1752 key.clone()
1753 } else {
1754 format!("{}.{}", path, key)
1755 };
1756 stats.total_fields += 1;
1757
1758 match val {
1759 Value::Null => {
1760 stats.null_count += 1;
1761 stats.null_paths.push(field_path.clone());
1762 }
1763 Value::String(s) if s.is_empty() => {
1764 stats.empty_string_count += 1;
1765 stats.empty_string_paths.push(field_path.clone());
1766 }
1767 _ => {}
1768 }
1769
1770 analyze_quality(val, field_path, stats);
1771 }
1772 }
1773 Value::Array(arr) => {
1774 if arr.len() > 1
1775 && let Some(Value::Object(first_obj)) = arr.first()
1776 {
1777 let expected_types: std::collections::BTreeMap<String, String> = first_obj
1778 .iter()
1779 .map(|(k, v)| (k.clone(), get_type_name(v)))
1780 .collect();
1781
1782 for (idx, item) in arr.iter().enumerate().skip(1) {
1783 if let Value::Object(obj) = item {
1784 for (key, val) in obj.iter() {
1785 let actual_type = get_type_name(val);
1786 if let Some(expected) = expected_types.get(key)
1787 && &actual_type != expected
1788 && actual_type != "null"
1789 && expected != "null"
1790 {
1791 stats.type_issues.push(TypeIssue {
1792 path: format!("{}[{}].{}", path, idx, key),
1793 expected: expected.clone(),
1794 got: actual_type,
1795 });
1796 }
1797 }
1798 }
1799 }
1800 }
1801
1802 for (idx, item) in arr.iter().enumerate() {
1803 let item_path = format!("{}[{}]", path, idx);
1804 analyze_quality(item, item_path, stats);
1805 }
1806 }
1807 _ => {}
1808 }
1809}
1810
1811defn!(RedactFn, vec![arg!(any), arg!(array)], None);
1816
1817impl Function for RedactFn {
1818 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1819 self.signature.validate(args, ctx)?;
1820
1821 let keys_arr = args[1]
1822 .as_array()
1823 .ok_or_else(|| custom_error(ctx, "Expected array of keys"))?;
1824
1825 let keys: HashSet<String> = keys_arr
1826 .iter()
1827 .filter_map(|k| k.as_str().map(|s| s.to_string()))
1828 .collect();
1829
1830 Ok(redact_recursive(&args[0], &keys))
1831 }
1832}
1833
1834fn redact_recursive(value: &Value, keys: &HashSet<String>) -> Value {
1835 match value {
1836 Value::Object(obj) => {
1837 let redacted: Map<String, Value> = obj
1838 .iter()
1839 .map(|(k, v)| {
1840 if keys.contains(k) {
1841 (k.clone(), Value::String("[REDACTED]".to_string()))
1842 } else {
1843 (k.clone(), redact_recursive(v, keys))
1844 }
1845 })
1846 .collect();
1847 Value::Object(redacted)
1848 }
1849 Value::Array(arr) => {
1850 let redacted: Vec<Value> = arr.iter().map(|v| redact_recursive(v, keys)).collect();
1851 Value::Array(redacted)
1852 }
1853 _ => value.clone(),
1854 }
1855}
1856
1857defn!(RedactKeysFn, vec![arg!(any), arg!(string)], None);
1862
1863impl Function for RedactKeysFn {
1864 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1865 self.signature.validate(args, ctx)?;
1866
1867 let pattern = args[1]
1868 .as_str()
1869 .ok_or_else(|| custom_error(ctx, "Expected pattern string"))?;
1870
1871 let regex = regex::Regex::new(pattern)
1872 .map_err(|e| custom_error(ctx, &format!("Invalid regex pattern: {}", e)))?;
1873
1874 Ok(redact_keys_recursive(&args[0], ®ex))
1875 }
1876}
1877
1878fn redact_keys_recursive(value: &Value, pattern: ®ex::Regex) -> Value {
1879 match value {
1880 Value::Object(obj) => {
1881 let redacted: Map<String, Value> = obj
1882 .iter()
1883 .map(|(k, v)| {
1884 if pattern.is_match(k) {
1885 (k.clone(), Value::String("[REDACTED]".to_string()))
1886 } else {
1887 (k.clone(), redact_keys_recursive(v, pattern))
1888 }
1889 })
1890 .collect();
1891 Value::Object(redacted)
1892 }
1893 Value::Array(arr) => {
1894 let redacted: Vec<Value> = arr
1895 .iter()
1896 .map(|v| redact_keys_recursive(v, pattern))
1897 .collect();
1898 Value::Array(redacted)
1899 }
1900 _ => value.clone(),
1901 }
1902}
1903
1904defn!(MaskFn, vec![arg!(string)], Some(arg!(number)));
1909
1910impl Function for MaskFn {
1911 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1912 self.signature.validate(args, ctx)?;
1913
1914 let s = args[0]
1915 .as_str()
1916 .ok_or_else(|| custom_error(ctx, "Expected string argument"))?;
1917
1918 let show_last = if args.len() > 1 {
1919 args[1].as_f64().unwrap_or(4.0) as usize
1920 } else {
1921 4
1922 };
1923
1924 let len = s.len();
1925 let masked = if len <= show_last {
1926 "*".repeat(len)
1927 } else {
1928 let mask_count = len - show_last;
1929 format!("{}{}", "*".repeat(mask_count), &s[mask_count..])
1930 };
1931
1932 Ok(Value::String(masked))
1933 }
1934}
1935
1936defn!(PluckDeepFn, vec![arg!(any), arg!(string)], None);
1941
1942impl Function for PluckDeepFn {
1943 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1944 self.signature.validate(args, ctx)?;
1945
1946 let key = args[1]
1947 .as_str()
1948 .ok_or_else(|| custom_error(ctx, "Expected key string"))?;
1949
1950 let mut results: Vec<Value> = Vec::new();
1951 pluck_deep_recursive(&args[0], key, &mut results);
1952 Ok(Value::Array(results))
1953 }
1954}
1955
1956fn pluck_deep_recursive(value: &Value, key: &str, results: &mut Vec<Value>) {
1957 match value {
1958 Value::Object(obj) => {
1959 if let Some(v) = obj.get(key) {
1960 results.push(v.clone());
1961 }
1962 for (_, v) in obj.iter() {
1963 pluck_deep_recursive(v, key, results);
1964 }
1965 }
1966 Value::Array(arr) => {
1967 for v in arr {
1968 pluck_deep_recursive(v, key, results);
1969 }
1970 }
1971 _ => {}
1972 }
1973}
1974
1975defn!(PathsToFn, vec![arg!(any), arg!(string)], None);
1980
1981impl Function for PathsToFn {
1982 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1983 self.signature.validate(args, ctx)?;
1984
1985 let key = args[1]
1986 .as_str()
1987 .ok_or_else(|| custom_error(ctx, "Expected key string"))?;
1988
1989 let mut paths: Vec<String> = Vec::new();
1990 paths_to_recursive(&args[0], key, String::new(), &mut paths);
1991
1992 let result: Vec<Value> = paths.into_iter().map(Value::String).collect();
1993 Ok(Value::Array(result))
1994 }
1995}
1996
1997fn paths_to_recursive(value: &Value, key: &str, current_path: String, paths: &mut Vec<String>) {
1998 match value {
1999 Value::Object(obj) => {
2000 for (k, v) in obj.iter() {
2001 let new_path = if current_path.is_empty() {
2002 k.clone()
2003 } else {
2004 format!("{}.{}", current_path, k)
2005 };
2006 if k == key {
2007 paths.push(new_path.clone());
2008 }
2009 paths_to_recursive(v, key, new_path, paths);
2010 }
2011 }
2012 Value::Array(arr) => {
2013 for (idx, v) in arr.iter().enumerate() {
2014 let new_path = if current_path.is_empty() {
2015 idx.to_string()
2016 } else {
2017 format!("{}.{}", current_path, idx)
2018 };
2019 paths_to_recursive(v, key, new_path, paths);
2020 }
2021 }
2022 _ => {}
2023 }
2024}
2025
2026defn!(SnakeKeysFn, vec![arg!(any)], None);
2031
2032impl Function for SnakeKeysFn {
2033 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
2034 self.signature.validate(args, ctx)?;
2035 Ok(transform_keys_recursive(&args[0], |s| s.to_snake_case()))
2036 }
2037}
2038
2039defn!(CamelKeysFn, vec![arg!(any)], None);
2044
2045impl Function for CamelKeysFn {
2046 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
2047 self.signature.validate(args, ctx)?;
2048 Ok(transform_keys_recursive(&args[0], |s| {
2049 s.to_lower_camel_case()
2050 }))
2051 }
2052}
2053
2054defn!(KebabKeysFn, vec![arg!(any)], None);
2059
2060impl Function for KebabKeysFn {
2061 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
2062 self.signature.validate(args, ctx)?;
2063 Ok(transform_keys_recursive(&args[0], |s| s.to_kebab_case()))
2064 }
2065}
2066
2067defn!(PascalKeysFn, vec![arg!(any)], None);
2072
2073impl Function for PascalKeysFn {
2074 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
2075 self.signature.validate(args, ctx)?;
2076 Ok(transform_keys_recursive(&args[0], |s| {
2077 s.to_upper_camel_case()
2078 }))
2079 }
2080}
2081
2082defn!(ShoutySnakeKeysFn, vec![arg!(any)], None);
2087
2088impl Function for ShoutySnakeKeysFn {
2089 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
2090 self.signature.validate(args, ctx)?;
2091 Ok(transform_keys_recursive(&args[0], |s| {
2092 s.to_shouty_snake_case()
2093 }))
2094 }
2095}
2096
2097defn!(ShoutyKebabKeysFn, vec![arg!(any)], None);
2102
2103impl Function for ShoutyKebabKeysFn {
2104 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
2105 self.signature.validate(args, ctx)?;
2106 Ok(transform_keys_recursive(&args[0], |s| {
2107 s.to_shouty_kebab_case()
2108 }))
2109 }
2110}
2111
2112defn!(TrainKeysFn, vec![arg!(any)], None);
2117
2118impl Function for TrainKeysFn {
2119 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
2120 self.signature.validate(args, ctx)?;
2121 Ok(transform_keys_recursive(&args[0], |s| s.to_train_case()))
2122 }
2123}
2124
2125fn transform_keys_recursive<F>(value: &Value, transform: F) -> Value
2126where
2127 F: Fn(&str) -> String + Copy,
2128{
2129 match value {
2130 Value::Object(obj) => {
2131 let transformed: Map<String, Value> = obj
2132 .iter()
2133 .map(|(k, v)| (transform(k), transform_keys_recursive(v, transform)))
2134 .collect();
2135 Value::Object(transformed)
2136 }
2137 Value::Array(arr) => {
2138 let transformed: Vec<Value> = arr
2139 .iter()
2140 .map(|v| transform_keys_recursive(v, transform))
2141 .collect();
2142 Value::Array(transformed)
2143 }
2144 _ => value.clone(),
2145 }
2146}
2147
2148defn!(StructuralDiffFn, vec![arg!(any), arg!(any)], None);
2153
2154impl Function for StructuralDiffFn {
2155 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
2156 self.signature.validate(args, ctx)?;
2157
2158 let mut added: Vec<String> = Vec::new();
2159 let mut removed: Vec<String> = Vec::new();
2160 let mut type_changed: Vec<Map<String, Value>> = Vec::new();
2161 let mut unchanged: Vec<String> = Vec::new();
2162
2163 compare_structure(
2164 &args[0],
2165 &args[1],
2166 String::new(),
2167 &mut added,
2168 &mut removed,
2169 &mut type_changed,
2170 &mut unchanged,
2171 );
2172
2173 let mut result = Map::new();
2174 result.insert(
2175 "added".to_string(),
2176 Value::Array(added.into_iter().map(Value::String).collect()),
2177 );
2178 result.insert(
2179 "removed".to_string(),
2180 Value::Array(removed.into_iter().map(Value::String).collect()),
2181 );
2182 result.insert(
2183 "type_changed".to_string(),
2184 Value::Array(type_changed.into_iter().map(Value::Object).collect()),
2185 );
2186 result.insert(
2187 "unchanged".to_string(),
2188 Value::Array(unchanged.into_iter().map(Value::String).collect()),
2189 );
2190
2191 Ok(Value::Object(result))
2192 }
2193}
2194
2195fn get_structural_type(value: &Value) -> &'static str {
2196 match value {
2197 Value::Null => "null",
2198 Value::Bool(_) => "boolean",
2199 Value::Number(_) => "number",
2200 Value::String(_) => "string",
2201 Value::Array(_) => "array",
2202 Value::Object(_) => "object",
2203 }
2204}
2205
2206fn compare_structure(
2207 a: &Value,
2208 b: &Value,
2209 path: String,
2210 added: &mut Vec<String>,
2211 removed: &mut Vec<String>,
2212 type_changed: &mut Vec<Map<String, Value>>,
2213 unchanged: &mut Vec<String>,
2214) {
2215 let type_a = get_structural_type(a);
2216 let type_b = get_structural_type(b);
2217
2218 if type_a != type_b {
2219 let mut change = Map::new();
2220 change.insert(
2221 "path".to_string(),
2222 Value::String(if path.is_empty() {
2223 "$".to_string()
2224 } else {
2225 path
2226 }),
2227 );
2228 change.insert("from".to_string(), Value::String(type_a.to_string()));
2229 change.insert("to".to_string(), Value::String(type_b.to_string()));
2230 type_changed.push(change);
2231 return;
2232 }
2233
2234 match (a, b) {
2235 (Value::Object(obj_a), Value::Object(obj_b)) => {
2236 for key in obj_a.keys() {
2237 let new_path = if path.is_empty() {
2238 key.clone()
2239 } else {
2240 format!("{}.{}", path, key)
2241 };
2242 if let Some(val_b) = obj_b.get(key) {
2243 compare_structure(
2244 obj_a.get(key).unwrap(),
2245 val_b,
2246 new_path,
2247 added,
2248 removed,
2249 type_changed,
2250 unchanged,
2251 );
2252 } else {
2253 removed.push(new_path);
2254 }
2255 }
2256 for key in obj_b.keys() {
2257 if !obj_a.contains_key(key) {
2258 let new_path = if path.is_empty() {
2259 key.clone()
2260 } else {
2261 format!("{}.{}", path, key)
2262 };
2263 added.push(new_path);
2264 }
2265 }
2266 }
2267 _ => {
2268 if !path.is_empty() {
2269 unchanged.push(path);
2270 }
2271 }
2272 }
2273}
2274
2275defn!(HasSameShapeFn, vec![arg!(any), arg!(any)], None);
2280
2281impl Function for HasSameShapeFn {
2282 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
2283 self.signature.validate(args, ctx)?;
2284 let same = check_same_shape(&args[0], &args[1]);
2285 Ok(Value::Bool(same))
2286 }
2287}
2288
2289fn check_same_shape(a: &Value, b: &Value) -> bool {
2290 let type_a = get_structural_type(a);
2291 let type_b = get_structural_type(b);
2292
2293 if type_a != type_b {
2294 return false;
2295 }
2296
2297 match (a, b) {
2298 (Value::Object(obj_a), Value::Object(obj_b)) => {
2299 if obj_a.keys().collect::<HashSet<_>>() != obj_b.keys().collect::<HashSet<_>>() {
2300 return false;
2301 }
2302 for key in obj_a.keys() {
2303 if !check_same_shape(obj_a.get(key).unwrap(), obj_b.get(key).unwrap()) {
2304 return false;
2305 }
2306 }
2307 true
2308 }
2309 (Value::Array(arr_a), Value::Array(arr_b)) => {
2310 if arr_a.is_empty() && arr_b.is_empty() {
2311 return true;
2312 }
2313 if arr_a.is_empty() || arr_b.is_empty() {
2314 return true;
2315 }
2316 check_same_shape(&arr_a[0], &arr_b[0])
2317 }
2318 _ => true,
2319 }
2320}
2321
2322defn!(InferSchemaFn, vec![arg!(any)], None);
2327
2328impl Function for InferSchemaFn {
2329 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
2330 self.signature.validate(args, ctx)?;
2331 Ok(infer_schema_recursive(&args[0]))
2332 }
2333}
2334
2335fn infer_schema_recursive(value: &Value) -> Value {
2336 match value {
2337 Value::Null => {
2338 let mut schema = Map::new();
2339 schema.insert("type".to_string(), Value::String("null".to_string()));
2340 Value::Object(schema)
2341 }
2342 Value::Bool(_) => {
2343 let mut schema = Map::new();
2344 schema.insert("type".to_string(), Value::String("boolean".to_string()));
2345 Value::Object(schema)
2346 }
2347 Value::Number(_) => {
2348 let mut schema = Map::new();
2349 schema.insert("type".to_string(), Value::String("number".to_string()));
2350 Value::Object(schema)
2351 }
2352 Value::String(_) => {
2353 let mut schema = Map::new();
2354 schema.insert("type".to_string(), Value::String("string".to_string()));
2355 Value::Object(schema)
2356 }
2357 Value::Array(arr) => {
2358 let mut schema = Map::new();
2359 schema.insert("type".to_string(), Value::String("array".to_string()));
2360 if !arr.is_empty() {
2361 let items_schema = infer_schema_recursive(&arr[0]);
2362 schema.insert("items".to_string(), items_schema);
2363 }
2364 Value::Object(schema)
2365 }
2366 Value::Object(obj) => {
2367 let mut schema = Map::new();
2368 schema.insert("type".to_string(), Value::String("object".to_string()));
2369
2370 let mut properties = Map::new();
2371 for (key, val) in obj.iter() {
2372 let prop_schema = infer_schema_recursive(val);
2373 properties.insert(key.clone(), prop_schema);
2374 }
2375 schema.insert("properties".to_string(), Value::Object(properties));
2376 Value::Object(schema)
2377 }
2378 }
2379}
2380
2381defn!(ChunkBySizeFn, vec![arg!(array), arg!(number)], None);
2386
2387impl Function for ChunkBySizeFn {
2388 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
2389 self.signature.validate(args, ctx)?;
2390
2391 let arr = args[0]
2392 .as_array()
2393 .ok_or_else(|| custom_error(ctx, "Expected array"))?;
2394
2395 let max_bytes = args[1].as_f64().unwrap_or(4000.0) as usize;
2396
2397 let mut chunks: Vec<Value> = Vec::new();
2398 let mut current_chunk: Vec<Value> = Vec::new();
2399 let mut current_size: usize = 2;
2400
2401 for item in arr {
2402 let item_size = serde_json::to_string(item).map(|s| s.len()).unwrap_or(0);
2403
2404 if current_size + item_size + 1 > max_bytes && !current_chunk.is_empty() {
2405 chunks.push(Value::Array(current_chunk));
2406 current_chunk = Vec::new();
2407 current_size = 2;
2408 }
2409
2410 current_chunk.push(item.clone());
2411 current_size += item_size + 1;
2412 }
2413
2414 if !current_chunk.is_empty() {
2415 chunks.push(Value::Array(current_chunk));
2416 }
2417
2418 Ok(Value::Array(chunks))
2419 }
2420}
2421
2422defn!(
2427 PaginateFn,
2428 vec![arg!(array), arg!(number), arg!(number)],
2429 None
2430);
2431
2432impl Function for PaginateFn {
2433 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
2434 self.signature.validate(args, ctx)?;
2435
2436 let arr = args[0]
2437 .as_array()
2438 .ok_or_else(|| custom_error(ctx, "Expected array"))?;
2439
2440 let page = args[1].as_f64().unwrap_or(1.0).max(1.0) as usize;
2441 let per_page = args[2].as_f64().unwrap_or(10.0).max(1.0) as usize;
2442
2443 let total = arr.len();
2444 let total_pages = total.div_ceil(per_page);
2445 let start = (page - 1) * per_page;
2446 let end = (start + per_page).min(total);
2447
2448 let data: Vec<Value> = if start < total {
2449 arr[start..end].to_vec()
2450 } else {
2451 vec![]
2452 };
2453
2454 let mut result = Map::new();
2455 result.insert("data".to_string(), Value::Array(data));
2456 result.insert("page".to_string(), Value::Number(Number::from(page as i64)));
2457 result.insert(
2458 "per_page".to_string(),
2459 Value::Number(Number::from(per_page as i64)),
2460 );
2461 result.insert(
2462 "total".to_string(),
2463 Value::Number(Number::from(total as i64)),
2464 );
2465 result.insert(
2466 "total_pages".to_string(),
2467 Value::Number(Number::from(total_pages as i64)),
2468 );
2469 result.insert("has_next".to_string(), Value::Bool(page < total_pages));
2470 result.insert("has_prev".to_string(), Value::Bool(page > 1));
2471
2472 Ok(Value::Object(result))
2473 }
2474}
2475
2476defn!(EstimateSizeFn, vec![arg!(any)], None);
2481
2482impl Function for EstimateSizeFn {
2483 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
2484 self.signature.validate(args, ctx)?;
2485 let size = serde_json::to_string(&args[0])
2486 .map(|s| s.len())
2487 .unwrap_or(0);
2488 Ok(Value::Number(Number::from(size as i64)))
2489 }
2490}
2491
2492defn!(TruncateToSizeFn, vec![arg!(any), arg!(number)], None);
2497
2498impl Function for TruncateToSizeFn {
2499 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
2500 self.signature.validate(args, ctx)?;
2501
2502 let max_bytes = args[1].as_f64().unwrap_or(1000.0) as usize;
2503 let current_size = serde_json::to_string(&args[0])
2504 .map(|s| s.len())
2505 .unwrap_or(0);
2506
2507 if current_size <= max_bytes {
2508 return Ok(args[0].clone());
2509 }
2510
2511 if let Some(arr) = args[0].as_array() {
2512 let mut result: Vec<Value> = Vec::new();
2513 let mut size = 2;
2514
2515 for item in arr {
2516 let item_size = serde_json::to_string(item).map(|s| s.len()).unwrap_or(0);
2517 if size + item_size + 1 > max_bytes {
2518 break;
2519 }
2520 result.push(item.clone());
2521 size += item_size + 1;
2522 }
2523 return Ok(Value::Array(result));
2524 }
2525
2526 if let Some(s) = args[0].as_str() {
2527 let target_len = max_bytes.saturating_sub(2);
2528 let truncated: String = s.chars().take(target_len).collect();
2529 return Ok(Value::String(truncated));
2530 }
2531
2532 Ok(args[0].clone())
2533 }
2534}
2535
2536defn!(TemplateFn, vec![arg!(any), arg!(string)], None);
2541
2542impl Function for TemplateFn {
2543 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
2544 if args.len() >= 2 && args[1].is_null() {
2545 return Err(custom_error(
2546 ctx,
2547 "template: second argument is null. Template strings must be JMESPath \
2548 literals using backticks, e.g., template(@, `\"Hello {{name}}\"`)",
2549 ));
2550 }
2551
2552 self.signature.validate(args, ctx)?;
2553
2554 let template = args[1]
2555 .as_str()
2556 .ok_or_else(|| custom_error(ctx, "Expected template string"))?;
2557
2558 let result =
2559 expand_template(&args[0], template, false).map_err(|e| custom_error(ctx, &e))?;
2560 Ok(Value::String(result))
2561 }
2562}
2563
2564defn!(TemplateStrictFn, vec![arg!(any), arg!(string)], None);
2569
2570impl Function for TemplateStrictFn {
2571 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
2572 if args.len() >= 2 && args[1].is_null() {
2573 return Err(custom_error(
2574 ctx,
2575 "template_strict: second argument is null. Template strings must be JMESPath \
2576 literals using backticks, e.g., template_strict(@, `\"Hello {{name}}\"`)",
2577 ));
2578 }
2579
2580 self.signature.validate(args, ctx)?;
2581
2582 let template = args[1]
2583 .as_str()
2584 .ok_or_else(|| custom_error(ctx, "Expected template string"))?;
2585
2586 let result =
2587 expand_template(&args[0], template, true).map_err(|e| custom_error(ctx, &e))?;
2588 Ok(Value::String(result))
2589 }
2590}
2591
2592fn expand_template(data: &Value, template: &str, strict: bool) -> Result<String, String> {
2593 let mut result = String::new();
2594 let mut chars = template.chars().peekable();
2595
2596 while let Some(c) = chars.next() {
2597 if c == '{' && chars.peek() == Some(&'{') {
2598 chars.next();
2599
2600 let mut var_name = String::new();
2601 let mut fallback: Option<String> = None;
2602
2603 while let Some(&next) = chars.peek() {
2604 if next == '}' {
2605 chars.next();
2606 if chars.peek() == Some(&'}') {
2607 chars.next();
2608 break;
2609 }
2610 } else if next == '|' {
2611 chars.next();
2612 let mut fb = String::new();
2613 while let Some(&fc) = chars.peek() {
2614 if fc == '}' {
2615 break;
2616 }
2617 fb.push(chars.next().unwrap());
2618 }
2619 fallback = Some(fb);
2620 } else {
2621 var_name.push(chars.next().unwrap());
2622 }
2623 }
2624
2625 let value = get_template_value(data, &var_name);
2626
2627 match value {
2628 Some(v) => result.push_str(&value_to_string(&v)),
2629 None => {
2630 if strict {
2631 return Err(format!("missing variable '{}'", var_name));
2632 }
2633 if let Some(fb) = fallback {
2634 result.push_str(&fb);
2635 }
2636 }
2637 }
2638 } else if c == '\\' && chars.peek() == Some(&'{') {
2639 result.push(chars.next().unwrap());
2640 } else {
2641 result.push(c);
2642 }
2643 }
2644
2645 Ok(result)
2646}
2647
2648fn get_template_value(data: &Value, path: &str) -> Option<Value> {
2649 let parts: Vec<&str> = path.trim().split('.').collect();
2650 let mut current = data.clone();
2651
2652 for part in parts {
2653 if let Ok(idx) = part.parse::<usize>() {
2654 if let Some(arr) = current.as_array()
2655 && idx < arr.len()
2656 {
2657 current = arr[idx].clone();
2658 continue;
2659 }
2660 return None;
2661 }
2662
2663 if let Some(obj) = current.as_object() {
2664 if let Some(val) = obj.get(part) {
2665 current = val.clone();
2666 } else {
2667 return None;
2668 }
2669 } else {
2670 return None;
2671 }
2672 }
2673
2674 if current.is_null() {
2675 None
2676 } else {
2677 Some(current)
2678 }
2679}
2680
2681fn value_to_string(value: &Value) -> String {
2682 match value {
2683 Value::String(s) => s.clone(),
2684 Value::Number(n) => n.to_string(),
2685 Value::Bool(b) => b.to_string(),
2686 Value::Null => String::new(),
2687 _ => serde_json::to_string(value).unwrap_or_default(),
2688 }
2689}