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