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