1use std::collections::HashMap;
6
7use thiserror::Error;
8
9#[derive(Debug, Clone, PartialEq, Eq, Error)]
13pub enum ValuesError {
14 #[error("column count mismatch: expected {expected}, got {got}")]
16 ColumnCountMismatch { expected: usize, got: usize },
17}
18
19#[derive(Debug, Clone, PartialEq, Eq)]
23pub struct ValuesRow(pub Vec<Option<String>>);
24
25impl ValuesRow {
26 pub fn new(values: Vec<Option<String>>) -> Self {
28 Self(values)
29 }
30
31 pub fn get(&self, idx: usize) -> Option<&Option<String>> {
33 self.0.get(idx)
34 }
35
36 pub fn len(&self) -> usize {
38 self.0.len()
39 }
40
41 pub fn is_empty(&self) -> bool {
43 self.0.is_empty()
44 }
45
46 pub fn is_undef(&self, idx: usize) -> bool {
48 matches!(self.0.get(idx), Some(None))
49 }
50}
51
52#[derive(Debug, Clone, PartialEq, Eq)]
54pub struct BindingResult {
55 pub var: String,
57 pub value: Option<String>,
59}
60
61#[derive(Debug, Clone, PartialEq, Eq)]
63pub struct ValuesExpansion {
64 pub rows: Vec<Vec<BindingResult>>,
66}
67
68#[derive(Debug, Clone, PartialEq, Eq)]
79pub struct ValuesClause {
80 pub variables: Vec<String>,
82 pub rows: Vec<ValuesRow>,
84}
85
86impl ValuesClause {
87 pub fn new(variables: Vec<String>) -> Self {
89 Self {
90 variables,
91 rows: Vec::new(),
92 }
93 }
94
95 pub fn add_row(&mut self, row: ValuesRow) -> Result<(), ValuesError> {
97 let expected = self.variables.len();
98 let got = row.len();
99 if got != expected {
100 return Err(ValuesError::ColumnCountMismatch { expected, got });
101 }
102 self.rows.push(row);
103 Ok(())
104 }
105
106 pub fn expand(&self) -> ValuesExpansion {
108 let rows = self
109 .rows
110 .iter()
111 .map(|row| {
112 self.variables
113 .iter()
114 .enumerate()
115 .map(|(i, var)| BindingResult {
116 var: var.clone(),
117 value: row.0.get(i).and_then(|v| v.clone()),
118 })
119 .collect()
120 })
121 .collect();
122 ValuesExpansion { rows }
123 }
124
125 pub fn join_with(&self, bindings: &[HashMap<String, String>]) -> Vec<HashMap<String, String>> {
129 if bindings.is_empty() {
130 return self.expand_as_maps();
132 }
133 let mut out = Vec::new();
134 for binding in bindings {
135 for row in &self.rows {
136 if let Some(merged) = self.try_merge(binding, row) {
137 out.push(merged);
138 }
139 }
140 }
141 out
142 }
143
144 pub fn row_count(&self) -> usize {
146 self.rows.len()
147 }
148
149 pub fn variable_count(&self) -> usize {
151 self.variables.len()
152 }
153
154 pub fn is_empty(&self) -> bool {
156 self.rows.is_empty()
157 }
158
159 pub fn filter_undef(&self) -> Self {
161 let rows = self
162 .rows
163 .iter()
164 .filter(|row| row.0.iter().any(|v| v.is_some()))
165 .cloned()
166 .collect();
167 Self {
168 variables: self.variables.clone(),
169 rows,
170 }
171 }
172
173 pub fn project(&self, vars: &[&str]) -> Self {
176 let indices: Vec<usize> = vars
178 .iter()
179 .filter_map(|v| self.variables.iter().position(|x| x == v))
180 .collect();
181 let new_variables: Vec<String> =
182 indices.iter().map(|&i| self.variables[i].clone()).collect();
183 let rows = self
184 .rows
185 .iter()
186 .map(|row| {
187 let values = indices
188 .iter()
189 .map(|&i| row.0.get(i).and_then(|v| v.clone()))
190 .collect();
191 ValuesRow(values)
192 })
193 .collect();
194 Self {
195 variables: new_variables,
196 rows,
197 }
198 }
199
200 fn expand_as_maps(&self) -> Vec<HashMap<String, String>> {
204 self.rows
205 .iter()
206 .map(|row| {
207 self.variables
208 .iter()
209 .enumerate()
210 .filter_map(|(i, var)| {
211 row.0
212 .get(i)
213 .and_then(|v| v.as_ref())
214 .map(|v| (var.clone(), v.clone()))
215 })
216 .collect()
217 })
218 .collect()
219 }
220
221 fn try_merge(
226 &self,
227 binding: &HashMap<String, String>,
228 row: &ValuesRow,
229 ) -> Option<HashMap<String, String>> {
230 let mut merged = binding.clone();
231 for (i, var) in self.variables.iter().enumerate() {
232 if let Some(Some(val)) = row.0.get(i) {
233 if let Some(existing) = merged.get(var) {
234 if existing != val {
235 return None; }
237 } else {
238 merged.insert(var.clone(), val.clone());
239 }
240 }
241 }
242 Some(merged)
243 }
244}
245
246#[cfg(test)]
249mod tests {
250 use super::*;
251
252 fn make_row(values: &[Option<&str>]) -> ValuesRow {
253 ValuesRow::new(values.iter().map(|v| v.map(String::from)).collect())
254 }
255
256 #[test]
259 fn test_row_new_and_len() {
260 let r = make_row(&[Some("a"), None, Some("c")]);
261 assert_eq!(r.len(), 3);
262 }
263
264 #[test]
265 fn test_row_is_empty_false() {
266 let r = make_row(&[Some("x")]);
267 assert!(!r.is_empty());
268 }
269
270 #[test]
271 fn test_row_is_empty_true() {
272 let r = ValuesRow::new(vec![]);
273 assert!(r.is_empty());
274 }
275
276 #[test]
277 fn test_row_get_some() {
278 let r = make_row(&[Some("hello"), None]);
279 assert_eq!(r.get(0), Some(&Some("hello".to_string())));
280 }
281
282 #[test]
283 fn test_row_get_none_value() {
284 let r = make_row(&[None]);
285 assert_eq!(r.get(0), Some(&None));
286 }
287
288 #[test]
289 fn test_row_get_out_of_range() {
290 let r = make_row(&[Some("x")]);
291 assert_eq!(r.get(5), None);
292 }
293
294 #[test]
295 fn test_row_is_undef_true() {
296 let r = make_row(&[Some("v"), None]);
297 assert!(r.is_undef(1));
298 }
299
300 #[test]
301 fn test_row_is_undef_false() {
302 let r = make_row(&[Some("v"), None]);
303 assert!(!r.is_undef(0));
304 }
305
306 #[test]
307 fn test_row_is_undef_out_of_range() {
308 let r = make_row(&[Some("v")]);
309 assert!(!r.is_undef(99));
310 }
311
312 #[test]
315 fn test_new_empty_clause() {
316 let vc = ValuesClause::new(vec!["x".into()]);
317 assert_eq!(vc.variable_count(), 1);
318 assert!(vc.is_empty());
319 }
320
321 #[test]
322 fn test_add_row_success() {
323 let mut vc = ValuesClause::new(vec!["x".into(), "y".into()]);
324 assert!(vc.add_row(make_row(&[Some("1"), Some("2")])).is_ok());
325 assert_eq!(vc.row_count(), 1);
326 }
327
328 #[test]
329 fn test_add_row_column_mismatch_too_few() {
330 let mut vc = ValuesClause::new(vec!["x".into(), "y".into()]);
331 let err = vc.add_row(make_row(&[Some("1")])).unwrap_err();
332 assert!(matches!(
333 err,
334 ValuesError::ColumnCountMismatch {
335 expected: 2,
336 got: 1
337 }
338 ));
339 }
340
341 #[test]
342 fn test_add_row_column_mismatch_too_many() {
343 let mut vc = ValuesClause::new(vec!["x".into()]);
344 let err = vc.add_row(make_row(&[Some("1"), Some("2")])).unwrap_err();
345 assert!(matches!(
346 err,
347 ValuesError::ColumnCountMismatch {
348 expected: 1,
349 got: 2
350 }
351 ));
352 }
353
354 #[test]
355 fn test_add_multiple_rows() {
356 let mut vc = ValuesClause::new(vec!["x".into()]);
357 vc.add_row(make_row(&[Some("a")])).unwrap();
358 vc.add_row(make_row(&[Some("b")])).unwrap();
359 vc.add_row(make_row(&[None])).unwrap();
360 assert_eq!(vc.row_count(), 3);
361 }
362
363 #[test]
364 fn test_variable_count() {
365 let vc = ValuesClause::new(vec!["a".into(), "b".into(), "c".into()]);
366 assert_eq!(vc.variable_count(), 3);
367 }
368
369 #[test]
370 fn test_row_count_zero() {
371 let vc = ValuesClause::new(vec!["x".into()]);
372 assert_eq!(vc.row_count(), 0);
373 }
374
375 #[test]
378 fn test_expand_single_variable() {
379 let mut vc = ValuesClause::new(vec!["x".into()]);
380 vc.add_row(make_row(&[Some("hello")])).unwrap();
381 let exp = vc.expand();
382 assert_eq!(exp.rows.len(), 1);
383 assert_eq!(exp.rows[0][0].var, "x");
384 assert_eq!(exp.rows[0][0].value, Some("hello".to_string()));
385 }
386
387 #[test]
388 fn test_expand_multiple_variables() {
389 let mut vc = ValuesClause::new(vec!["x".into(), "y".into()]);
390 vc.add_row(make_row(&[Some("a"), Some("b")])).unwrap();
391 let exp = vc.expand();
392 assert_eq!(exp.rows[0][0].var, "x");
393 assert_eq!(exp.rows[0][1].var, "y");
394 assert_eq!(exp.rows[0][1].value, Some("b".to_string()));
395 }
396
397 #[test]
398 fn test_expand_undef_value() {
399 let mut vc = ValuesClause::new(vec!["x".into(), "y".into()]);
400 vc.add_row(make_row(&[Some("a"), None])).unwrap();
401 let exp = vc.expand();
402 assert_eq!(exp.rows[0][1].value, None);
403 }
404
405 #[test]
406 fn test_expand_multiple_rows() {
407 let mut vc = ValuesClause::new(vec!["x".into()]);
408 vc.add_row(make_row(&[Some("1")])).unwrap();
409 vc.add_row(make_row(&[Some("2")])).unwrap();
410 let exp = vc.expand();
411 assert_eq!(exp.rows.len(), 2);
412 assert_eq!(exp.rows[1][0].value, Some("2".to_string()));
413 }
414
415 #[test]
416 fn test_expand_empty_clause() {
417 let vc = ValuesClause::new(vec!["x".into()]);
418 let exp = vc.expand();
419 assert!(exp.rows.is_empty());
420 }
421
422 #[test]
425 fn test_join_with_empty_bindings_returns_values_rows() {
426 let mut vc = ValuesClause::new(vec!["x".into()]);
427 vc.add_row(make_row(&[Some("1")])).unwrap();
428 vc.add_row(make_row(&[Some("2")])).unwrap();
429 let result = vc.join_with(&[]);
430 assert_eq!(result.len(), 2);
431 }
432
433 #[test]
434 fn test_join_with_compatible_bindings() {
435 let mut vc = ValuesClause::new(vec!["y".into()]);
436 vc.add_row(make_row(&[Some("b")])).unwrap();
437
438 let bindings = vec![{
439 let mut m = HashMap::new();
440 m.insert("x".to_string(), "a".to_string());
441 m
442 }];
443
444 let result = vc.join_with(&bindings);
445 assert_eq!(result.len(), 1);
446 assert_eq!(result[0].get("x").map(String::as_str), Some("a"));
447 assert_eq!(result[0].get("y").map(String::as_str), Some("b"));
448 }
449
450 #[test]
451 fn test_join_with_conflict_drops_row() {
452 let mut vc = ValuesClause::new(vec!["x".into()]);
453 vc.add_row(make_row(&[Some("wrong")])).unwrap();
454
455 let bindings = vec![{
456 let mut m = HashMap::new();
457 m.insert("x".to_string(), "correct".to_string());
458 m
459 }];
460
461 let result = vc.join_with(&bindings);
462 assert!(result.is_empty());
463 }
464
465 #[test]
466 fn test_join_with_undef_is_compatible() {
467 let mut vc = ValuesClause::new(vec!["x".into()]);
468 vc.add_row(make_row(&[None])).unwrap(); let bindings = vec![{
471 let mut m = HashMap::new();
472 m.insert("x".to_string(), "anything".to_string());
473 m
474 }];
475
476 let result = vc.join_with(&bindings);
477 assert_eq!(result.len(), 1);
478 assert_eq!(result[0].get("x").map(String::as_str), Some("anything"));
479 }
480
481 #[test]
482 fn test_join_with_same_value_no_conflict() {
483 let mut vc = ValuesClause::new(vec!["x".into()]);
484 vc.add_row(make_row(&[Some("same")])).unwrap();
485
486 let bindings = vec![{
487 let mut m = HashMap::new();
488 m.insert("x".to_string(), "same".to_string());
489 m
490 }];
491
492 let result = vc.join_with(&bindings);
493 assert_eq!(result.len(), 1);
494 }
495
496 #[test]
497 fn test_join_cross_product_multiple_rows_multiple_bindings() {
498 let mut vc = ValuesClause::new(vec!["y".into()]);
499 vc.add_row(make_row(&[Some("1")])).unwrap();
500 vc.add_row(make_row(&[Some("2")])).unwrap();
501
502 let bindings: Vec<HashMap<String, String>> = vec![
503 {
504 let mut m = HashMap::new();
505 m.insert("x".to_string(), "a".to_string());
506 m
507 },
508 {
509 let mut m = HashMap::new();
510 m.insert("x".to_string(), "b".to_string());
511 m
512 },
513 ];
514
515 let result = vc.join_with(&bindings);
516 assert_eq!(result.len(), 4); }
518
519 #[test]
522 fn test_filter_undef_removes_all_undef_row() {
523 let mut vc = ValuesClause::new(vec!["x".into(), "y".into()]);
524 vc.add_row(make_row(&[Some("a"), Some("b")])).unwrap();
525 vc.add_row(make_row(&[None, None])).unwrap();
526 let filtered = vc.filter_undef();
527 assert_eq!(filtered.row_count(), 1);
528 }
529
530 #[test]
531 fn test_filter_undef_keeps_partial_undef() {
532 let mut vc = ValuesClause::new(vec!["x".into(), "y".into()]);
533 vc.add_row(make_row(&[Some("a"), None])).unwrap(); let filtered = vc.filter_undef();
535 assert_eq!(filtered.row_count(), 1);
536 }
537
538 #[test]
539 fn test_filter_undef_empty_clause() {
540 let vc = ValuesClause::new(vec!["x".into()]);
541 let filtered = vc.filter_undef();
542 assert!(filtered.is_empty());
543 }
544
545 #[test]
548 fn test_project_single_variable() {
549 let mut vc = ValuesClause::new(vec!["x".into(), "y".into()]);
550 vc.add_row(make_row(&[Some("a"), Some("b")])).unwrap();
551 let projected = vc.project(&["x"]);
552 assert_eq!(projected.variable_count(), 1);
553 assert_eq!(projected.variables[0], "x");
554 assert_eq!(projected.rows[0].0[0], Some("a".to_string()));
555 }
556
557 #[test]
558 fn test_project_all_variables() {
559 let mut vc = ValuesClause::new(vec!["x".into(), "y".into()]);
560 vc.add_row(make_row(&[Some("a"), Some("b")])).unwrap();
561 let projected = vc.project(&["x", "y"]);
562 assert_eq!(projected.variable_count(), 2);
563 }
564
565 #[test]
566 fn test_project_reorder_variables() {
567 let mut vc = ValuesClause::new(vec!["x".into(), "y".into(), "z".into()]);
568 vc.add_row(make_row(&[Some("a"), Some("b"), Some("c")]))
569 .unwrap();
570 let projected = vc.project(&["z", "x"]);
571 assert_eq!(projected.variables[0], "z");
572 assert_eq!(projected.variables[1], "x");
573 assert_eq!(projected.rows[0].0[0], Some("c".to_string()));
574 assert_eq!(projected.rows[0].0[1], Some("a".to_string()));
575 }
576
577 #[test]
578 fn test_project_unknown_variable_omitted() {
579 let mut vc = ValuesClause::new(vec!["x".into()]);
580 vc.add_row(make_row(&[Some("a")])).unwrap();
581 let projected = vc.project(&["unknown"]);
582 assert_eq!(projected.variable_count(), 0);
583 }
584
585 #[test]
586 fn test_project_preserves_undef() {
587 let mut vc = ValuesClause::new(vec!["x".into(), "y".into()]);
588 vc.add_row(make_row(&[Some("a"), None])).unwrap();
589 let projected = vc.project(&["y"]);
590 assert_eq!(projected.rows[0].0[0], None);
591 }
592
593 #[test]
596 fn test_values_error_display() {
597 let e = ValuesError::ColumnCountMismatch {
598 expected: 3,
599 got: 1,
600 };
601 let msg = e.to_string();
602 assert!(msg.contains("3"));
603 assert!(msg.contains("1"));
604 }
605
606 #[test]
607 fn test_single_variable_full_cycle() {
608 let mut vc = ValuesClause::new(vec!["name".into()]);
609 vc.add_row(make_row(&[Some("Alice")])).unwrap();
610 vc.add_row(make_row(&[Some("Bob")])).unwrap();
611 vc.add_row(make_row(&[None])).unwrap(); assert_eq!(vc.row_count(), 3);
614 let exp = vc.expand();
615 assert_eq!(exp.rows[2][0].value, None);
616
617 let filtered = vc.filter_undef();
618 assert_eq!(filtered.row_count(), 2);
619
620 let projected = vc.project(&["name"]);
621 assert_eq!(projected.variable_count(), 1);
622 }
623
624 #[test]
625 fn test_join_with_no_variable_overlap_full_cross_product() {
626 let mut vc = ValuesClause::new(vec!["b".into()]);
627 vc.add_row(make_row(&[Some("x")])).unwrap();
628 vc.add_row(make_row(&[Some("y")])).unwrap();
629
630 let bindings = vec![{
631 let mut m = HashMap::new();
632 m.insert("a".to_string(), "1".to_string());
633 m
634 }];
635
636 let result = vc.join_with(&bindings);
637 assert_eq!(result.len(), 2);
639 for r in &result {
640 assert!(r.contains_key("a"));
641 assert!(r.contains_key("b"));
642 }
643 }
644
645 #[test]
646 fn test_all_undef_row_filtered() {
647 let mut vc = ValuesClause::new(vec!["x".into()]);
648 vc.add_row(make_row(&[None])).unwrap();
649 let filtered = vc.filter_undef();
650 assert!(filtered.is_empty());
651 }
652}