vortex_array/display/mod.rs
1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright the Vortex contributors
3
4mod extractor;
5mod extractors;
6mod tree_display;
7
8use std::fmt::Display;
9use std::iter::repeat_n;
10
11pub use extractor::IndentedFormatter;
12pub use extractor::TreeContext;
13pub use extractor::TreeExtractor;
14pub use extractors::BufferExtractor;
15pub use extractors::EncodingSummaryExtractor;
16pub use extractors::MetadataExtractor;
17pub use extractors::NbytesExtractor;
18pub use extractors::StatsExtractor;
19use itertools::Itertools as _;
20pub use tree_display::TreeDisplay;
21
22use crate::ArrayRef;
23use crate::ExecutionCtx;
24
25/// Describe how to convert an array to a string.
26///
27/// See also:
28/// [Array::display_as](../trait.Array.html#method.display_as)
29/// and [DisplayArrayAs].
30pub enum DisplayOptions {
31 /// Only the top-level encoding id and limited metadata: `vortex.primitive(i16, len=5)`.
32 ///
33 /// ```
34 /// # use vortex_array::VortexSessionExecute;
35 /// # let ctx = vortex_array::array_session().create_execution_ctx();
36 /// # use vortex_array::display::DisplayOptions;
37 /// # use vortex_array::IntoArray;
38 /// # use vortex_buffer::buffer;
39 /// let array = buffer![0_i16, 1, 2, 3, 4].into_array();
40 /// assert_eq!(
41 /// format!("{}", array.display_as(DisplayOptions::MetadataOnly, &ctx)),
42 /// "vortex.primitive(i16, len=5)",
43 /// );
44 /// ```
45 MetadataOnly,
46 /// Only the logical values of the array: `[0i16, 1i16, 2i16, 3i16, 4i16]`.
47 ///
48 /// ```
49 /// # use vortex_array::VortexSessionExecute;
50 /// # let ctx = vortex_array::array_session().create_execution_ctx();
51 /// # use vortex_array::display::DisplayOptions;
52 /// # use vortex_array::IntoArray;
53 /// # use vortex_buffer::buffer;
54 /// let array = buffer![0_i16, 1, 2, 3, 4].into_array();
55 /// assert_eq!(
56 /// format!("{}", array.display_as(DisplayOptions::default(), &ctx)),
57 /// "[0i16, 1i16, 2i16, 3i16, 4i16]",
58 /// );
59 /// assert_eq!(
60 /// format!("{}", array.display_as(DisplayOptions::default(), &ctx)),
61 /// format!("{}", array.display_values(&ctx)),
62 /// );
63 /// ```
64 CommaSeparatedScalars { omit_comma_after_space: bool },
65 /// The tree of encodings without any concrete values.
66 ///
67 /// With buffers, metadata, and stats:
68 ///
69 /// ```
70 /// # use vortex_array::VortexSessionExecute;
71 /// # let ctx = vortex_array::array_session().create_execution_ctx();
72 /// # use vortex_array::display::DisplayOptions;
73 /// # use vortex_array::IntoArray;
74 /// # use vortex_buffer::buffer;
75 /// let array = buffer![0_i16, 1, 2, 3, 4].into_array();
76 /// let expected = "root: vortex.primitive(i16, len=5) nbytes=10 B (100.00%)
77 /// metadata: ptype: i16
78 /// buffer: values host 10 B (align=2) (100.00%)
79 /// ";
80 /// assert_eq!(format!("{}", array.display_as(DisplayOptions::TreeDisplay { buffers: true, metadata: true, stats: true }, &ctx)), expected);
81 ///
82 /// # use vortex_array::arrays::StructArray;
83 /// let array = StructArray::from_fields(&[
84 /// ("x", buffer![1, 2].into_array()),
85 /// ("y", buffer![3, 4].into_array()),
86 /// ]).unwrap().into_array();
87 /// let expected = "root: vortex.struct({x=i32, y=i32}, len=2) nbytes=16 B (100.00%)
88 /// metadata:\x20
89 /// x: vortex.primitive(i32, len=2) nbytes=8 B (50.00%)
90 /// metadata: ptype: i32
91 /// buffer: values host 8 B (align=4) (100.00%)
92 /// y: vortex.primitive(i32, len=2) nbytes=8 B (50.00%)
93 /// metadata: ptype: i32
94 /// buffer: values host 8 B (align=4) (100.00%)
95 /// ";
96 /// assert_eq!(format!("{}", array.display_as(DisplayOptions::TreeDisplay { buffers: true, metadata: true, stats: true }, &ctx)), expected);
97 /// ```
98 ///
99 /// With metadata and stats but no buffers:
100 ///
101 /// ```
102 /// # use vortex_array::VortexSessionExecute;
103 /// # let ctx = vortex_array::array_session().create_execution_ctx();
104 /// # use vortex_array::display::DisplayOptions;
105 /// # use vortex_array::IntoArray;
106 /// # use vortex_buffer::buffer;
107 /// let array = buffer![0_i16, 1, 2, 3, 4].into_array();
108 /// let expected = "root: vortex.primitive(i16, len=5) nbytes=10 B (100.00%)
109 /// metadata: ptype: i16
110 /// ";
111 /// assert_eq!(format!("{}", array.display_as(DisplayOptions::TreeDisplay { buffers: false, metadata: true, stats: true }, &ctx)), expected);
112 ///
113 /// # use vortex_array::arrays::StructArray;
114 /// let array = StructArray::from_fields(&[
115 /// ("x", buffer![1, 2].into_array()),
116 /// ("y", buffer![3, 4].into_array()),
117 /// ]).unwrap().into_array();
118 /// let expected = "root: vortex.struct({x=i32, y=i32}, len=2) nbytes=16 B (100.00%)
119 /// metadata:\x20
120 /// x: vortex.primitive(i32, len=2) nbytes=8 B (50.00%)
121 /// metadata: ptype: i32
122 /// y: vortex.primitive(i32, len=2) nbytes=8 B (50.00%)
123 /// metadata: ptype: i32
124 /// ";
125 /// assert_eq!(format!("{}", array.display_as(DisplayOptions::TreeDisplay { buffers: false, metadata: true, stats: true }, &ctx)), expected);
126 /// ```
127 ///
128 /// With metadata and buffers but no stats:
129 ///
130 /// ```
131 /// # use vortex_array::VortexSessionExecute;
132 /// # let ctx = vortex_array::array_session().create_execution_ctx();
133 /// # use vortex_array::display::DisplayOptions;
134 /// # use vortex_array::IntoArray;
135 /// # use vortex_buffer::buffer;
136 /// let array = buffer![0_i16, 1, 2, 3, 4].into_array();
137 /// let expected = "root: vortex.primitive(i16, len=5)
138 /// metadata: ptype: i16
139 /// buffer: values host 10 B (align=2)
140 /// ";
141 /// assert_eq!(format!("{}", array.display_as(DisplayOptions::TreeDisplay { buffers: true, metadata: true, stats: false }, &ctx)), expected);
142 ///
143 /// # use vortex_array::arrays::StructArray;
144 /// let array = StructArray::from_fields(&[
145 /// ("x", buffer![1, 2].into_array()),
146 /// ("y", buffer![3, 4].into_array()),
147 /// ]).unwrap().into_array();
148 /// let expected = "root: vortex.struct({x=i32, y=i32}, len=2)
149 /// metadata:\x20
150 /// x: vortex.primitive(i32, len=2)
151 /// metadata: ptype: i32
152 /// buffer: values host 8 B (align=4)
153 /// y: vortex.primitive(i32, len=2)
154 /// metadata: ptype: i32
155 /// buffer: values host 8 B (align=4)
156 /// ";
157 /// assert_eq!(format!("{}", array.display_as(DisplayOptions::TreeDisplay { buffers: true, metadata: true, stats: false }, &ctx)), expected);
158 /// ```
159 ///
160 /// With buffers and stats but no metadata:
161 ///
162 /// ```
163 /// # use vortex_array::VortexSessionExecute;
164 /// # let ctx = vortex_array::array_session().create_execution_ctx();
165 /// # use vortex_array::display::DisplayOptions;
166 /// # use vortex_array::IntoArray;
167 /// # use vortex_buffer::buffer;
168 /// let array = buffer![0_i16, 1, 2, 3, 4].into_array();
169 /// let expected = "root: vortex.primitive(i16, len=5) nbytes=10 B (100.00%)
170 /// buffer: values host 10 B (align=2) (100.00%)
171 /// ";
172 /// assert_eq!(format!("{}", array.display_as(DisplayOptions::TreeDisplay { buffers: true, metadata: false, stats: true }, &ctx)), expected);
173 ///
174 /// # use vortex_array::arrays::StructArray;
175 /// let array = StructArray::from_fields(&[
176 /// ("x", buffer![1, 2].into_array()),
177 /// ("y", buffer![3, 4].into_array()),
178 /// ]).unwrap().into_array();
179 /// let expected = "root: vortex.struct({x=i32, y=i32}, len=2) nbytes=16 B (100.00%)
180 /// x: vortex.primitive(i32, len=2) nbytes=8 B (50.00%)
181 /// buffer: values host 8 B (align=4) (100.00%)
182 /// y: vortex.primitive(i32, len=2) nbytes=8 B (50.00%)
183 /// buffer: values host 8 B (align=4) (100.00%)
184 /// ";
185 /// assert_eq!(format!("{}", array.display_as(DisplayOptions::TreeDisplay { buffers: true, metadata: false, stats: true }, &ctx)), expected);
186 /// ```
187 ///
188 /// With just buffers:
189 ///
190 /// ```
191 /// # use vortex_array::VortexSessionExecute;
192 /// # let ctx = vortex_array::array_session().create_execution_ctx();
193 /// # use vortex_array::display::DisplayOptions;
194 /// # use vortex_array::IntoArray;
195 /// # use vortex_buffer::buffer;
196 /// let array = buffer![0_i16, 1, 2, 3, 4].into_array();
197 /// let expected = "root: vortex.primitive(i16, len=5)
198 /// buffer: values host 10 B (align=2)
199 /// ";
200 /// assert_eq!(format!("{}", array.display_as(DisplayOptions::TreeDisplay { buffers: true, metadata: false, stats: false }, &ctx)), expected);
201 ///
202 /// # use vortex_array::arrays::StructArray;
203 /// let array = StructArray::from_fields(&[
204 /// ("x", buffer![1, 2].into_array()),
205 /// ("y", buffer![3, 4].into_array()),
206 /// ]).unwrap().into_array();
207 /// let expected = "root: vortex.struct({x=i32, y=i32}, len=2)
208 /// x: vortex.primitive(i32, len=2)
209 /// buffer: values host 8 B (align=4)
210 /// y: vortex.primitive(i32, len=2)
211 /// buffer: values host 8 B (align=4)
212 /// ";
213 /// assert_eq!(format!("{}", array.display_as(DisplayOptions::TreeDisplay { buffers: true, metadata: false, stats: false }, &ctx)), expected);
214 /// ```
215 ///
216 /// With just metadata:
217 ///
218 /// ```
219 /// # use vortex_array::VortexSessionExecute;
220 /// # let ctx = vortex_array::array_session().create_execution_ctx();
221 /// # use vortex_array::display::DisplayOptions;
222 /// # use vortex_array::IntoArray;
223 /// # use vortex_buffer::buffer;
224 /// let array = buffer![0_i16, 1, 2, 3, 4].into_array();
225 /// let expected = "root: vortex.primitive(i16, len=5)
226 /// metadata: ptype: i16
227 /// ";
228 /// assert_eq!(format!("{}", array.display_as(DisplayOptions::TreeDisplay { buffers: false, metadata: true, stats: false }, &ctx)), expected);
229 ///
230 /// # use vortex_array::arrays::StructArray;
231 /// let array = StructArray::from_fields(&[
232 /// ("x", buffer![1, 2].into_array()),
233 /// ("y", buffer![3, 4].into_array()),
234 /// ]).unwrap().into_array();
235 /// let expected = "root: vortex.struct({x=i32, y=i32}, len=2)
236 /// metadata:\x20
237 /// x: vortex.primitive(i32, len=2)
238 /// metadata: ptype: i32
239 /// y: vortex.primitive(i32, len=2)
240 /// metadata: ptype: i32
241 /// ";
242 /// assert_eq!(format!("{}", array.display_as(DisplayOptions::TreeDisplay { buffers: false, metadata: true, stats: false }, &ctx)), expected);
243 /// ```
244 ///
245 /// With just stats:
246 ///
247 /// ```
248 /// # use vortex_array::VortexSessionExecute;
249 /// # let ctx = vortex_array::array_session().create_execution_ctx();
250 /// # use vortex_array::display::DisplayOptions;
251 /// # use vortex_array::IntoArray;
252 /// # use vortex_buffer::buffer;
253 /// let array = buffer![0_i16, 1, 2, 3, 4].into_array();
254 /// let expected = "root: vortex.primitive(i16, len=5) nbytes=10 B (100.00%)
255 /// ";
256 /// assert_eq!(format!("{}", array.display_as(DisplayOptions::TreeDisplay { buffers: false, metadata: false, stats: true }, &ctx)), expected);
257 ///
258 /// # use vortex_array::arrays::StructArray;
259 /// let array = StructArray::from_fields(&[
260 /// ("x", buffer![1, 2].into_array()),
261 /// ("y", buffer![3, 4].into_array()),
262 /// ]).unwrap().into_array();
263 /// let expected = "root: vortex.struct({x=i32, y=i32}, len=2) nbytes=16 B (100.00%)
264 /// x: vortex.primitive(i32, len=2) nbytes=8 B (50.00%)
265 /// y: vortex.primitive(i32, len=2) nbytes=8 B (50.00%)
266 /// ";
267 /// assert_eq!(format!("{}", array.display_as(DisplayOptions::TreeDisplay { buffers: false, metadata: false, stats: true }, &ctx)), expected);
268 /// ```
269 ///
270 /// With neither buffers, metadata, stats, nor values:
271 ///
272 /// ```
273 /// # use vortex_array::VortexSessionExecute;
274 /// # let ctx = vortex_array::array_session().create_execution_ctx();
275 /// # use vortex_array::display::DisplayOptions;
276 /// # use vortex_array::IntoArray;
277 /// # use vortex_buffer::buffer;
278 /// let array = buffer![0_i16, 1, 2, 3, 4].into_array();
279 /// let expected = "root: vortex.primitive(i16, len=5)\n";
280 /// assert_eq!(format!("{}", array.display_as(DisplayOptions::TreeDisplay { buffers: false, metadata: false, stats: false }, &ctx)), expected);
281 ///
282 /// # use vortex_array::arrays::StructArray;
283 /// let array = StructArray::from_fields(&[
284 /// ("x", buffer![1, 2].into_array()),
285 /// ("y", buffer![3, 4].into_array()),
286 /// ]).unwrap().into_array();
287 /// let expected = "root: vortex.struct({x=i32, y=i32}, len=2)
288 /// x: vortex.primitive(i32, len=2)
289 /// y: vortex.primitive(i32, len=2)
290 /// ";
291 /// assert_eq!(format!("{}", array.display_as(DisplayOptions::TreeDisplay { buffers: false, metadata: false, stats: false }, &ctx)), expected);
292 /// ```
293 TreeDisplay {
294 buffers: bool,
295 metadata: bool,
296 stats: bool,
297 },
298 /// Display values in a formatted table with columns.
299 ///
300 /// For struct arrays, displays a column for each field in the struct.
301 /// For regular arrays, displays a single column with values.
302 ///
303 /// ```
304 /// # use vortex_array::VortexSessionExecute;
305 /// # let ctx = vortex_array::array_session().create_execution_ctx();
306 /// # use vortex_array::display::DisplayOptions;
307 /// # use vortex_array::arrays::StructArray;
308 /// # use vortex_array::IntoArray;
309 /// # use vortex_buffer::buffer;
310 /// let s = StructArray::from_fields(&[
311 /// ("x", buffer![1, 2].into_array()),
312 /// ("y", buffer![3, 4].into_array()),
313 /// ]).unwrap().into_array();
314 /// let expected = "
315 /// ┌──────┬──────┐
316 /// │ x │ y │
317 /// ├──────┼──────┤
318 /// │ 1i32 │ 3i32 │
319 /// ├──────┼──────┤
320 /// │ 2i32 │ 4i32 │
321 /// └──────┴──────┘".trim();
322 /// assert_eq!(format!("{}", s.display_as(DisplayOptions::TableDisplay, &ctx)), expected);
323 /// ```
324 #[cfg(feature = "table-display")]
325 TableDisplay,
326}
327
328impl Default for DisplayOptions {
329 fn default() -> Self {
330 Self::CommaSeparatedScalars {
331 omit_comma_after_space: false,
332 }
333 }
334}
335
336/// A shim used to display an array as specified in the options.
337///
338/// Values are read with a clone of the execution context, made each time the array is formatted.
339///
340/// See also:
341/// [Array::display_as](../trait.Array.html#method.display_as)
342/// and [DisplayOptions].
343pub struct DisplayArrayAs<'a> {
344 array: &'a ArrayRef,
345 options: DisplayOptions,
346 ctx: &'a ExecutionCtx,
347}
348
349impl<'a> DisplayArrayAs<'a> {
350 /// Display `array` as specified by `options`, reading values with `ctx`.
351 pub fn new(array: &'a ArrayRef, options: DisplayOptions, ctx: &'a ExecutionCtx) -> Self {
352 Self {
353 array,
354 options,
355 ctx,
356 }
357 }
358}
359
360impl Display for DisplayArrayAs<'_> {
361 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
362 self.array.fmt_as(f, &self.options, &mut self.ctx.clone())
363 }
364}
365
366/// Display the encoding and limited metadata of this array.
367///
368/// # Examples
369/// ```
370/// # use vortex_array::IntoArray;
371/// # use vortex_buffer::buffer;
372/// let array = buffer![0_i16, 1, 2, 3, 4].into_array();
373/// assert_eq!(
374/// format!("{}", array),
375/// "vortex.primitive(i16, len=5)",
376/// );
377/// ```
378impl Display for ArrayRef {
379 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
380 EncodingSummaryExtractor::write(self, f)
381 }
382}
383
384const DISPLAY_LIMIT: usize = 16;
385impl ArrayRef {
386 /// Display logical values of the array
387 ///
388 /// For example, an `i16` typed array containing the first five non-negative integers is displayed
389 /// as: `[0i16, 1i16, 2i16, 3i16, 4i16]`.
390 ///
391 /// # Examples
392 ///
393 /// ```
394 /// # use vortex_array::VortexSessionExecute;
395 /// # let ctx = vortex_array::array_session().create_execution_ctx();
396 /// # use vortex_array::IntoArray;
397 /// # use vortex_buffer::buffer;
398 /// let array = buffer![0_i16, 1, 2, 3, 4].into_array();
399 /// assert_eq!(
400 /// format!("{}", array.display_values(&ctx)),
401 /// "[0i16, 1i16, 2i16, 3i16, 4i16]",
402 /// )
403 /// ```
404 ///
405 /// See also:
406 /// [Array::display_as](..//trait.Array.html#method.display_as),
407 /// [DisplayArrayAs], and [DisplayOptions].
408 pub fn display_values<'a>(&'a self, ctx: &'a ExecutionCtx) -> DisplayArrayAs<'a> {
409 DisplayArrayAs::new(
410 self,
411 DisplayOptions::CommaSeparatedScalars {
412 omit_comma_after_space: false,
413 },
414 ctx,
415 )
416 }
417
418 /// Display the array as specified by the options, reading values with `ctx`.
419 ///
420 /// See [DisplayOptions] for examples.
421 pub fn display_as<'a>(
422 &'a self,
423 options: DisplayOptions,
424 ctx: &'a ExecutionCtx,
425 ) -> DisplayArrayAs<'a> {
426 DisplayArrayAs::new(self, options, ctx)
427 }
428
429 /// Display the tree of array encodings and lengths without metadata, buffers, or stats.
430 ///
431 /// # Examples
432 /// ```
433 /// # use vortex_array::display::DisplayOptions;
434 /// # use vortex_array::IntoArray;
435 /// # use vortex_buffer::buffer;
436 /// let array = buffer![0_i16, 1, 2, 3, 4].into_array();
437 /// let expected = "root: vortex.primitive(i16, len=5)\n";
438 /// assert_eq!(format!("{}", array.display_tree_encodings_only()), expected);
439 ///
440 /// # use vortex_array::arrays::StructArray;
441 /// let array = StructArray::from_fields(&[
442 /// ("x", buffer![1, 2].into_array()),
443 /// ("y", buffer![3, 4].into_array()),
444 /// ]).unwrap().into_array();
445 /// let expected = "root: vortex.struct({x=i32, y=i32}, len=2)
446 /// x: vortex.primitive(i32, len=2)
447 /// y: vortex.primitive(i32, len=2)
448 /// ";
449 /// assert_eq!(format!("{}", array.display_tree_encodings_only()), expected);
450 /// ```
451 pub fn display_tree_encodings_only(&self) -> TreeDisplay {
452 self.tree_display_builder().with(EncodingSummaryExtractor)
453 }
454
455 /// Display the tree of encodings of this array as an indented lists.
456 ///
457 /// While some metadata (such as length, bytes and validity-rate) are included, the logical
458 /// values of the array are not displayed. To view the logical values see
459 /// [Array::display_as](../trait.Array.html#method.display_as)
460 /// and [DisplayOptions].
461 ///
462 /// # Examples
463 /// ```
464 /// # use vortex_array::display::DisplayOptions;
465 /// # use vortex_array::IntoArray;
466 /// # use vortex_buffer::buffer;
467 /// let array = buffer![0_i16, 1, 2, 3, 4].into_array();
468 /// let expected = "root: vortex.primitive(i16, len=5) nbytes=10 B (100.00%)
469 /// metadata: ptype: i16
470 /// buffer: values host 10 B (align=2) (100.00%)
471 /// ";
472 /// assert_eq!(format!("{}", array.display_tree()), expected);
473 /// ```
474 pub fn display_tree(&self) -> TreeDisplay {
475 TreeDisplay::default_display(self.clone())
476 }
477
478 /// Create a tree display with all built-in extractors (nbytes, stats, metadata, buffers).
479 ///
480 /// This is the default, fully-detailed tree display. Use
481 /// `tree_display_builder()` for a blank slate.
482 ///
483 /// # Examples
484 /// ```
485 /// # use vortex_array::IntoArray;
486 /// # use vortex_buffer::buffer;
487 /// let array = buffer![0_i16, 1, 2, 3, 4].into_array();
488 /// let expected = "root: vortex.primitive(i16, len=5) nbytes=10 B (100.00%)
489 /// metadata: ptype: i16
490 /// buffer: values host 10 B (align=2) (100.00%)
491 /// ";
492 /// assert_eq!(array.tree_display().to_string(), expected);
493 /// ```
494 pub fn tree_display(&self) -> TreeDisplay {
495 TreeDisplay::default_display(self.clone())
496 }
497
498 /// Create a composable tree display builder with no extractors.
499 ///
500 /// With no extractors, only the node names are shown.
501 /// Add extractors with [`.with()`][TreeDisplay::with] to include additional information.
502 /// Most builders should start with [`EncodingSummaryExtractor`] to include encoding headers.
503 ///
504 /// # Examples
505 /// ```
506 /// # use vortex_array::IntoArray;
507 /// # use vortex_buffer::buffer;
508 /// use vortex_array::display::{EncodingSummaryExtractor, NbytesExtractor, MetadataExtractor, BufferExtractor};
509 ///
510 /// let array = buffer![0_i16, 1, 2, 3, 4].into_array();
511 ///
512 /// // Encodings only
513 /// let encodings = array.tree_display_builder()
514 /// .with(EncodingSummaryExtractor)
515 /// .to_string();
516 /// assert_eq!(encodings, "root: vortex.primitive(i16, len=5)\n");
517 ///
518 /// // With encoding + nbytes
519 /// let with_nbytes = array.tree_display_builder()
520 /// .with(EncodingSummaryExtractor)
521 /// .with(NbytesExtractor)
522 /// .to_string();
523 /// assert_eq!(with_nbytes, "root: vortex.primitive(i16, len=5) nbytes=10 B (100.00%)\n");
524 ///
525 /// // With encoding, metadata, and buffers
526 /// let detailed = array.tree_display_builder()
527 /// .with(EncodingSummaryExtractor)
528 /// .with(MetadataExtractor)
529 /// .with(BufferExtractor { show_percent: false })
530 /// .to_string();
531 /// let expected = "root: vortex.primitive(i16, len=5)\n metadata: ptype: i16\n buffer: values host 10 B (align=2)\n";
532 /// assert_eq!(detailed, expected);
533 /// ```
534 pub fn tree_display_builder(&self) -> TreeDisplay {
535 TreeDisplay::new(self.clone())
536 }
537
538 /// Display the array as a formatted table.
539 ///
540 /// For struct arrays, displays a column for each field in the struct.
541 /// For regular arrays, displays a single column with values.
542 ///
543 /// # Examples
544 /// ```
545 /// # use vortex_array::VortexSessionExecute;
546 /// # let ctx = vortex_array::array_session().create_execution_ctx();
547 /// # #[cfg(feature = "table-display")]
548 /// # {
549 /// # use vortex_array::arrays::StructArray;
550 /// # use vortex_array::IntoArray;
551 /// # use vortex_buffer::buffer;
552 /// let s = StructArray::from_fields(&[
553 /// ("x", buffer![1, 2].into_array()),
554 /// ("y", buffer![3, 4].into_array()),
555 /// ]).unwrap().into_array();
556 /// let expected = "
557 /// ┌──────┬──────┐
558 /// │ x │ y │
559 /// ├──────┼──────┤
560 /// │ 1i32 │ 3i32 │
561 /// ├──────┼──────┤
562 /// │ 2i32 │ 4i32 │
563 /// └──────┴──────┘".trim();
564 /// assert_eq!(format!("{}", s.display_table(&ctx)), expected);
565 /// # }
566 /// ```
567 #[cfg(feature = "table-display")]
568 pub fn display_table<'a>(&'a self, ctx: &'a ExecutionCtx) -> DisplayArrayAs<'a> {
569 DisplayArrayAs::new(self, DisplayOptions::TableDisplay, ctx)
570 }
571
572 fn fmt_as(
573 &self,
574 f: &mut std::fmt::Formatter,
575 options: &DisplayOptions,
576 ctx: &mut ExecutionCtx,
577 ) -> std::fmt::Result {
578 match options {
579 DisplayOptions::MetadataOnly => EncodingSummaryExtractor::write(self, f),
580 DisplayOptions::CommaSeparatedScalars {
581 omit_comma_after_space,
582 } => {
583 let opening_brace = if f.alternate() { "[\n" } else { "[" };
584 let closing_brace = if f.alternate() { "\n]" } else { "]" };
585
586 let sep = if *omit_comma_after_space { "," } else { ", " };
587 let sep = if f.alternate() { ",\n" } else { sep };
588 let limit = self.len().min(f.precision().unwrap_or(DISPLAY_LIMIT));
589 let is_truncated = self.len() > limit;
590
591 let mut fmt_scalar = |i| {
592 self.execute_scalar(i, ctx)
593 .map_or_else(|e| format!("<error: {e}>"), |s| s.to_string())
594 };
595 let head = (0..limit.saturating_sub(3))
596 .map(&mut fmt_scalar)
597 .collect::<Vec<_>>();
598 let tail = (self.len().saturating_sub(3)..self.len())
599 .map(&mut fmt_scalar)
600 .collect::<Vec<_>>();
601 write!(
602 f,
603 "{opening_brace}{}{closing_brace}",
604 head.into_iter()
605 .chain(repeat_n("...".to_string(), is_truncated as usize))
606 .chain(tail)
607 .format(sep)
608 )
609 }
610 DisplayOptions::TreeDisplay {
611 buffers,
612 metadata,
613 stats,
614 } => {
615 let extractors: [(bool, Box<dyn TreeExtractor<ArrayRef, TreeContext>>); 5] = [
616 (true, Box::new(EncodingSummaryExtractor)),
617 (*stats, Box::new(NbytesExtractor)),
618 (*stats, Box::new(StatsExtractor)),
619 (*metadata, Box::new(MetadataExtractor)),
620 (
621 *buffers,
622 Box::new(BufferExtractor {
623 show_percent: *stats,
624 }),
625 ),
626 ];
627 let mut display = TreeDisplay::new(self.clone());
628 for (enabled, extractor) in extractors {
629 if enabled {
630 display = display.with_boxed(extractor);
631 }
632 }
633 write!(f, "{display}")
634 }
635 #[cfg(feature = "table-display")]
636 DisplayOptions::TableDisplay => {
637 use vortex_mask::Mask;
638
639 use crate::arrays::StructArray;
640 use crate::arrays::struct_::StructArrayExt;
641 use crate::dtype::DType;
642
643 let mut builder = tabled::builder::Builder::default();
644
645 // Special logic for struct arrays.
646 let DType::Struct(sf, _) = self.dtype() else {
647 // For non-struct arrays, simply display a single column table without header.
648 for row_idx in 0..self.len() {
649 let value = self
650 .execute_scalar(row_idx, ctx)
651 .map_or_else(|e| format!("<error: {e}>"), |s| s.to_string());
652 builder.push_record([value]);
653 }
654
655 let mut table = builder.build();
656 table.with(tabled::settings::Style::modern());
657
658 return write!(f, "{table}");
659 };
660
661 let struct_ = match self.clone().execute::<StructArray>(ctx) {
662 Ok(struct_) => struct_,
663 Err(e) => return write!(f, "<error: {e}>"),
664 };
665 builder.push_record(sf.names().iter().map(|name| name.to_string()));
666
667 // Resolve validity to a mask once instead of probing it per row.
668 let validity = self
669 .validity()
670 .and_then(|v| v.execute_mask(self.len(), ctx))
671 .unwrap_or_else(|_| Mask::new_false(self.len()));
672
673 for row_idx in 0..self.len() {
674 if !validity.value(row_idx) {
675 let null_row = vec!["null".to_string(); sf.names().len()];
676 builder.push_record(null_row);
677 } else {
678 let mut row = Vec::with_capacity(struct_.struct_fields().nfields());
679 for field_array in StructArrayExt::iter_unmasked_fields(&struct_) {
680 let value = field_array
681 .execute_scalar(row_idx, ctx)
682 .map_or_else(|e| format!("<error: {e}>"), |s| s.to_string());
683 row.push(value);
684 }
685 builder.push_record(row);
686 }
687 }
688
689 let mut table = builder.build();
690 table.with(tabled::settings::Style::modern());
691
692 // Center headers
693 for col_idx in 0..sf.names().len() {
694 table.modify((0, col_idx), tabled::settings::Alignment::center());
695 }
696
697 for row_idx in 0..self.len() {
698 if !validity.value(row_idx) {
699 table.modify(
700 (1 + row_idx, 0),
701 tabled::settings::Span::column(sf.names().len() as isize),
702 );
703 table.modify((1 + row_idx, 0), tabled::settings::Alignment::center());
704 }
705 }
706
707 write!(f, "{table}")
708 }
709 }
710 }
711}
712
713#[cfg(test)]
714mod test {
715 use vortex_buffer::Buffer;
716 use vortex_buffer::buffer;
717
718 use crate::IntoArray as _;
719 use crate::VortexSessionExecute;
720 use crate::array_session;
721 use crate::arrays::BoolArray;
722 use crate::arrays::ListArray;
723 use crate::arrays::PrimitiveArray;
724 use crate::arrays::StructArray;
725 use crate::display::DISPLAY_LIMIT;
726 use crate::dtype::FieldNames;
727 use crate::validity::Validity;
728
729 #[test]
730 fn test_primitive() {
731 let ctx = array_session().create_execution_ctx();
732 let x = Buffer::<u32>::empty().into_array();
733 assert_eq!(x.display_values(&ctx).to_string(), "[]");
734
735 let x = buffer![1].into_array();
736 assert_eq!(x.display_values(&ctx).to_string(), "[1i32]");
737
738 let x = buffer![1, 2, 3, 4].into_array();
739 assert_eq!(
740 x.display_values(&ctx).to_string(),
741 "[1i32, 2i32, 3i32, 4i32]"
742 );
743
744 let x =
745 PrimitiveArray::from_iter(0i32..i32::try_from(DISPLAY_LIMIT).unwrap() + 1).into_array();
746 assert_eq!(
747 x.display_values(&ctx).to_string(),
748 "[0i32, 1i32, 2i32, 3i32, 4i32, 5i32, 6i32, 7i32, 8i32, 9i32, 10i32, 11i32, 12i32, ..., 14i32, 15i32, 16i32]"
749 );
750 }
751
752 #[test]
753 fn test_empty_struct() {
754 let ctx = array_session().create_execution_ctx();
755 let s = StructArray::try_new(
756 FieldNames::empty(),
757 vec![],
758 3,
759 Validity::Array(BoolArray::from_iter([true, false, true]).into_array()),
760 )
761 .unwrap()
762 .into_array();
763 assert_eq!(s.display_values(&ctx).to_string(), "[{}, null, {}]");
764 }
765
766 #[test]
767 fn test_simple_struct() {
768 let ctx = array_session().create_execution_ctx();
769 let s = StructArray::from_fields(&[
770 ("x", buffer![1, 2, 3, 4].into_array()),
771 ("y", buffer![-1, -2, -3, -4].into_array()),
772 ])
773 .unwrap()
774 .into_array();
775 assert_eq!(
776 s.display_values(&ctx).to_string(),
777 "[{x: 1i32, y: -1i32}, {x: 2i32, y: -2i32}, {x: 3i32, y: -3i32}, {x: 4i32, y: -4i32}]"
778 );
779 }
780
781 #[test]
782 fn test_list() {
783 let ctx = array_session().create_execution_ctx();
784 let x = ListArray::try_new(
785 buffer![1, 2, 3, 4].into_array(),
786 buffer![0, 0, 1, 1, 2, 4].into_array(),
787 Validity::Array(BoolArray::from_iter([true, true, false, true, true]).into_array()),
788 )
789 .unwrap()
790 .into_array();
791 assert_eq!(
792 x.display_values(&ctx).to_string(),
793 "[[], [1i32], null, [2i32], [3i32, 4i32]]"
794 );
795 }
796
797 #[test]
798 fn test_display_tree_nullable_primitive_validity_child() {
799 let array =
800 PrimitiveArray::from_option_iter([Some(1i64), Some(2), None, Some(3)]).into_array();
801 let expected = "root: vortex.primitive(i64?, len=4) nbytes=33 B (100.00%)\n metadata: ptype: i64\n buffer: values host 32 B (align=8) (96.97%)\n validity: vortex.bool(bool, len=4) nbytes=1 B (3.03%)\n metadata: offset: 0\n buffer: bits host 1 B (align=1) (100.00%)\n";
802 assert_eq!(format!("{}", array.display_tree()), expected);
803 }
804
805 #[test]
806 fn test_table_display_primitive() {
807 let ctx = array_session().create_execution_ctx();
808 use crate::display::DisplayOptions;
809
810 let array = buffer![1, 2, 3, 4].into_array();
811 let table_display = array.display_as(DisplayOptions::TableDisplay, &ctx);
812 assert_eq!(
813 table_display.to_string(),
814 r"
815┌──────┐
816│ 1i32 │
817├──────┤
818│ 2i32 │
819├──────┤
820│ 3i32 │
821├──────┤
822│ 4i32 │
823└──────┘"
824 .trim()
825 );
826 }
827
828 #[test]
829 fn test_table_display() {
830 let ctx = array_session().create_execution_ctx();
831 use crate::display::DisplayOptions;
832
833 let array =
834 PrimitiveArray::from_option_iter(vec![Some(-1), Some(-2), Some(-3), None]).into_array();
835
836 let struct_ = StructArray::try_from_iter_with_validity(
837 [("x", buffer![1, 2, 3, 4].into_array()), ("y", array)],
838 Validity::Array(BoolArray::from_iter([true, false, true, true]).into_array()),
839 )
840 .unwrap()
841 .into_array();
842
843 let table_display = struct_.display_as(DisplayOptions::TableDisplay, &ctx);
844 assert_eq!(
845 table_display.to_string(),
846 r"
847┌──────┬───────┐
848│ x │ y │
849├──────┼───────┤
850│ 1i32 │ -1i32 │
851├──────┼───────┤
852│ null │
853├──────┼───────┤
854│ 3i32 │ -3i32 │
855├──────┼───────┤
856│ 4i32 │ null │
857└──────┴───────┘"
858 .trim()
859 );
860 }
861}