polydat_core/library/
exactly_one.rs1use crate::ast::Value;
24
25#[crate::polydat_node(category = Diagnostic)]
42fn exactly_one_value(body: Value) -> String {
43 if crate::library::debug_nodes_enabled() {
44 let body_disp = body.to_display_string();
48 let snippet: String = body_disp.chars().take(400).collect();
49 let ellipsis = if body_disp.len() > snippet.len() {
50 "…"
51 } else {
52 ""
53 };
54 eprintln!(
55 "[DEBUG] exactly_one_value: body.variant={:?} body.len={} snippet={}{ellipsis}",
56 body.port_type(),
57 body_disp.len(),
58 snippet,
59 );
60 }
61 let leaf_value: Value = match &body {
62 Value::Str(_)
66 | Value::Bool(_)
67 | Value::U64(_)
68 | Value::I64(_)
69 | Value::U128(_)
70 | Value::I128(_)
71 | Value::F64(_) => body.clone(),
72
73 Value::VecF32(arc) => {
76 if arc.len() != 1 {
77 panic!(
78 "exactly_one_value: expected unary structure \
79 (1 row × 1 column), found vec_f32 of length {}",
80 arc.len()
81 );
82 }
83 Value::F64(arc[0] as f64)
84 }
85 Value::VecI32(arc) => {
86 if arc.len() != 1 {
87 panic!(
88 "exactly_one_value: expected unary structure \
89 (1 row × 1 column), found vec_i32 of length {}",
90 arc.len()
91 );
92 }
93 Value::I64(arc[0] as i64)
94 }
95 Value::VecF64(arc) => {
96 if arc.len() != 1 {
97 panic!(
98 "exactly_one_value: expected unary structure \
99 (1 row × 1 column), found vec_f64 of length {}",
100 arc.len()
101 );
102 }
103 Value::F64(arc[0])
104 }
105 Value::VecI64(arc) => {
106 if arc.len() != 1 {
107 panic!(
108 "exactly_one_value: expected unary structure \
109 (1 row × 1 column), found vec_i64 of length {}",
110 arc.len()
111 );
112 }
113 Value::I64(arc[0])
114 }
115 Value::VecF16(arc) => {
116 if arc.len() != 1 {
117 panic!(
118 "exactly_one_value: expected unary structure \
119 (1 row × 1 column), found vec_f16 of length {}",
120 arc.len()
121 );
122 }
123 Value::F64(arc[0].to_f32() as f64)
124 }
125 Value::VecI16(arc) => {
126 if arc.len() != 1 {
127 panic!(
128 "exactly_one_value: expected unary structure \
129 (1 row × 1 column), found vec_i16 of length {}",
130 arc.len()
131 );
132 }
133 Value::I64(arc[0] as i64)
134 }
135 Value::VecI8(arc) => {
136 if arc.len() != 1 {
137 panic!(
138 "exactly_one_value: expected unary structure \
139 (1 row × 1 column), found vec_i8 of length {}",
140 arc.len()
141 );
142 }
143 Value::I64(arc[0] as i64)
144 }
145
146 Value::None => panic!(
147 "exactly_one_value: empty body (Value::None); the upstream \
148 op produced no result to unwrap"
149 ),
150
151 Value::Json(j) => unwrap_unary_json(j),
154
155 Value::Bytes(_) | Value::Ext(_) | Value::Handle(_) | Value::Reg128(_, _) => body.clone(),
158 };
159 match leaf_value {
163 Value::Str(s) => s.to_string(),
164 other => other.to_display_string(),
165 }
166}
167
168fn unwrap_unary_json(j: &serde_json::Value) -> Value {
174 use serde_json::Value as J;
175 let row = match j {
177 J::Array(arr) => match arr.len() {
178 0 => panic!(
179 "exactly_one_value: expected unary structure (1 row × 1 column), \
180 found 0 rows"
181 ),
182 1 => &arr[0],
183 n => panic!(
184 "exactly_one_value: expected unary structure (1 row × 1 column), \
185 found {n} rows"
186 ),
187 },
188 other => other,
193 };
194 let leaf = match row {
196 J::Object(obj) => match obj.len() {
197 0 => panic!(
198 "exactly_one_value: expected unary structure (1 row × 1 column), \
199 found 1 row × 0 columns"
200 ),
201 1 => obj.values().next().expect("len==1"),
202 n => panic!(
203 "exactly_one_value: expected unary structure (1 row × 1 column), \
204 found 1 row × {n} columns"
205 ),
206 },
207 other => other,
211 };
212 match leaf {
213 J::String(s) => Value::Str(s.as_str().into()),
214 J::Bool(b) => Value::Bool(*b),
215 J::Number(n) => {
220 if let Some(u) = n.as_u64() {
221 Value::U64(u)
222 } else if let Some(i) = n.as_i64() {
223 Value::I64(i)
224 } else if let Some(f) = n.as_f64() {
225 Value::F64(f)
226 } else {
227 panic!(
228 "exactly_one_value: numeric leaf is not representable as u64, i64, or f64: {n}"
229 )
230 }
231 }
232 J::Null => panic!("exactly_one_value: leaf cell is null; expected a non-null value"),
233 J::Array(_) | J::Object(_) => panic!(
238 "exactly_one_value: leaf cell is itself structural ({}); \
239 expected a scalar (string, number, or boolean)",
240 describe_json_kind(leaf)
241 ),
242 }
243}
244
245fn describe_json_kind(j: &serde_json::Value) -> &'static str {
246 use serde_json::Value as J;
247 match j {
248 J::Null => "null",
249 J::Bool(_) => "bool",
250 J::Number(_) => "number",
251 J::String(_) => "string",
252 J::Array(_) => "array",
253 J::Object(_) => "object",
254 }
255}
256
257#[cfg(test)]
258mod tests {
259 use super::*;
260 use crate::ast::PolydatNode;
261
262 fn run(input: Value) -> Value {
263 let node = ExactlyOneValue::new(input.port_type());
268 let mut out = [Value::None];
269 node.eval(&[input], &mut out);
270 out.into_iter().next().unwrap()
271 }
272
273 #[test]
274 fn passes_through_str() {
275 let v = run(Value::Str("hello".into()));
276 assert_eq!(v.as_str(), "hello");
277 }
278
279 #[test]
280 fn passes_through_bool() {
281 let v = run(Value::Bool(true));
283 assert_eq!(v.as_str(), "true");
284 }
285
286 #[test]
287 fn passes_through_u64() {
288 let v = run(Value::U64(42));
290 assert_eq!(v.as_str(), "42");
291 }
292
293 #[test]
294 fn passes_through_f64() {
295 let v = run(Value::F64(2.5));
296 assert_eq!(v.as_str(), "2.5");
297 }
298
299 #[test]
300 fn unwraps_singleton_vec_f32() {
301 let v = Value::VecF32(crate::ast::SliceArc::from_vec(vec![1.5_f32]));
302 let out = run(v);
303 assert_eq!(out.as_str(), "1.5");
304 }
305
306 #[test]
307 #[should_panic(expected = "expected unary structure")]
308 fn rejects_multi_element_vec_f32() {
309 let v = Value::VecF32(crate::ast::SliceArc::from_vec(vec![1.0_f32, 2.0]));
310 run(v);
311 }
312
313 #[test]
314 #[should_panic(expected = "exactly_one_value: empty body")]
315 fn rejects_none() {
316 run(Value::None);
317 }
318
319 #[test]
324 fn unwraps_unary_json_describe_keyspace_shape() {
325 let j = serde_json::json!([
326 {"create_statement": "VIRTUAL TABLE system_views.sai_column_indexes (\n ...\n)"}
327 ]);
328 let out = run(Value::Json(std::sync::Arc::new(j)));
329 let s = out.as_str();
330 assert!(s.starts_with("VIRTUAL TABLE"), "got: {s:?}");
331 }
332
333 #[test]
334 fn unwraps_unary_json_string_leaf() {
335 let j = serde_json::json!([{"value": "hello"}]);
336 let out = run(Value::Json(std::sync::Arc::new(j)));
337 assert_eq!(out.as_str(), "hello");
338 }
339
340 #[test]
341 fn unwraps_unary_json_numeric_leaf() {
342 let j = serde_json::json!([{"n": 42}]);
343 let out = run(Value::Json(std::sync::Arc::new(j)));
344 assert_eq!(out.as_str(), "42");
345 }
346
347 #[test]
348 fn unwraps_unary_json_bool_leaf() {
349 let j = serde_json::json!([{"b": true}]);
350 let out = run(Value::Json(std::sync::Arc::new(j)));
351 assert_eq!(out.as_str(), "true");
352 }
353
354 #[test]
355 #[should_panic(expected = "found 0 rows")]
356 fn rejects_empty_json_array() {
357 run(Value::Json(std::sync::Arc::new(serde_json::json!([]))));
358 }
359
360 #[test]
361 #[should_panic(expected = "found 2 rows")]
362 fn rejects_multi_row_json() {
363 let j = serde_json::json!([{"a": 1}, {"a": 2}]);
364 run(Value::Json(std::sync::Arc::new(j)));
365 }
366
367 #[test]
368 #[should_panic(expected = "found 1 row × 2 columns")]
369 fn rejects_multi_column_json() {
370 let j = serde_json::json!([{"a": 1, "b": 2}]);
371 run(Value::Json(std::sync::Arc::new(j)));
372 }
373
374 #[test]
375 #[should_panic(expected = "leaf cell is null")]
376 fn rejects_json_null_leaf() {
377 let j = serde_json::json!([{"a": null}]);
378 run(Value::Json(std::sync::Arc::new(j)));
379 }
380
381 #[test]
382 #[should_panic(expected = "leaf cell is itself structural")]
383 fn rejects_json_nested_structural_leaf() {
384 let j = serde_json::json!([{"a": {"nested": 1}}]);
385 run(Value::Json(std::sync::Arc::new(j)));
386 }
387}