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