1use std::fmt::{Display, Formatter};
5
6use crate::expr::Expression;
7
8pub enum DisplayFormat {
9 Compact,
10 Tree,
11}
12
13pub struct DisplayTreeExpr<'a>(pub &'a Expression);
14
15impl Display for DisplayTreeExpr<'_> {
16 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
17 pub use termtree::Tree;
18 fn make_tree(expr: &Expression) -> Result<Tree<String>, std::fmt::Error> {
19 let node_name = format!("{}", ExpressionDebug(expr));
20
21 let child_names = (0..expr.children().len()).map(|i| expr.child_name(i));
23 let children = expr.children();
24
25 let child_trees: Result<Vec<Tree<String>>, _> = children
26 .iter()
27 .zip(child_names)
28 .map(|(child, name)| {
29 let child_tree = make_tree(child)?;
30 Ok(Tree::new(format!("{}: {}", name, child_tree.root))
31 .with_leaves(child_tree.leaves))
32 })
33 .collect();
34
35 Ok(Tree::new(node_name).with_leaves(child_trees?))
36 }
37
38 write!(f, "{}", make_tree(self.0)?)
39 }
40}
41
42struct ExpressionDebug<'a>(&'a Expression);
43impl Display for ExpressionDebug<'_> {
44 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
45 if self.0.data().is::<()>() {
47 return write!(f, "{}", self.0.id().as_ref());
48 }
49 write!(f, "{} ", self.0.id().as_ref())?;
50 self.0.vtable().as_dyn().fmt_data(self.0.data().as_ref(), f)
51 }
52}
53
54#[cfg(test)]
55mod tests {
56 use vortex_dtype::{DType, Nullability, PType};
57
58 use crate::compute::{BetweenOptions, StrictComparison};
59 use crate::expr::exprs::between::between;
60 use crate::expr::exprs::binary::{and, eq, gt};
61 use crate::expr::exprs::cast::cast;
62 use crate::expr::exprs::get_item::get_item;
63 use crate::expr::exprs::literal::lit;
64 use crate::expr::exprs::not::not;
65 use crate::expr::exprs::pack::pack;
66 use crate::expr::exprs::root::root;
67 use crate::expr::exprs::select::{select, select_exclude};
68
69 #[test]
70 fn tree_display_getitem() {
71 let expr = get_item("x", root());
72 println!("{}", expr.display_tree());
73 }
74
75 #[test]
76 fn tree_display_binary() {
77 let expr = gt(get_item("x", root()), lit(5));
78 println!("{}", expr.display_tree());
79 }
80
81 #[test]
82 fn test_child_names_debug() {
83 let binary_expr = gt(get_item("x", root()), lit(10));
85 println!("Binary expr tree:\n{}", binary_expr.display_tree());
86
87 let between_expr = between(
88 get_item("score", root()),
89 lit(0),
90 lit(100),
91 BetweenOptions {
92 lower_strict: StrictComparison::NonStrict,
93 upper_strict: StrictComparison::NonStrict,
94 },
95 );
96 println!("Between expr tree:\n{}", between_expr.display_tree());
97 }
98
99 #[test]
100 fn test_display_tree() {
101 use insta::assert_snapshot;
102
103 let root_expr = root();
104 assert_snapshot!(root_expr.display_tree().to_string(), @"vortex.root");
105
106 let lit_expr = lit(42);
107 assert_snapshot!(lit_expr.display_tree().to_string(), @"vortex.literal 42i32");
108
109 let get_item_expr = get_item("my_field", root());
110 assert_snapshot!(get_item_expr.display_tree().to_string(), @r#"
111 vortex.get_item "my_field"
112 └── input: vortex.root
113 "#);
114
115 let binary_expr = gt(get_item("x", root()), lit(10));
116 assert_snapshot!(binary_expr.display_tree().to_string(), @r#"
117 vortex.binary >
118 ├── lhs: vortex.get_item "x"
119 │ └── input: vortex.root
120 └── rhs: vortex.literal 10i32
121 "#);
122
123 let complex_binary = and(
124 eq(get_item("name", root()), lit("alice")),
125 gt(get_item("age", root()), lit(18)),
126 );
127 assert_snapshot!(complex_binary.display_tree().to_string(), @r#"
128 vortex.binary and
129 ├── lhs: vortex.binary =
130 │ ├── lhs: vortex.get_item "name"
131 │ │ └── input: vortex.root
132 │ └── rhs: vortex.literal "alice"
133 └── rhs: vortex.binary >
134 ├── lhs: vortex.get_item "age"
135 │ └── input: vortex.root
136 └── rhs: vortex.literal 18i32
137 "#);
138
139 let select_expr = select(["name", "age"], root());
140 assert_snapshot!(select_expr.display_tree().to_string(), @r"
141 vortex.select include={name, age}
142 └── child: vortex.root
143 ");
144
145 let select_exclude_expr = select_exclude(["internal_id", "metadata"], root());
146 assert_snapshot!(select_exclude_expr.display_tree().to_string(), @r"
147 vortex.select exclude={internal_id, metadata}
148 └── child: vortex.root
149 ");
150
151 let cast_expr = cast(
152 get_item("value", root()),
153 DType::Primitive(PType::I64, Nullability::NonNullable),
154 );
155 assert_snapshot!(cast_expr.display_tree().to_string(), @r#"
156 vortex.cast i64
157 └── input: vortex.get_item "value"
158 └── input: vortex.root
159 "#);
160
161 let not_expr = not(eq(get_item("active", root()), lit(true)));
162 assert_snapshot!(not_expr.display_tree().to_string(), @r#"
163 vortex.not
164 └── input: vortex.binary =
165 ├── lhs: vortex.get_item "active"
166 │ └── input: vortex.root
167 └── rhs: vortex.literal true
168 "#);
169
170 let between_expr = between(
171 get_item("score", root()),
172 lit(0),
173 lit(100),
174 BetweenOptions {
175 lower_strict: StrictComparison::NonStrict,
176 upper_strict: StrictComparison::NonStrict,
177 },
178 );
179 assert_snapshot!(between_expr.display_tree().to_string(), @r#"
180 vortex.between BetweenOptions { lower_strict: NonStrict, upper_strict: NonStrict }
181 ├── array: vortex.get_item "score"
182 │ └── input: vortex.root
183 ├── lower: vortex.literal 0i32
184 └── upper: vortex.literal 100i32
185 "#);
186
187 let nested_expr = select(
189 ["result"],
190 cast(
191 between(
192 get_item("score", root()),
193 lit(50),
194 lit(100),
195 BetweenOptions {
196 lower_strict: StrictComparison::Strict,
197 upper_strict: StrictComparison::NonStrict,
198 },
199 ),
200 DType::Bool(Nullability::NonNullable),
201 ),
202 );
203 assert_snapshot!(nested_expr.display_tree().to_string(), @r#"
204 vortex.select include={result}
205 └── child: vortex.cast bool
206 └── input: vortex.between BetweenOptions { lower_strict: Strict, upper_strict: NonStrict }
207 ├── array: vortex.get_item "score"
208 │ └── input: vortex.root
209 ├── lower: vortex.literal 50i32
210 └── upper: vortex.literal 100i32
211 "#);
212
213 let select_from_pack_expr = select(
214 ["fizz", "buzz"],
215 pack(
216 [
217 ("fizz", root()),
218 ("bar", lit(5)),
219 ("buzz", eq(lit(42), get_item("answer", root()))),
220 ],
221 Nullability::Nullable,
222 ),
223 );
224 assert_snapshot!(select_from_pack_expr.display_tree().to_string(), @r#"
225 vortex.select include={fizz, buzz}
226 └── child: vortex.pack PackOptions { names: FieldNames([FieldName("fizz"), FieldName("bar"), FieldName("buzz")]), nullability: Nullable }
227 ├── fizz: vortex.root
228 ├── bar: vortex.literal 5i32
229 └── buzz: vortex.binary =
230 ├── lhs: vortex.literal 42i32
231 └── rhs: vortex.get_item "answer"
232 └── input: vortex.root
233 "#);
234 }
235}