1use std::fmt::Display;
26
27use comfy_table::{Cell, Table};
28
29use arrow_array::{Array, ArrayRef, RecordBatch};
30use arrow_schema::ArrowError;
31
32use crate::display::{ArrayFormatter, FormatOptions};
33
34pub fn pretty_format_batches(results: &[RecordBatch]) -> Result<impl Display, ArrowError> {
36 let options = FormatOptions::default().with_display_error(true);
37 pretty_format_batches_with_options(results, &options)
38}
39
40pub fn pretty_format_batches_with_options(
42 results: &[RecordBatch],
43 options: &FormatOptions,
44) -> Result<impl Display, ArrowError> {
45 create_table(results, options)
46}
47
48pub fn pretty_format_columns(
50 col_name: &str,
51 results: &[ArrayRef],
52) -> Result<impl Display, ArrowError> {
53 let options = FormatOptions::default().with_display_error(true);
54 pretty_format_columns_with_options(col_name, results, &options)
55}
56
57fn pretty_format_columns_with_options(
59 col_name: &str,
60 results: &[ArrayRef],
61 options: &FormatOptions,
62) -> Result<impl Display, ArrowError> {
63 create_column(col_name, results, options)
64}
65
66pub fn print_batches(results: &[RecordBatch]) -> Result<(), ArrowError> {
68 println!("{}", pretty_format_batches(results)?);
69 Ok(())
70}
71
72pub fn print_columns(col_name: &str, results: &[ArrayRef]) -> Result<(), ArrowError> {
74 println!("{}", pretty_format_columns(col_name, results)?);
75 Ok(())
76}
77
78fn create_table(results: &[RecordBatch], options: &FormatOptions) -> Result<Table, ArrowError> {
80 let mut table = Table::new();
81 table.load_preset("||--+-++| ++++++");
82
83 if results.is_empty() {
84 return Ok(table);
85 }
86
87 let schema = results[0].schema();
88
89 let mut header = Vec::new();
90 for field in schema.fields() {
91 header.push(Cell::new(field.name()));
92 }
93 table.set_header(header);
94
95 for batch in results {
96 let formatters = batch
97 .columns()
98 .iter()
99 .map(|c| ArrayFormatter::try_new(c.as_ref(), options))
100 .collect::<Result<Vec<_>, ArrowError>>()?;
101
102 for row in 0..batch.num_rows() {
103 let mut cells = Vec::new();
104 for formatter in &formatters {
105 cells.push(Cell::new(formatter.value(row)));
106 }
107 table.add_row(cells);
108 }
109 }
110
111 Ok(table)
112}
113
114fn create_column(
115 field: &str,
116 columns: &[ArrayRef],
117 options: &FormatOptions,
118) -> Result<Table, ArrowError> {
119 let mut table = Table::new();
120 table.load_preset("||--+-++| ++++++");
121
122 if columns.is_empty() {
123 return Ok(table);
124 }
125
126 let header = vec![Cell::new(field)];
127 table.set_header(header);
128
129 for col in columns {
130 let formatter = ArrayFormatter::try_new(col.as_ref(), options)?;
131 for row in 0..col.len() {
132 let cells = vec![Cell::new(formatter.value(row))];
133 table.add_row(cells);
134 }
135 }
136
137 Ok(table)
138}
139
140#[cfg(test)]
141mod tests {
142 use std::fmt::Write;
143 use std::sync::Arc;
144
145 use half::f16;
146
147 use arrow_array::builder::*;
148 use arrow_array::types::*;
149 use arrow_array::*;
150 use arrow_buffer::{IntervalDayTime, IntervalMonthDayNano, ScalarBuffer};
151 use arrow_schema::*;
152
153 use crate::display::array_value_to_string;
154
155 use super::*;
156
157 #[test]
158 fn test_pretty_format_batches() {
159 let schema = Arc::new(Schema::new(vec![
161 Field::new("a", DataType::Utf8, true),
162 Field::new("b", DataType::Int32, true),
163 ]));
164
165 let batch = RecordBatch::try_new(
167 schema,
168 vec![
169 Arc::new(array::StringArray::from(vec![
170 Some("a"),
171 Some("b"),
172 None,
173 Some("d"),
174 ])),
175 Arc::new(array::Int32Array::from(vec![
176 Some(1),
177 None,
178 Some(10),
179 Some(100),
180 ])),
181 ],
182 )
183 .unwrap();
184
185 let table = pretty_format_batches(&[batch]).unwrap().to_string();
186
187 let expected = vec![
188 "+---+-----+",
189 "| a | b |",
190 "+---+-----+",
191 "| a | 1 |",
192 "| b | |",
193 "| | 10 |",
194 "| d | 100 |",
195 "+---+-----+",
196 ];
197
198 let actual: Vec<&str> = table.lines().collect();
199
200 assert_eq!(expected, actual, "Actual result:\n{table}");
201 }
202
203 #[test]
204 fn test_pretty_format_columns() {
205 let columns = vec![
206 Arc::new(array::StringArray::from(vec![
207 Some("a"),
208 Some("b"),
209 None,
210 Some("d"),
211 ])) as ArrayRef,
212 Arc::new(array::StringArray::from(vec![Some("e"), None, Some("g")])),
213 ];
214
215 let table = pretty_format_columns("a", &columns).unwrap().to_string();
216
217 let expected = vec![
218 "+---+", "| a |", "+---+", "| a |", "| b |", "| |", "| d |", "| e |", "| |",
219 "| g |", "+---+",
220 ];
221
222 let actual: Vec<&str> = table.lines().collect();
223
224 assert_eq!(expected, actual, "Actual result:\n{table}");
225 }
226
227 #[test]
228 fn test_pretty_format_null() {
229 let schema = Arc::new(Schema::new(vec![
230 Field::new("a", DataType::Utf8, true),
231 Field::new("b", DataType::Int32, true),
232 Field::new("c", DataType::Null, true),
233 ]));
234
235 let num_rows = 4;
236 let arrays = schema
237 .fields()
238 .iter()
239 .map(|f| new_null_array(f.data_type(), num_rows))
240 .collect();
241
242 let batch = RecordBatch::try_new(schema, arrays).unwrap();
244
245 let table = pretty_format_batches(&[batch]).unwrap().to_string();
246
247 let expected = vec![
248 "+---+---+---+",
249 "| a | b | c |",
250 "+---+---+---+",
251 "| | | |",
252 "| | | |",
253 "| | | |",
254 "| | | |",
255 "+---+---+---+",
256 ];
257
258 let actual: Vec<&str> = table.lines().collect();
259
260 assert_eq!(expected, actual, "Actual result:\n{table:#?}");
261 }
262
263 #[test]
264 fn test_pretty_format_dictionary() {
265 let field = Field::new_dictionary("d1", DataType::Int32, DataType::Utf8, true);
267 let schema = Arc::new(Schema::new(vec![field]));
268
269 let mut builder = StringDictionaryBuilder::<Int32Type>::new();
270
271 builder.append_value("one");
272 builder.append_null();
273 builder.append_value("three");
274 let array = Arc::new(builder.finish());
275
276 let batch = RecordBatch::try_new(schema, vec![array]).unwrap();
277
278 let table = pretty_format_batches(&[batch]).unwrap().to_string();
279
280 let expected = vec![
281 "+-------+",
282 "| d1 |",
283 "+-------+",
284 "| one |",
285 "| |",
286 "| three |",
287 "+-------+",
288 ];
289
290 let actual: Vec<&str> = table.lines().collect();
291
292 assert_eq!(expected, actual, "Actual result:\n{table}");
293 }
294
295 #[test]
296 fn test_pretty_format_fixed_size_list() {
297 let field_type =
299 DataType::FixedSizeList(Arc::new(Field::new_list_field(DataType::Int32, true)), 3);
300 let schema = Arc::new(Schema::new(vec![Field::new("d1", field_type, true)]));
301
302 let keys_builder = Int32Array::builder(3);
303 let mut builder = FixedSizeListBuilder::new(keys_builder, 3);
304
305 builder.values().append_slice(&[1, 2, 3]);
306 builder.append(true);
307 builder.values().append_slice(&[4, 5, 6]);
308 builder.append(false);
309 builder.values().append_slice(&[7, 8, 9]);
310 builder.append(true);
311
312 let array = Arc::new(builder.finish());
313
314 let batch = RecordBatch::try_new(schema, vec![array]).unwrap();
315 let table = pretty_format_batches(&[batch]).unwrap().to_string();
316 let expected = vec![
317 "+-----------+",
318 "| d1 |",
319 "+-----------+",
320 "| [1, 2, 3] |",
321 "| |",
322 "| [7, 8, 9] |",
323 "+-----------+",
324 ];
325
326 let actual: Vec<&str> = table.lines().collect();
327
328 assert_eq!(expected, actual, "Actual result:\n{table}");
329 }
330
331 #[test]
332 fn test_pretty_format_string_view() {
333 let schema = Arc::new(Schema::new(vec![Field::new(
334 "d1",
335 DataType::Utf8View,
336 true,
337 )]));
338
339 let mut builder = StringViewBuilder::with_capacity(20);
341 builder.append_value("hello");
342 builder.append_null();
343 builder.append_value("longer than 12 bytes");
344 builder.append_value("another than 12 bytes");
345 builder.append_null();
346 builder.append_value("small");
347
348 let array: ArrayRef = Arc::new(builder.finish());
349 let batch = RecordBatch::try_new(schema, vec![array]).unwrap();
350 let table = pretty_format_batches(&[batch]).unwrap().to_string();
351 let expected = vec![
352 "+-----------------------+",
353 "| d1 |",
354 "+-----------------------+",
355 "| hello |",
356 "| |",
357 "| longer than 12 bytes |",
358 "| another than 12 bytes |",
359 "| |",
360 "| small |",
361 "+-----------------------+",
362 ];
363
364 let actual: Vec<&str> = table.lines().collect();
365
366 assert_eq!(expected, actual, "Actual result:\n{table:#?}");
367 }
368
369 #[test]
370 fn test_pretty_format_binary_view() {
371 let schema = Arc::new(Schema::new(vec![Field::new(
372 "d1",
373 DataType::BinaryView,
374 true,
375 )]));
376
377 let mut builder = BinaryViewBuilder::with_capacity(20);
379 builder.append_value(b"hello");
380 builder.append_null();
381 builder.append_value(b"longer than 12 bytes");
382 builder.append_value(b"another than 12 bytes");
383 builder.append_null();
384 builder.append_value(b"small");
385
386 let array: ArrayRef = Arc::new(builder.finish());
387 let batch = RecordBatch::try_new(schema, vec![array]).unwrap();
388 let table = pretty_format_batches(&[batch]).unwrap().to_string();
389 let expected = vec![
390 "+--------------------------------------------+",
391 "| d1 |",
392 "+--------------------------------------------+",
393 "| 68656c6c6f |",
394 "| |",
395 "| 6c6f6e676572207468616e203132206279746573 |",
396 "| 616e6f74686572207468616e203132206279746573 |",
397 "| |",
398 "| 736d616c6c |",
399 "+--------------------------------------------+",
400 ];
401
402 let actual: Vec<&str> = table.lines().collect();
403
404 assert_eq!(expected, actual, "Actual result:\n\n{table:#?}");
405 }
406
407 #[test]
408 fn test_pretty_format_fixed_size_binary() {
409 let field_type = DataType::FixedSizeBinary(3);
411 let schema = Arc::new(Schema::new(vec![Field::new("d1", field_type, true)]));
412
413 let mut builder = FixedSizeBinaryBuilder::with_capacity(3, 3);
414
415 builder.append_value([1, 2, 3]).unwrap();
416 builder.append_null();
417 builder.append_value([7, 8, 9]).unwrap();
418
419 let array = Arc::new(builder.finish());
420
421 let batch = RecordBatch::try_new(schema, vec![array]).unwrap();
422 let table = pretty_format_batches(&[batch]).unwrap().to_string();
423 let expected = vec![
424 "+--------+",
425 "| d1 |",
426 "+--------+",
427 "| 010203 |",
428 "| |",
429 "| 070809 |",
430 "+--------+",
431 ];
432
433 let actual: Vec<&str> = table.lines().collect();
434
435 assert_eq!(expected, actual, "Actual result:\n{table}");
436 }
437
438 macro_rules! check_datetime {
442 ($ARRAYTYPE:ident, $VALUE:expr, $EXPECTED_RESULT:expr) => {
443 let mut builder = $ARRAYTYPE::builder(10);
444 builder.append_value($VALUE);
445 builder.append_null();
446 let array = builder.finish();
447
448 let schema = Arc::new(Schema::new(vec![Field::new(
449 "f",
450 array.data_type().clone(),
451 true,
452 )]));
453 let batch = RecordBatch::try_new(schema, vec![Arc::new(array)]).unwrap();
454
455 let table = pretty_format_batches(&[batch])
456 .expect("formatting batches")
457 .to_string();
458
459 let expected = $EXPECTED_RESULT;
460 let actual: Vec<&str> = table.lines().collect();
461
462 assert_eq!(expected, actual, "Actual result:\n\n{actual:#?}\n\n");
463 };
464 }
465
466 fn timestamp_batch<T: ArrowTimestampType>(timezone: &str, value: T::Native) -> RecordBatch {
467 let mut builder = PrimitiveBuilder::<T>::with_capacity(10);
468 builder.append_value(value);
469 builder.append_null();
470 let array = builder.finish();
471 let array = array.with_timezone(timezone);
472
473 let schema = Arc::new(Schema::new(vec![Field::new(
474 "f",
475 array.data_type().clone(),
476 true,
477 )]));
478 RecordBatch::try_new(schema, vec![Arc::new(array)]).unwrap()
479 }
480
481 #[test]
482 fn test_pretty_format_timestamp_second_with_fixed_offset_timezone() {
483 let batch = timestamp_batch::<TimestampSecondType>("+08:00", 11111111);
484 let table = pretty_format_batches(&[batch]).unwrap().to_string();
485
486 let expected = vec![
487 "+---------------------------+",
488 "| f |",
489 "+---------------------------+",
490 "| 1970-05-09T22:25:11+08:00 |",
491 "| |",
492 "+---------------------------+",
493 ];
494 let actual: Vec<&str> = table.lines().collect();
495 assert_eq!(expected, actual, "Actual result:\n\n{actual:#?}\n\n");
496 }
497
498 #[test]
499 fn test_pretty_format_timestamp_second() {
500 let expected = vec![
501 "+---------------------+",
502 "| f |",
503 "+---------------------+",
504 "| 1970-05-09T14:25:11 |",
505 "| |",
506 "+---------------------+",
507 ];
508 check_datetime!(TimestampSecondArray, 11111111, expected);
509 }
510
511 #[test]
512 fn test_pretty_format_timestamp_millisecond() {
513 let expected = vec![
514 "+-------------------------+",
515 "| f |",
516 "+-------------------------+",
517 "| 1970-01-01T03:05:11.111 |",
518 "| |",
519 "+-------------------------+",
520 ];
521 check_datetime!(TimestampMillisecondArray, 11111111, expected);
522 }
523
524 #[test]
525 fn test_pretty_format_timestamp_microsecond() {
526 let expected = vec![
527 "+----------------------------+",
528 "| f |",
529 "+----------------------------+",
530 "| 1970-01-01T00:00:11.111111 |",
531 "| |",
532 "+----------------------------+",
533 ];
534 check_datetime!(TimestampMicrosecondArray, 11111111, expected);
535 }
536
537 #[test]
538 fn test_pretty_format_timestamp_nanosecond() {
539 let expected = vec![
540 "+-------------------------------+",
541 "| f |",
542 "+-------------------------------+",
543 "| 1970-01-01T00:00:00.011111111 |",
544 "| |",
545 "+-------------------------------+",
546 ];
547 check_datetime!(TimestampNanosecondArray, 11111111, expected);
548 }
549
550 #[test]
551 fn test_pretty_format_date_32() {
552 let expected = vec![
553 "+------------+",
554 "| f |",
555 "+------------+",
556 "| 1973-05-19 |",
557 "| |",
558 "+------------+",
559 ];
560 check_datetime!(Date32Array, 1234, expected);
561 }
562
563 #[test]
564 fn test_pretty_format_date_64() {
565 let expected = vec![
566 "+---------------------+",
567 "| f |",
568 "+---------------------+",
569 "| 2005-03-18T01:58:20 |",
570 "| |",
571 "+---------------------+",
572 ];
573 check_datetime!(Date64Array, 1111111100000, expected);
574 }
575
576 #[test]
577 fn test_pretty_format_time_32_second() {
578 let expected = vec![
579 "+----------+",
580 "| f |",
581 "+----------+",
582 "| 00:18:31 |",
583 "| |",
584 "+----------+",
585 ];
586 check_datetime!(Time32SecondArray, 1111, expected);
587 }
588
589 #[test]
590 fn test_pretty_format_time_32_millisecond() {
591 let expected = vec![
592 "+--------------+",
593 "| f |",
594 "+--------------+",
595 "| 03:05:11.111 |",
596 "| |",
597 "+--------------+",
598 ];
599 check_datetime!(Time32MillisecondArray, 11111111, expected);
600 }
601
602 #[test]
603 fn test_pretty_format_time_64_microsecond() {
604 let expected = vec![
605 "+-----------------+",
606 "| f |",
607 "+-----------------+",
608 "| 00:00:11.111111 |",
609 "| |",
610 "+-----------------+",
611 ];
612 check_datetime!(Time64MicrosecondArray, 11111111, expected);
613 }
614
615 #[test]
616 fn test_pretty_format_time_64_nanosecond() {
617 let expected = vec![
618 "+--------------------+",
619 "| f |",
620 "+--------------------+",
621 "| 00:00:00.011111111 |",
622 "| |",
623 "+--------------------+",
624 ];
625 check_datetime!(Time64NanosecondArray, 11111111, expected);
626 }
627
628 #[test]
629 fn test_int_display() {
630 let array = Arc::new(Int32Array::from(vec![6, 3])) as ArrayRef;
631 let actual_one = array_value_to_string(&array, 0).unwrap();
632 let expected_one = "6";
633
634 let actual_two = array_value_to_string(&array, 1).unwrap();
635 let expected_two = "3";
636 assert_eq!(actual_one, expected_one);
637 assert_eq!(actual_two, expected_two);
638 }
639
640 #[test]
641 fn test_decimal_display() {
642 let precision = 10;
643 let scale = 2;
644
645 let array = [Some(101), None, Some(200), Some(3040)]
646 .into_iter()
647 .collect::<Decimal128Array>()
648 .with_precision_and_scale(precision, scale)
649 .unwrap();
650
651 let dm = Arc::new(array) as ArrayRef;
652
653 let schema = Arc::new(Schema::new(vec![Field::new(
654 "f",
655 dm.data_type().clone(),
656 true,
657 )]));
658
659 let batch = RecordBatch::try_new(schema, vec![dm]).unwrap();
660
661 let table = pretty_format_batches(&[batch]).unwrap().to_string();
662
663 let expected = vec![
664 "+-------+",
665 "| f |",
666 "+-------+",
667 "| 1.01 |",
668 "| |",
669 "| 2.00 |",
670 "| 30.40 |",
671 "+-------+",
672 ];
673
674 let actual: Vec<&str> = table.lines().collect();
675 assert_eq!(expected, actual, "Actual result:\n{table}");
676 }
677
678 #[test]
679 fn test_decimal_display_zero_scale() {
680 let precision = 5;
681 let scale = 0;
682
683 let array = [Some(101), None, Some(200), Some(3040)]
684 .into_iter()
685 .collect::<Decimal128Array>()
686 .with_precision_and_scale(precision, scale)
687 .unwrap();
688
689 let dm = Arc::new(array) as ArrayRef;
690
691 let schema = Arc::new(Schema::new(vec![Field::new(
692 "f",
693 dm.data_type().clone(),
694 true,
695 )]));
696
697 let batch = RecordBatch::try_new(schema, vec![dm]).unwrap();
698
699 let table = pretty_format_batches(&[batch]).unwrap().to_string();
700 let expected = vec![
701 "+------+", "| f |", "+------+", "| 101 |", "| |", "| 200 |", "| 3040 |",
702 "+------+",
703 ];
704
705 let actual: Vec<&str> = table.lines().collect();
706 assert_eq!(expected, actual, "Actual result:\n{table}");
707 }
708
709 #[test]
710 fn test_pretty_format_struct() {
711 let schema = Schema::new(vec![
712 Field::new_struct(
713 "c1",
714 vec![
715 Field::new("c11", DataType::Int32, true),
716 Field::new_struct(
717 "c12",
718 vec![Field::new("c121", DataType::Utf8, false)],
719 false,
720 ),
721 ],
722 false,
723 ),
724 Field::new("c2", DataType::Utf8, false),
725 ]);
726
727 let c1 = StructArray::from(vec![
728 (
729 Arc::new(Field::new("c11", DataType::Int32, true)),
730 Arc::new(Int32Array::from(vec![Some(1), None, Some(5)])) as ArrayRef,
731 ),
732 (
733 Arc::new(Field::new_struct(
734 "c12",
735 vec![Field::new("c121", DataType::Utf8, false)],
736 false,
737 )),
738 Arc::new(StructArray::from(vec![(
739 Arc::new(Field::new("c121", DataType::Utf8, false)),
740 Arc::new(StringArray::from(vec![Some("e"), Some("f"), Some("g")])) as ArrayRef,
741 )])) as ArrayRef,
742 ),
743 ]);
744 let c2 = StringArray::from(vec![Some("a"), Some("b"), Some("c")]);
745
746 let batch =
747 RecordBatch::try_new(Arc::new(schema), vec![Arc::new(c1), Arc::new(c2)]).unwrap();
748
749 let table = pretty_format_batches(&[batch]).unwrap().to_string();
750 let expected = vec![
751 "+--------------------------+----+",
752 "| c1 | c2 |",
753 "+--------------------------+----+",
754 "| {c11: 1, c12: {c121: e}} | a |",
755 "| {c11: , c12: {c121: f}} | b |",
756 "| {c11: 5, c12: {c121: g}} | c |",
757 "+--------------------------+----+",
758 ];
759
760 let actual: Vec<&str> = table.lines().collect();
761 assert_eq!(expected, actual, "Actual result:\n{table}");
762 }
763
764 #[test]
765 fn test_pretty_format_dense_union() {
766 let mut builder = UnionBuilder::new_dense();
767 builder.append::<Int32Type>("a", 1).unwrap();
768 builder.append::<Float64Type>("b", 3.2234).unwrap();
769 builder.append_null::<Float64Type>("b").unwrap();
770 builder.append_null::<Int32Type>("a").unwrap();
771 let union = builder.build().unwrap();
772
773 let schema = Schema::new(vec![Field::new_union(
774 "Teamsters",
775 vec![0, 1],
776 vec![
777 Field::new("a", DataType::Int32, false),
778 Field::new("b", DataType::Float64, false),
779 ],
780 UnionMode::Dense,
781 )]);
782
783 let batch = RecordBatch::try_new(Arc::new(schema), vec![Arc::new(union)]).unwrap();
784 let table = pretty_format_batches(&[batch]).unwrap().to_string();
785 let actual: Vec<&str> = table.lines().collect();
786 let expected = vec![
787 "+------------+",
788 "| Teamsters |",
789 "+------------+",
790 "| {a=1} |",
791 "| {b=3.2234} |",
792 "| {b=} |",
793 "| {a=} |",
794 "+------------+",
795 ];
796
797 assert_eq!(expected, actual);
798 }
799
800 #[test]
801 fn test_pretty_format_sparse_union() {
802 let mut builder = UnionBuilder::new_sparse();
803 builder.append::<Int32Type>("a", 1).unwrap();
804 builder.append::<Float64Type>("b", 3.2234).unwrap();
805 builder.append_null::<Float64Type>("b").unwrap();
806 builder.append_null::<Int32Type>("a").unwrap();
807 let union = builder.build().unwrap();
808
809 let schema = Schema::new(vec![Field::new_union(
810 "Teamsters",
811 vec![0, 1],
812 vec![
813 Field::new("a", DataType::Int32, false),
814 Field::new("b", DataType::Float64, false),
815 ],
816 UnionMode::Sparse,
817 )]);
818
819 let batch = RecordBatch::try_new(Arc::new(schema), vec![Arc::new(union)]).unwrap();
820 let table = pretty_format_batches(&[batch]).unwrap().to_string();
821 let actual: Vec<&str> = table.lines().collect();
822 let expected = vec![
823 "+------------+",
824 "| Teamsters |",
825 "+------------+",
826 "| {a=1} |",
827 "| {b=3.2234} |",
828 "| {b=} |",
829 "| {a=} |",
830 "+------------+",
831 ];
832
833 assert_eq!(expected, actual);
834 }
835
836 #[test]
837 fn test_pretty_format_nested_union() {
838 let mut builder = UnionBuilder::new_dense();
840 builder.append::<Int32Type>("b", 1).unwrap();
841 builder.append::<Float64Type>("c", 3.2234).unwrap();
842 builder.append_null::<Float64Type>("c").unwrap();
843 builder.append_null::<Int32Type>("b").unwrap();
844 builder.append_null::<Float64Type>("c").unwrap();
845 let inner = builder.build().unwrap();
846
847 let inner_field = Field::new_union(
848 "European Union",
849 vec![0, 1],
850 vec![
851 Field::new("b", DataType::Int32, false),
852 Field::new("c", DataType::Float64, false),
853 ],
854 UnionMode::Dense,
855 );
856
857 let a_array = Int32Array::from(vec![None, None, None, Some(1234), Some(23)]);
859 let type_ids = [1, 1, 0, 0, 1].into_iter().collect::<ScalarBuffer<i8>>();
860
861 let children = vec![Arc::new(a_array) as Arc<dyn Array>, Arc::new(inner)];
862
863 let union_fields = [
864 (0, Arc::new(Field::new("a", DataType::Int32, true))),
865 (1, Arc::new(inner_field.clone())),
866 ]
867 .into_iter()
868 .collect();
869
870 let outer = UnionArray::try_new(union_fields, type_ids, None, children).unwrap();
871
872 let schema = Schema::new(vec![Field::new_union(
873 "Teamsters",
874 vec![0, 1],
875 vec![Field::new("a", DataType::Int32, true), inner_field],
876 UnionMode::Sparse,
877 )]);
878
879 let batch = RecordBatch::try_new(Arc::new(schema), vec![Arc::new(outer)]).unwrap();
880 let table = pretty_format_batches(&[batch]).unwrap().to_string();
881 let actual: Vec<&str> = table.lines().collect();
882 let expected = vec![
883 "+-----------------------------+",
884 "| Teamsters |",
885 "+-----------------------------+",
886 "| {European Union={b=1}} |",
887 "| {European Union={c=3.2234}} |",
888 "| {a=} |",
889 "| {a=1234} |",
890 "| {European Union={c=}} |",
891 "+-----------------------------+",
892 ];
893 assert_eq!(expected, actual);
894 }
895
896 #[test]
897 fn test_writing_formatted_batches() {
898 let schema = Arc::new(Schema::new(vec![
900 Field::new("a", DataType::Utf8, true),
901 Field::new("b", DataType::Int32, true),
902 ]));
903
904 let batch = RecordBatch::try_new(
906 schema,
907 vec![
908 Arc::new(array::StringArray::from(vec![
909 Some("a"),
910 Some("b"),
911 None,
912 Some("d"),
913 ])),
914 Arc::new(array::Int32Array::from(vec![
915 Some(1),
916 None,
917 Some(10),
918 Some(100),
919 ])),
920 ],
921 )
922 .unwrap();
923
924 let mut buf = String::new();
925 write!(&mut buf, "{}", pretty_format_batches(&[batch]).unwrap()).unwrap();
926
927 let s = [
928 "+---+-----+",
929 "| a | b |",
930 "+---+-----+",
931 "| a | 1 |",
932 "| b | |",
933 "| | 10 |",
934 "| d | 100 |",
935 "+---+-----+",
936 ];
937 let expected = s.join("\n");
938 assert_eq!(expected, buf);
939 }
940
941 #[test]
942 fn test_float16_display() {
943 let values = vec![
944 Some(f16::from_f32(f32::NAN)),
945 Some(f16::from_f32(4.0)),
946 Some(f16::from_f32(f32::NEG_INFINITY)),
947 ];
948 let array = Arc::new(values.into_iter().collect::<Float16Array>()) as ArrayRef;
949
950 let schema = Arc::new(Schema::new(vec![Field::new(
951 "f16",
952 array.data_type().clone(),
953 true,
954 )]));
955
956 let batch = RecordBatch::try_new(schema, vec![array]).unwrap();
957
958 let table = pretty_format_batches(&[batch]).unwrap().to_string();
959
960 let expected = vec![
961 "+------+", "| f16 |", "+------+", "| NaN |", "| 4 |", "| -inf |", "+------+",
962 ];
963
964 let actual: Vec<&str> = table.lines().collect();
965 assert_eq!(expected, actual, "Actual result:\n{table}");
966 }
967
968 #[test]
969 fn test_pretty_format_interval_day_time() {
970 let arr = Arc::new(arrow_array::IntervalDayTimeArray::from(vec![
971 Some(IntervalDayTime::new(-1, -600_000)),
972 Some(IntervalDayTime::new(0, -1001)),
973 Some(IntervalDayTime::new(0, -1)),
974 Some(IntervalDayTime::new(0, 1)),
975 Some(IntervalDayTime::new(0, 10)),
976 Some(IntervalDayTime::new(0, 100)),
977 ]));
978
979 let schema = Arc::new(Schema::new(vec![Field::new(
980 "IntervalDayTime",
981 arr.data_type().clone(),
982 true,
983 )]));
984
985 let batch = RecordBatch::try_new(schema, vec![arr]).unwrap();
986
987 let table = pretty_format_batches(&[batch]).unwrap().to_string();
988
989 let expected = vec![
990 "+------------------+",
991 "| IntervalDayTime |",
992 "+------------------+",
993 "| -1 days -10 mins |",
994 "| -1.001 secs |",
995 "| -0.001 secs |",
996 "| 0.001 secs |",
997 "| 0.010 secs |",
998 "| 0.100 secs |",
999 "+------------------+",
1000 ];
1001
1002 let actual: Vec<&str> = table.lines().collect();
1003
1004 assert_eq!(expected, actual, "Actual result:\n{table}");
1005 }
1006
1007 #[test]
1008 fn test_pretty_format_interval_month_day_nano_array() {
1009 let arr = Arc::new(arrow_array::IntervalMonthDayNanoArray::from(vec![
1010 Some(IntervalMonthDayNano::new(-1, -1, -600_000_000_000)),
1011 Some(IntervalMonthDayNano::new(0, 0, -1_000_000_001)),
1012 Some(IntervalMonthDayNano::new(0, 0, -1)),
1013 Some(IntervalMonthDayNano::new(0, 0, 1)),
1014 Some(IntervalMonthDayNano::new(0, 0, 10)),
1015 Some(IntervalMonthDayNano::new(0, 0, 100)),
1016 Some(IntervalMonthDayNano::new(0, 0, 1_000)),
1017 Some(IntervalMonthDayNano::new(0, 0, 10_000)),
1018 Some(IntervalMonthDayNano::new(0, 0, 100_000)),
1019 Some(IntervalMonthDayNano::new(0, 0, 1_000_000)),
1020 Some(IntervalMonthDayNano::new(0, 0, 10_000_000)),
1021 Some(IntervalMonthDayNano::new(0, 0, 100_000_000)),
1022 Some(IntervalMonthDayNano::new(0, 0, 1_000_000_000)),
1023 ]));
1024
1025 let schema = Arc::new(Schema::new(vec![Field::new(
1026 "IntervalMonthDayNano",
1027 arr.data_type().clone(),
1028 true,
1029 )]));
1030
1031 let batch = RecordBatch::try_new(schema, vec![arr]).unwrap();
1032
1033 let table = pretty_format_batches(&[batch]).unwrap().to_string();
1034
1035 let expected = vec![
1036 "+--------------------------+",
1037 "| IntervalMonthDayNano |",
1038 "+--------------------------+",
1039 "| -1 mons -1 days -10 mins |",
1040 "| -1.000000001 secs |",
1041 "| -0.000000001 secs |",
1042 "| 0.000000001 secs |",
1043 "| 0.000000010 secs |",
1044 "| 0.000000100 secs |",
1045 "| 0.000001000 secs |",
1046 "| 0.000010000 secs |",
1047 "| 0.000100000 secs |",
1048 "| 0.001000000 secs |",
1049 "| 0.010000000 secs |",
1050 "| 0.100000000 secs |",
1051 "| 1.000000000 secs |",
1052 "+--------------------------+",
1053 ];
1054
1055 let actual: Vec<&str> = table.lines().collect();
1056
1057 assert_eq!(expected, actual, "Actual result:\n{table}");
1058 }
1059
1060 #[test]
1061 fn test_format_options() {
1062 let options = FormatOptions::default().with_null("null");
1063 let array = Int32Array::from(vec![Some(1), Some(2), None, Some(3), Some(4)]);
1064 let batch = RecordBatch::try_from_iter([("my_column_name", Arc::new(array) as _)]).unwrap();
1065
1066 let column = pretty_format_columns_with_options(
1067 "my_column_name",
1068 &[batch.column(0).clone()],
1069 &options,
1070 )
1071 .unwrap()
1072 .to_string();
1073
1074 let batch = pretty_format_batches_with_options(&[batch], &options)
1075 .unwrap()
1076 .to_string();
1077
1078 let expected = vec![
1079 "+----------------+",
1080 "| my_column_name |",
1081 "+----------------+",
1082 "| 1 |",
1083 "| 2 |",
1084 "| null |",
1085 "| 3 |",
1086 "| 4 |",
1087 "+----------------+",
1088 ];
1089
1090 let actual: Vec<&str> = column.lines().collect();
1091 assert_eq!(expected, actual, "Actual result:\n{column}");
1092
1093 let actual: Vec<&str> = batch.lines().collect();
1094 assert_eq!(expected, actual, "Actual result:\n{batch}");
1095 }
1096}