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