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