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