use crate::node::{GkNode, NodeMeta, Port, PortType, Slot, Value};
pub struct ExactlyOneValue {
meta: NodeMeta,
}
impl Default for ExactlyOneValue {
fn default() -> Self {
Self::new()
}
}
impl ExactlyOneValue {
pub fn new() -> Self {
Self {
meta: NodeMeta {
name: "exactly_one_value".into(),
outs: vec![Port::new("output", PortType::Str)],
ins: vec![Slot::Wire(Port::new("body", PortType::Str))],
},
}
}
}
impl GkNode for ExactlyOneValue {
fn meta(&self) -> &NodeMeta {
&self.meta
}
fn eval(&self, inputs: &[Value], outputs: &mut [Value]) {
let body = &inputs[0];
if crate::nodes::debug_nodes_enabled() {
let body_disp = body.to_display_string();
let snippet: String = body_disp.chars().take(400).collect();
let ellipsis = if body_disp.len() > snippet.len() { "…" } else { "" };
eprintln!(
"[DEBUG] exactly_one_value: body.variant={:?} body.len={} snippet={}{ellipsis}",
body.port_type(),
body_disp.len(),
snippet,
);
}
outputs[0] = match body {
Value::Str(_) | Value::Bool(_) | Value::U64(_) | Value::F64(_) => body.clone(),
Value::VecF32(arc) => {
if arc.len() != 1 {
panic!(
"exactly_one_value: expected unary structure \
(1 row × 1 column), found vec_f32 of length {}",
arc.len()
);
}
Value::F64(arc[0] as f64)
}
Value::VecI32(arc) => {
if arc.len() != 1 {
panic!(
"exactly_one_value: expected unary structure \
(1 row × 1 column), found vec_i32 of length {}",
arc.len()
);
}
Value::U64(arc[0] as u64)
}
Value::None => panic!(
"exactly_one_value: empty body (Value::None); the upstream \
op produced no result to unwrap"
),
Value::Json(j) => unwrap_unary_json(j),
Value::Bytes(_) | Value::Ext(_) | Value::Handle(_) => body.clone(),
};
}
}
fn unwrap_unary_json(j: &serde_json::Value) -> Value {
use serde_json::Value as J;
let row = match j {
J::Array(arr) => match arr.len() {
0 => panic!(
"exactly_one_value: expected unary structure (1 row × 1 column), \
found 0 rows"
),
1 => &arr[0],
n => panic!(
"exactly_one_value: expected unary structure (1 row × 1 column), \
found {n} rows"
),
},
other => other,
};
let leaf = match row {
J::Object(obj) => match obj.len() {
0 => panic!(
"exactly_one_value: expected unary structure (1 row × 1 column), \
found 1 row × 0 columns"
),
1 => obj.values().next().expect("len==1"),
n => panic!(
"exactly_one_value: expected unary structure (1 row × 1 column), \
found 1 row × {n} columns"
),
},
other => other,
};
match leaf {
J::String(s) => Value::Str(s.as_str().into()),
J::Bool(b) => Value::Bool(*b),
J::Number(n) => {
if let Some(u) = n.as_u64() {
Value::U64(u)
} else if let Some(f) = n.as_f64() {
Value::F64(f)
} else {
panic!("exactly_one_value: numeric leaf is not representable as u64 or f64: {n}")
}
}
J::Null => panic!(
"exactly_one_value: leaf cell is null; expected a non-null value"
),
J::Array(_) | J::Object(_) => panic!(
"exactly_one_value: leaf cell is itself structural ({}); \
expected a scalar (string, number, or boolean)",
describe_json_kind(leaf)
),
}
}
fn describe_json_kind(j: &serde_json::Value) -> &'static str {
use serde_json::Value as J;
match j {
J::Null => "null",
J::Bool(_) => "bool",
J::Number(_) => "number",
J::String(_) => "string",
J::Array(_) => "array",
J::Object(_) => "object",
}
}
use crate::dsl::registry::{Arity, FuncCategory, FuncSig, ParamSpec};
use crate::node::SlotType;
pub fn signatures() -> &'static [FuncSig] {
use FuncCategory as C;
&[FuncSig {
name: "exactly_one_value",
category: C::Diagnostic,
outputs: 1,
description: "assert body has unary structure (1 row × 1 column) and return its single cell",
help: "exactly_one_value(body) -> V\n\
\n\
Inspect a structural body and return its single cell value;\n\
panic with a shape diagnostic if the body has zero or\n\
multiple rows / columns. Use to assertively unwrap a unary\n\
result (e.g. CQL `describe keyspace`) before applying a\n\
regex or other scalar predicate, instead of relying on an\n\
implicit modal projection. Push 1 supports scalar carriers\n\
and length-1 typed vectors; Push 2 extends to structural\n\
row × column bodies.\n\
\n\
Example:\n \
has_sai := regex_match(exactly_one_value(body),\n \
\"^TABLE\\\\s+system_views\\\\.sai\")",
identity: None,
variadic_ctor: None,
params: &[ParamSpec {
name: "body",
slot_type: SlotType::Wire,
required: true,
example: "body",
constraint: None,
}],
arity: Arity::Fixed,
commutativity: crate::node::Commutativity::Positional,
default_resolver: None,
output_type: crate::dsl::registry::OutputType::Fixed,
}]
}
pub(crate) fn build_node(
name: &str,
_wires: &[crate::assembly::WireRef], _wire_types: &[crate::node::PortType],
_consts: &[crate::dsl::factory::ConstArg],
) -> Option<Result<Box<dyn crate::node::GkNode>, String>> {
match name {
"exactly_one_value" => Some(Ok(Box::new(ExactlyOneValue::new()))),
_ => None,
}
}
crate::register_nodes!(signatures, build_node);
#[cfg(test)]
mod tests {
use super::*;
fn run(input: Value) -> Value {
let node = ExactlyOneValue::new();
let mut out = [Value::None];
node.eval(&[input], &mut out);
out.into_iter().next().unwrap()
}
#[test]
fn passes_through_str() {
let v = run(Value::Str("hello".into()));
assert_eq!(v.as_str(), "hello");
}
#[test]
fn passes_through_bool() {
let v = run(Value::Bool(true));
assert!(v.as_bool());
}
#[test]
fn passes_through_u64() {
let v = run(Value::U64(42));
assert_eq!(v.as_u64(), 42);
}
#[test]
fn passes_through_f64() {
let v = run(Value::F64(2.5));
assert_eq!(v.as_f64(), 2.5);
}
#[test]
fn unwraps_singleton_vec_f32() {
let v = Value::VecF32(crate::node::SliceArc::from_vec(vec![1.5_f32]));
let out = run(v);
assert_eq!(out.as_f64(), 1.5);
}
#[test]
#[should_panic(expected = "expected unary structure")]
fn rejects_multi_element_vec_f32() {
let v = Value::VecF32(crate::node::SliceArc::from_vec(vec![1.0_f32, 2.0]));
run(v);
}
#[test]
#[should_panic(expected = "exactly_one_value: empty body")]
fn rejects_none() {
run(Value::None);
}
#[test]
fn unwraps_unary_json_describe_keyspace_shape() {
let j = serde_json::json!([
{"create_statement": "VIRTUAL TABLE system_views.sai_column_indexes (\n ...\n)"}
]);
let out = run(Value::Json(std::sync::Arc::new(j)));
let s = out.as_str();
assert!(s.starts_with("VIRTUAL TABLE"), "got: {s:?}");
}
#[test]
fn unwraps_unary_json_string_leaf() {
let j = serde_json::json!([{"value": "hello"}]);
let out = run(Value::Json(std::sync::Arc::new(j)));
assert_eq!(out.as_str(), "hello");
}
#[test]
fn unwraps_unary_json_numeric_leaf() {
let j = serde_json::json!([{"n": 42}]);
let out = run(Value::Json(std::sync::Arc::new(j)));
assert_eq!(out.as_u64(), 42);
}
#[test]
fn unwraps_unary_json_bool_leaf() {
let j = serde_json::json!([{"b": true}]);
let out = run(Value::Json(std::sync::Arc::new(j)));
assert!(out.as_bool());
}
#[test]
#[should_panic(expected = "found 0 rows")]
fn rejects_empty_json_array() {
run(Value::Json(std::sync::Arc::new(serde_json::json!([]))));
}
#[test]
#[should_panic(expected = "found 2 rows")]
fn rejects_multi_row_json() {
let j = serde_json::json!([{"a": 1}, {"a": 2}]);
run(Value::Json(std::sync::Arc::new(j)));
}
#[test]
#[should_panic(expected = "found 1 row × 2 columns")]
fn rejects_multi_column_json() {
let j = serde_json::json!([{"a": 1, "b": 2}]);
run(Value::Json(std::sync::Arc::new(j)));
}
#[test]
#[should_panic(expected = "leaf cell is null")]
fn rejects_json_null_leaf() {
let j = serde_json::json!([{"a": null}]);
run(Value::Json(std::sync::Arc::new(j)));
}
#[test]
#[should_panic(expected = "leaf cell is itself structural")]
fn rejects_json_nested_structural_leaf() {
let j = serde_json::json!([{"a": {"nested": 1}}]);
run(Value::Json(std::sync::Arc::new(j)));
}
}