reifydb_evaluate/expression/
branch.rs1use reifydb_core::value::column::{arith::is_family, view::group_by::common_key_type};
5use reifydb_value::{
6 error::{RuntimeErrorKind, TypeError},
7 fragment::Fragment,
8 value::value_type::ValueType,
9};
10
11use crate::Result;
12
13pub struct BranchLayout {
14 types: Vec<ValueType>,
15 open: Vec<bool>,
16 described: Vec<String>,
17}
18
19impl BranchLayout {
20 pub fn new<'a>(columns: impl IntoIterator<Item = (&'a str, ValueType, bool)>) -> Self {
21 let mut layout = Self {
22 types: Vec::new(),
23 open: Vec::new(),
24 described: Vec::new(),
25 };
26 for (name, ty, fits_any) in columns {
27 layout.described.push(describe(name, &ty));
28 layout.types.push(ty.inner_type().clone());
29 layout.open.push(fits_any);
30 }
31 layout
32 }
33
34 pub fn types(&self) -> &[ValueType] {
35 &self.types
36 }
37
38 pub fn admit<'a>(
39 &mut self,
40 columns: impl IntoIterator<Item = (&'a str, ValueType, bool)>,
41 fragment: &Fragment,
42 ) -> Result<()> {
43 let incoming: Vec<(&str, ValueType, bool)> = columns.into_iter().collect();
44 let mut disagrees = incoming.len() != self.types.len();
45 if !disagrees {
46 for ((slot, open), (_, ty, fits_any)) in
47 self.types.iter_mut().zip(self.open.iter_mut()).zip(incoming.iter())
48 {
49 if *fits_any {
50 continue;
51 }
52 let ty = ty.inner_type();
53 if *open {
54 *slot = ty.clone();
55 *open = false;
56 } else if ty != slot {
57 match common_key_type(slot, ty) {
58 Some(common) => *slot = common,
59 None if (is_family(slot) && ty.is_floating_point())
60 || (slot.is_floating_point() && is_family(ty)) =>
61 {
62 *slot = ValueType::Float8
63 }
64 None => {
65 disagrees = true;
66 break;
67 }
68 }
69 }
70 }
71 }
72
73 if disagrees {
74 return Err(TypeError::Runtime {
75 kind: RuntimeErrorKind::ConditionalBranchMismatch {
76 expected: self.described.clone(),
77 actual: incoming.iter().map(|(name, ty, _)| describe(name, ty)).collect(),
78 fragment: fragment.clone(),
79 },
80 message: "conditional branches produce different columns".to_string(),
81 }
82 .into());
83 }
84 Ok(())
85 }
86}
87
88fn describe(name: &str, ty: &ValueType) -> String {
89 format!("{}: {}", name, ty)
90}