1use crate::ast::{Value, ValueRef};
23use serde_json::json;
24
25#[crate::polydat_node(category = Json)]
42fn json_with(
43 key: crate::derive_support::Const<&str>,
44 value: Value,
45) -> std::sync::Arc<serde_json::Value> {
46 let mut m = serde_json::Map::new();
47 m.insert(key.0.to_string(), value_to_json(&value));
48 std::sync::Arc::new(serde_json::Value::Object(m))
49}
50
51#[crate::polydat_node(category = Json, variadic_min = 0)]
61fn json_object(parts: &[Value]) -> std::sync::Arc<serde_json::Value> {
62 std::sync::Arc::new(json_object_of(parts))
63}
64
65pub(crate) fn json_object_of_refs<'a>(
68 parts: impl IntoIterator<Item = ValueRef<'a>>,
69) -> serde_json::Value {
70 let mut merged = serde_json::Map::new();
71 for v in parts {
72 if let ValueRef::Json(serde_json::Value::Object(map)) = v {
73 for (k, val) in map {
74 merged.insert(k.clone(), val.clone());
75 }
76 }
77 }
78 serde_json::Value::Object(merged)
79}
80
81pub(crate) fn json_object_of(parts: &[Value]) -> serde_json::Value {
83 json_object_of_refs(parts.iter().map(ValueRef::from))
84}
85
86pub(crate) fn json_array_of_refs<'a>(
88 elems: impl IntoIterator<Item = ValueRef<'a>>,
89) -> serde_json::Value {
90 serde_json::Value::Array(elems.into_iter().map(|v| json_of_ref(v)).collect())
91}
92
93pub(crate) fn json_array_of(elems: &[Value]) -> serde_json::Value {
95 json_array_of_refs(elems.iter().map(ValueRef::from))
96}
97
98#[crate::polydat_node(category = Json, variadic_min = 0)]
105fn json_array(elems: &[Value]) -> std::sync::Arc<serde_json::Value> {
106 std::sync::Arc::new(json_array_of(elems))
107}
108
109#[crate::polydat_node(category = Json)]
116fn to_json(input: Value) -> std::sync::Arc<serde_json::Value> {
117 std::sync::Arc::new(value_to_json(&input))
118}
119
120#[crate::polydat_node(category = Json)]
124fn json_merge(
125 left: &serde_json::Value,
126 right: &serde_json::Value,
127) -> std::sync::Arc<serde_json::Value> {
128 let mut result = left.clone();
129 if let (serde_json::Value::Object(base), serde_json::Value::Object(overlay)) =
130 (&mut result, right)
131 {
132 for (k, v) in overlay {
133 base.insert(k.clone(), v.clone());
134 }
135 }
136 std::sync::Arc::new(result)
137}
138
139#[crate::polydat_node(category = Conversions)]
151fn json_to_str(input: &serde_json::Value) -> String {
152 input.to_string()
153}
154
155#[crate::polydat_node(category = Json)]
157fn json_to_str_pretty(input: &serde_json::Value) -> String {
158 serde_json::to_string_pretty(input).unwrap_or_default()
159}
160
161#[crate::polydat_node(category = Json)]
163fn str_to_json(input: &str) -> std::sync::Arc<serde_json::Value> {
164 let parsed = serde_json::from_str(input).unwrap_or(serde_json::Value::Null);
165 std::sync::Arc::new(parsed)
166}
167
168#[crate::polydat_node(category = Json)]
176fn escape_json(input: String) -> String {
177 let json_str = serde_json::to_string(&input).unwrap_or_default();
179 json_str[1..json_str.len() - 1].to_string()
180}
181
182#[crate::polydat_node(category = Json)]
191fn json_field(
192 input: &serde_json::Value,
193 key: crate::derive_support::Const<&str>,
194) -> std::sync::Arc<serde_json::Value> {
195 std::sync::Arc::new(input.get(*key).cloned().unwrap_or(serde_json::Value::Null))
196}
197
198pub(crate) fn value_to_json(v: &Value) -> serde_json::Value {
203 json_of_ref(ValueRef::from(v))
204}
205
206pub(crate) fn json_of_ref(v: ValueRef<'_>) -> serde_json::Value {
209 match v {
210 ValueRef::Bytes(b) => {
216 use base64::Engine;
217 json!(base64::engine::general_purpose::STANDARD.encode(b))
218 }
219 other => other.to_json_value(),
220 }
221}
222
223#[crate::polydat_node(category = Json)]
248fn json_text(input: Value) -> String {
249 json_text_of(&input)
250}
251
252pub(crate) fn json_text_of(input: &Value) -> String {
254 let mut buf = String::new();
255 json_text_into(input, &mut buf);
256 buf
257}
258
259pub(crate) fn json_text_into<W: std::fmt::Write>(input: &Value, out: &mut W) {
261 json_text_ref_into(ValueRef::from(input), out);
262}
263
264pub(crate) fn json_text_ref_into<W: std::fmt::Write>(input: ValueRef<'_>, out: &mut W) {
266 match input {
267 ValueRef::Json(j) => {
268 let mut first = true;
269 walk_json_leaves(j, out, &mut first);
270 }
271 other => {
272 let _ = out.write_str(&other.display());
273 }
274 }
275}
276
277fn walk_json_leaves<W: std::fmt::Write>(j: &serde_json::Value, out: &mut W, first: &mut bool) {
278 use serde_json::Value as J;
279 let mut leaf = |out: &mut W, text: &str| {
280 if !*first {
281 let _ = out.write_char('\n');
282 }
283 *first = false;
284 let _ = out.write_str(text);
285 };
286 match j {
287 J::String(s) => leaf(out, s),
288 J::Number(n) => leaf(out, &n.to_string()),
289 J::Bool(b) => leaf(out, if *b { "true" } else { "false" }),
290 J::Null => {}
291 J::Array(arr) => {
292 for item in arr {
293 walk_json_leaves(item, out, first);
294 }
295 }
296 J::Object(obj) => {
297 for value in obj.values() {
298 walk_json_leaves(value, out, first);
299 }
300 }
301 }
302}
303
304#[crate::polydat_node(category = Json)]
336fn body_column_i32(body: Value, column: crate::derive_support::Const<&str>) -> Vec<i32> {
337 let json = match &body {
338 Value::Json(j) => j,
339 _ => return Vec::new(),
340 };
341 extract_column_i32(json, column.0)
342}
343
344fn extract_column_i32(json: &serde_json::Value, column: &str) -> Vec<i32> {
349 match json {
350 serde_json::Value::Array(rows) => rows
351 .iter()
352 .filter_map(|row| json_value_as_i32(row.get(column)?))
353 .collect(),
354 serde_json::Value::Object(obj) => {
355 if let Some(serde_json::Value::Array(rows)) = obj.get("rows") {
361 return rows
362 .iter()
363 .filter_map(|row| json_value_as_i32(row.get(column)?))
364 .collect();
365 }
366 obj.get(column)
367 .and_then(json_value_as_i32)
368 .map(|n| vec![n])
369 .unwrap_or_default()
370 }
371 _ => Vec::new(),
372 }
373}
374
375fn json_value_as_i32(v: &serde_json::Value) -> Option<i32> {
381 match v {
382 serde_json::Value::Number(n) => n
383 .as_i64()
384 .map(|i| i.clamp(i32::MIN as i64, i32::MAX as i64) as i32)
385 .or_else(|| n.as_u64().map(|u| u.min(i32::MAX as u64) as i32))
386 .or_else(|| {
387 n.as_f64()
388 .and_then(|f| if f.is_finite() { Some(f as i32) } else { None })
389 }),
390 serde_json::Value::String(s) => s.trim().parse::<i32>().ok(),
391 _ => None,
392 }
393}
394
395#[crate::polydat_node(category = Json)]
408fn normalize_vector(vector: &str) -> String {
409 let trimmed = vector.trim();
410 if !trimmed.starts_with('[') || !trimmed.ends_with(']') {
411 return vector.to_string();
412 }
413 let inner = &trimmed[1..trimmed.len() - 1];
414 let values: Vec<f64> = inner
415 .split(',')
416 .filter_map(|v| v.trim().parse::<f64>().ok())
417 .collect();
418 let norm = values.iter().map(|v| v * v).sum::<f64>().sqrt();
419 if norm < 1e-15 {
420 return vector.to_string();
421 }
422 let normalized: Vec<String> = values.iter().map(|v| format!("{}", v / norm)).collect();
423 format!("[{}]", normalized.join(","))
424}
425
426#[crate::polydat_node(category = Json)]
435fn random_vector(
436 seed: u64,
437 dim: u64,
438 #[poly_default(0.0f64)] min: crate::derive_support::Const<f64>,
439 #[poly_default(1.0f64)] max: crate::derive_support::Const<f64>,
440) -> String {
441 let min_v = *min;
442 let max_v = *max;
443 let range = max_v - min_v;
444 let dim = dim as usize;
445 let mut h = seed;
446 let mut values = Vec::with_capacity(dim);
447 for _ in 0..dim {
448 h = xxhash_rust::xxh3::xxh3_64(&h.to_le_bytes());
449 let unit = (h as f64) / (u64::MAX as f64); values.push(format!("{}", min_v + range * unit));
451 }
452 format!("[{}]", values.join(","))
453}
454
455#[crate::polydat_node(category = Json)]
464fn array_len(input: &str) -> u64 {
465 let trimmed = input.trim();
466 if trimmed == "[]" || trimmed.is_empty() {
467 0
468 } else if trimmed.starts_with('[') && trimmed.ends_with(']') {
469 let inner = &trimmed[1..trimmed.len() - 1];
470 inner.split(',').count() as u64
471 } else {
472 0
473 }
474}
475
476#[crate::polydat_node(category = Json)]
481fn array_at(array: &str, index: u64) -> String {
482 let trimmed = array.trim();
483 if trimmed.starts_with('[') && trimmed.ends_with(']') {
484 let inner = &trimmed[1..trimmed.len() - 1];
485 let elements: Vec<&str> = inner.split(',').map(|e| e.trim()).collect();
486 if elements.is_empty() || (elements.len() == 1 && elements[0].is_empty()) {
487 String::new()
488 } else {
489 elements[(index as usize) % elements.len()].to_string()
490 }
491 } else {
492 String::new()
493 }
494}
495
496#[cfg(test)]
497mod tests {
498 use super::*;
499 use crate::ast::{PolydatNode, PortType, Value};
500
501 #[test]
502 fn body_column_i32_extracts_array_of_rows() {
503 let body = Value::Json(std::sync::Arc::new(serde_json::json!([
505 { "key": 4, "value": 0.5 },
506 { "key": 17, "value": 0.4 },
507 { "key": 42, "value": 0.3 },
508 ])));
509 let node = BodyColumnI32::new(PortType::Json, "key".to_string());
510 let mut out = [Value::None];
511 node.eval(&[body], &mut out);
512 let Value::VecI32(slice) = &out[0] else {
513 panic!("expected VecI32, got {:?}", out[0])
514 };
515 assert_eq!(slice.as_slice(), &[4, 17, 42]);
516 }
517
518 #[test]
519 fn body_column_i32_extracts_envelope_rows() {
520 let body = Value::Json(std::sync::Arc::new(serde_json::json!({
522 "rows": [
523 { "id": 1 },
524 { "id": 7 },
525 { "id": 13 },
526 ],
527 "metadata": "ignored",
528 })));
529 let node = BodyColumnI32::new(PortType::Json, "id".to_string());
530 let mut out = [Value::None];
531 node.eval(&[body], &mut out);
532 let Value::VecI32(slice) = &out[0] else {
533 panic!("expected VecI32")
534 };
535 assert_eq!(slice.as_slice(), &[1, 7, 13]);
536 }
537
538 #[test]
539 fn body_column_i32_skips_rows_missing_column() {
540 let body = Value::Json(std::sync::Arc::new(serde_json::json!([
542 { "key": 1 },
543 { "other": 2 }, { "key": "not_an_int" }, { "key": 3 },
546 ])));
547 let node = BodyColumnI32::new(PortType::Json, "key".to_string());
548 let mut out = [Value::None];
549 node.eval(&[body], &mut out);
550 let Value::VecI32(slice) = &out[0] else {
551 panic!("expected VecI32")
552 };
553 assert_eq!(slice.as_slice(), &[1, 3]);
554 }
555
556 #[test]
557 fn body_column_i32_string_numeric_parses() {
558 let body = Value::Json(std::sync::Arc::new(serde_json::json!([
561 { "key": "42" },
562 { "key": "-7" },
563 ])));
564 let node = BodyColumnI32::new(PortType::Json, "key".to_string());
565 let mut out = [Value::None];
566 node.eval(&[body], &mut out);
567 let Value::VecI32(slice) = &out[0] else {
568 panic!("expected VecI32")
569 };
570 assert_eq!(slice.as_slice(), &[42, -7]);
571 }
572
573 #[test]
574 fn body_column_i32_empty_body_produces_empty_vec() {
575 let body = Value::Json(std::sync::Arc::new(serde_json::json!([])));
576 let node = BodyColumnI32::new(PortType::Json, "key".to_string());
577 let mut out = [Value::None];
578 node.eval(&[body], &mut out);
579 let Value::VecI32(slice) = &out[0] else {
580 panic!("expected VecI32")
581 };
582 assert!(slice.as_slice().is_empty());
583 }
584
585 #[test]
586 fn body_column_i32_non_json_input_produces_empty_vec() {
587 let node = BodyColumnI32::new(PortType::Json, "key".to_string());
591 let mut out = [Value::None];
592 node.eval(&[Value::Str("not json".into())], &mut out);
593 let Value::VecI32(slice) = &out[0] else {
594 panic!("expected VecI32")
595 };
596 assert!(slice.as_slice().is_empty());
597 }
598
599 #[test]
606 fn json_text_flattens_multirow_describe_for_regex() {
607 let body = Value::Json(std::sync::Arc::new(serde_json::json!([
608 {
609 "keyspace_name": "system_views",
610 "type": "table",
611 "name": "sai_column_indexes",
612 "create_statement": "CREATE TABLE system_views.sai_column_indexes (\n keyspace_name text,\n table_name text\n);"
613 },
614 {
615 "keyspace_name": "system_views",
616 "type": "table",
617 "name": "indexes",
618 "create_statement": "CREATE VIRTUAL TABLE system_views.indexes (\n keyspace_name text\n);"
619 },
620 ])));
621
622 let node = JsonText::new(PortType::Json);
623 let mut out = [Value::None];
624 node.eval(&[body], &mut out);
625
626 let text = match &out[0] {
627 Value::Str(s) => s.clone(),
628 other => panic!("expected Str, got {other:?}"),
629 };
630
631 assert!(text.contains("CREATE TABLE system_views.sai_column_indexes (\n"));
635 assert!(text.contains("CREATE VIRTUAL TABLE system_views.indexes (\n"));
636
637 let pat = regex::Regex::new(
640 r"(?im)^\s*(?:CREATE\s+)?(?:VIRTUAL\s+)?TABLE\s+system_views\.sai_column_indexes\s*\(",
641 )
642 .unwrap();
643 assert!(
644 pat.is_match(&text),
645 "regex should match the flattened schema text"
646 );
647 }
648
649 fn jw(key: &str, value_pt: PortType, value: Value) -> Value {
654 let node = JsonWith::new(key.to_string(), value_pt);
655 let mut out = [Value::None];
656 node.eval(&[value], &mut out);
657 std::mem::replace(&mut out[0], Value::None)
658 }
659
660 #[test]
661 fn json_object_basic() {
662 let name = jw("name", PortType::Str, Value::Str("Alice".into()));
664 let age = jw("age", PortType::U64, Value::U64(30));
665 let active = jw("active", PortType::Bool, Value::Bool(true));
666
667 let node = JsonObject::new(3);
668 let mut out = [Value::None];
669 node.eval(&[name, age, active], &mut out);
670 let j = out[0].as_json();
671 assert_eq!(j["name"], "Alice");
672 assert_eq!(j["age"], 30);
673 assert_eq!(j["active"], true);
674 }
675
676 #[test]
677 fn json_object_nested() {
678 let inner_x = jw("x", PortType::U64, Value::U64(10));
680 let inner_y = jw("y", PortType::U64, Value::U64(20));
681 let inner_node = JsonObject::new(2);
682 let mut inner_out = [Value::None];
683 inner_node.eval(&[inner_x, inner_y], &mut inner_out);
684
685 let point = jw(
687 "point",
688 PortType::Json,
689 std::mem::replace(&mut inner_out[0], Value::None),
690 );
691 let outer = JsonObject::new(1);
692 let mut out = [Value::None];
693 outer.eval(&[point], &mut out);
694 let j = out[0].as_json();
695 assert_eq!(j["point"]["x"], 10);
696 assert_eq!(j["point"]["y"], 20);
697 }
698
699 #[test]
700 fn json_object_later_part_wins_on_collision() {
701 let first = jw("k", PortType::U64, Value::U64(1));
704 let second = jw("k", PortType::U64, Value::U64(2));
705 let node = JsonObject::new(2);
706 let mut out = [Value::None];
707 node.eval(&[first, second], &mut out);
708 assert_eq!(out[0].as_json()["k"], 2);
709 }
710
711 #[test]
712 fn json_object_skips_non_json_parts() {
713 let valid = jw("k", PortType::U64, Value::U64(1));
716 let node = JsonObject::new(2);
717 let mut out = [Value::None];
718 node.eval(&[valid, Value::None], &mut out);
719 let j = out[0].as_json();
720 assert_eq!(j["k"], 1);
721 assert_eq!(j.as_object().unwrap().len(), 1);
722 }
723
724 #[test]
725 fn json_array_basic() {
726 let node = JsonArray::new(3);
727 let mut out = [Value::None];
728 node.eval(
729 &[Value::U64(1), Value::Str("two".into()), Value::F64(3.0)],
730 &mut out,
731 );
732 let j = out[0].as_json();
733 let arr = j.as_array().unwrap();
734 assert_eq!(arr.len(), 3);
735 assert_eq!(arr[0], 1);
736 assert_eq!(arr[1], "two");
737 assert_eq!(arr[2], 3.0);
738 }
739
740 #[test]
741 fn json_to_str_compact() {
742 let node = JsonToStr::new();
743 let mut out = [Value::None];
744 let input = Value::Json(std::sync::Arc::new(json!({"a": 1, "b": "hello"})));
745 node.eval(&[input], &mut out);
746 let s = out[0].as_str();
747 assert!(s.contains("\"a\":1") || s.contains("\"a\": 1"));
748 assert!(s.contains("\"b\":\"hello\"") || s.contains("\"b\": \"hello\""));
749 }
750
751 #[test]
752 fn str_to_json_roundtrip() {
753 let to_str = JsonToStr::new();
754 let from_str = StrToJson::default();
755 let original = Value::Json(std::sync::Arc::new(json!({"key": [1, 2, 3]})));
756 let mut mid = [Value::None];
757 let mut out = [Value::None];
758 to_str.eval(std::slice::from_ref(&original), &mut mid);
759 from_str.eval(&[mid[0].clone()], &mut out);
760 assert_eq!(out[0].as_json(), original.as_json());
761 }
762
763 #[test]
764 fn escape_json_basic() {
765 let node = EscapeJson::new();
766 let mut out = [Value::None];
767 node.eval(&[Value::Str("hello \"world\"\nline2".into())], &mut out);
768 let s = out[0].as_str();
769 assert!(s.contains("\\\""));
770 assert!(s.contains("\\n"));
771 assert!(!s.starts_with('"'));
772 }
773
774 #[test]
775 fn json_merge_basic() {
776 let node = JsonMerge::new();
777 let mut out = [Value::None];
778 let left = Value::Json(std::sync::Arc::new(json!({"a": 1, "b": 2})));
779 let right = Value::Json(std::sync::Arc::new(json!({"b": 99, "c": 3})));
780 node.eval(&[left, right], &mut out);
781 let j = out[0].as_json();
782 assert_eq!(j["a"], 1);
783 assert_eq!(j["b"], 99); assert_eq!(j["c"], 3);
785 }
786
787 #[test]
788 fn json_field_basic() {
789 let node = JsonField::new("name".to_string());
790 let mut out = [Value::None];
791 node.eval(
792 &[Value::Json(std::sync::Arc::new(
793 json!({"name": "Alice", "age": 30}),
794 ))],
795 &mut out,
796 );
797 assert_eq!(out[0].as_json(), &json!("Alice"));
798 }
799
800 #[test]
801 fn json_field_missing() {
802 let node = JsonField::new("missing".to_string());
803 let mut out = [Value::None];
804 node.eval(
805 &[Value::Json(std::sync::Arc::new(json!({"name": "Alice"})))],
806 &mut out,
807 );
808 assert!(out[0].as_json().is_null());
809 }
810
811 #[test]
812 fn to_json_from_u64() {
813 let node = ToJson::new(PortType::U64);
814 let mut out = [Value::None];
815 node.eval(&[Value::U64(42)], &mut out);
816 assert_eq!(out[0].as_json(), &json!(42));
817 }
818
819 #[test]
820 fn json_pretty_print() {
821 let node = JsonToStrPretty::default();
822 let mut out = [Value::None];
823 node.eval(
824 &[Value::Json(std::sync::Arc::new(json!({"a": 1})))],
825 &mut out,
826 );
827 let s = out[0].as_str();
828 assert!(s.contains('\n'), "pretty print should have newlines");
829 }
830}