polydat_core/library/
exactly_one.rs1use crate::ast::Value;
25
26#[crate::polydat_node(category = Diagnostic)]
43fn exactly_one_value(body: Value) -> String {
44 if crate::library::debug_nodes_enabled() {
45 let body_disp = body.to_display_string();
49 let snippet: String = body_disp.chars().take(400).collect();
50 let ellipsis = if body_disp.len() > snippet.len() {
51 "…"
52 } else {
53 ""
54 };
55 eprintln!(
56 "[DEBUG] exactly_one_value: body.variant={:?} body.len={} snippet={}{ellipsis}",
57 body.port_type(),
58 body_disp.len(),
59 snippet,
60 );
61 }
62 let leaf_value: Value = match &body {
63 Value::Str(_)
67 | Value::Bool(_)
68 | Value::U64(_)
69 | Value::I64(_)
70 | Value::U128(_)
71 | Value::I128(_)
72 | Value::F64(_) => body.clone(),
73
74 Value::VecF32(arc) => {
77 if arc.len() != 1 {
78 panic!(
79 "exactly_one_value: expected unary structure \
80 (1 row × 1 column), found vec_f32 of length {}",
81 arc.len()
82 );
83 }
84 Value::F64(arc[0] as f64)
85 }
86 Value::VecI32(arc) => {
87 if arc.len() != 1 {
88 panic!(
89 "exactly_one_value: expected unary structure \
90 (1 row × 1 column), found vec_i32 of length {}",
91 arc.len()
92 );
93 }
94 Value::I64(arc[0] as i64)
95 }
96 Value::VecF64(arc) => {
97 if arc.len() != 1 {
98 panic!(
99 "exactly_one_value: expected unary structure \
100 (1 row × 1 column), found vec_f64 of length {}",
101 arc.len()
102 );
103 }
104 Value::F64(arc[0])
105 }
106 Value::VecI64(arc) => {
107 if arc.len() != 1 {
108 panic!(
109 "exactly_one_value: expected unary structure \
110 (1 row × 1 column), found vec_i64 of length {}",
111 arc.len()
112 );
113 }
114 Value::I64(arc[0])
115 }
116 Value::VecF16(arc) => {
117 if arc.len() != 1 {
118 panic!(
119 "exactly_one_value: expected unary structure \
120 (1 row × 1 column), found vec_f16 of length {}",
121 arc.len()
122 );
123 }
124 Value::F64(arc[0].to_f32() as f64)
125 }
126 Value::VecI16(arc) => {
127 if arc.len() != 1 {
128 panic!(
129 "exactly_one_value: expected unary structure \
130 (1 row × 1 column), found vec_i16 of length {}",
131 arc.len()
132 );
133 }
134 Value::I64(arc[0] as i64)
135 }
136 Value::VecI8(arc) => {
137 if arc.len() != 1 {
138 panic!(
139 "exactly_one_value: expected unary structure \
140 (1 row × 1 column), found vec_i8 of length {}",
141 arc.len()
142 );
143 }
144 Value::I64(arc[0] as i64)
145 }
146
147 Value::None => panic!(
148 "exactly_one_value: empty body (Value::None); the upstream \
149 op produced no result to unwrap"
150 ),
151
152 Value::Json(j) => unwrap_unary_json(j),
155
156 Value::Bytes(_) | Value::Ext(_) | Value::Handle(_) | Value::Reg128(_, _) => body.clone(),
159 };
160 match leaf_value {
164 Value::Str(s) => s.to_string(),
165 other => other.to_display_string(),
166 }
167}
168
169fn unwrap_unary_json(j: &serde_json::Value) -> Value {
175 use serde_json::Value as J;
176 let row = match j {
178 J::Array(arr) => match arr.len() {
179 0 => panic!(
180 "exactly_one_value: expected unary structure (1 row × 1 column), \
181 found 0 rows"
182 ),
183 1 => &arr[0],
184 n => panic!(
185 "exactly_one_value: expected unary structure (1 row × 1 column), \
186 found {n} rows"
187 ),
188 },
189 other => other,
194 };
195 let leaf = match row {
197 J::Object(obj) => match obj.len() {
198 0 => panic!(
199 "exactly_one_value: expected unary structure (1 row × 1 column), \
200 found 1 row × 0 columns"
201 ),
202 1 => obj.values().next().expect("len==1"),
203 n => panic!(
204 "exactly_one_value: expected unary structure (1 row × 1 column), \
205 found 1 row × {n} columns"
206 ),
207 },
208 other => other,
212 };
213 match leaf {
214 J::String(s) => Value::Str(s.as_str().into()),
215 J::Bool(b) => Value::Bool(*b),
216 J::Number(n) => {
221 if let Some(u) = n.as_u64() {
222 Value::U64(u)
223 } else if let Some(i) = n.as_i64() {
224 Value::I64(i)
225 } else if let Some(f) = n.as_f64() {
226 Value::F64(f)
227 } else {
228 panic!(
229 "exactly_one_value: numeric leaf is not representable as u64, i64, or f64: {n}"
230 )
231 }
232 }
233 J::Null => panic!("exactly_one_value: leaf cell is null; expected a non-null value"),
234 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}