1use std::{fmt, io};
47
48use crate::basic::{
49 ConvertedType, DecimalType, GeographyType, GeometryType, IntType, LogicalType, TimeUnit,
50 Type as PhysicalType, VariantType,
51};
52use crate::file::metadata::{ColumnChunkMetaData, FileMetaData, ParquetMetaData, RowGroupMetaData};
53use crate::schema::types::Type;
54
55#[expect(unused_must_use)]
57pub fn print_parquet_metadata(out: &mut dyn io::Write, metadata: &ParquetMetaData) {
58 print_file_metadata(out, metadata.file_metadata());
59 writeln!(out);
60 writeln!(out);
61 writeln!(out, "num of row groups: {}", metadata.num_row_groups());
62 writeln!(out, "row groups:");
63 writeln!(out);
64 for (i, rg) in metadata.row_groups().iter().enumerate() {
65 writeln!(out, "row group {i}:");
66 print_dashes(out, 80);
67 print_row_group_metadata(out, rg);
68 }
69}
70
71#[expect(unused_must_use)]
73pub fn print_file_metadata(out: &mut dyn io::Write, file_metadata: &FileMetaData) {
74 writeln!(out, "version: {}", file_metadata.version());
75 writeln!(out, "num of rows: {}", file_metadata.num_rows());
76 if let Some(created_by) = file_metadata.created_by().as_ref() {
77 writeln!(out, "created by: {created_by}");
78 }
79 if let Some(metadata) = file_metadata.key_value_metadata() {
80 writeln!(out, "metadata:");
81 for kv in metadata {
82 writeln!(
83 out,
84 " {}: {}",
85 kv.key,
86 kv.value.as_ref().unwrap_or(&String::new())
87 );
88 }
89 }
90 let schema = file_metadata.schema();
91 print_schema(out, schema);
92}
93
94#[expect(unused_must_use)]
147pub fn print_schema(out: &mut dyn io::Write, tp: &Type) {
148 let mut s = String::new();
151 {
152 let mut printer = Printer::new(&mut s);
153 printer.print(tp);
154 }
155 writeln!(out, "{s}");
156}
157
158#[expect(unused_must_use)]
159fn print_row_group_metadata(out: &mut dyn io::Write, rg_metadata: &RowGroupMetaData) {
160 writeln!(out, "total byte size: {}", rg_metadata.total_byte_size());
161 writeln!(out, "num of rows: {}", rg_metadata.num_rows());
162 writeln!(out);
163 writeln!(out, "num of columns: {}", rg_metadata.num_columns());
164 writeln!(out, "columns: ");
165 for (i, cc) in rg_metadata.columns().iter().enumerate() {
166 writeln!(out);
167 writeln!(out, "column {i}:");
168 print_dashes(out, 80);
169 print_column_chunk_metadata(out, cc);
170 }
171}
172
173#[expect(unused_must_use)]
174fn print_column_chunk_metadata(out: &mut dyn io::Write, cc_metadata: &ColumnChunkMetaData) {
175 writeln!(out, "column type: {}", cc_metadata.column_type());
176 writeln!(out, "column path: {}", cc_metadata.column_path());
177 let encoding_strs: Vec<_> = cc_metadata.encodings().map(|e| format!("{e}")).collect();
178 writeln!(out, "encodings: {}", encoding_strs.join(" "));
179 let file_path_str = cc_metadata.file_path().unwrap_or("N/A");
180 writeln!(out, "file path: {file_path_str}");
181 writeln!(out, "file offset: {}", cc_metadata.file_offset());
182 writeln!(out, "num of values: {}", cc_metadata.num_values());
183 writeln!(out, "compression: {}", cc_metadata.compression_codec());
184 writeln!(
185 out,
186 "total compressed size (in bytes): {}",
187 cc_metadata.compressed_size()
188 );
189 writeln!(
190 out,
191 "total uncompressed size (in bytes): {}",
192 cc_metadata.uncompressed_size()
193 );
194 writeln!(out, "data page offset: {}", cc_metadata.data_page_offset());
195 let index_page_offset_str = match cc_metadata.index_page_offset() {
196 None => "N/A".to_owned(),
197 Some(ipo) => ipo.to_string(),
198 };
199 writeln!(out, "index page offset: {index_page_offset_str}");
200 let dict_page_offset_str = match cc_metadata.dictionary_page_offset() {
201 None => "N/A".to_owned(),
202 Some(dpo) => dpo.to_string(),
203 };
204 writeln!(out, "dictionary page offset: {dict_page_offset_str}");
205 let statistics_str = match cc_metadata.statistics() {
206 None => "N/A".to_owned(),
207 Some(stats) => stats.to_string(),
208 };
209 writeln!(out, "statistics: {statistics_str}");
210 let bloom_filter_offset_str = match cc_metadata.bloom_filter_offset() {
211 None => "N/A".to_owned(),
212 Some(bfo) => bfo.to_string(),
213 };
214 writeln!(out, "bloom filter offset: {bloom_filter_offset_str}");
215 let bloom_filter_length_str = match cc_metadata.bloom_filter_length() {
216 None => "N/A".to_owned(),
217 Some(bfo) => bfo.to_string(),
218 };
219 writeln!(out, "bloom filter length: {bloom_filter_length_str}");
220 let offset_index_offset_str = match cc_metadata.offset_index_offset() {
221 None => "N/A".to_owned(),
222 Some(oio) => oio.to_string(),
223 };
224 writeln!(out, "offset index offset: {offset_index_offset_str}");
225 let offset_index_length_str = match cc_metadata.offset_index_length() {
226 None => "N/A".to_owned(),
227 Some(oil) => oil.to_string(),
228 };
229 writeln!(out, "offset index length: {offset_index_length_str}");
230 let column_index_offset_str = match cc_metadata.column_index_offset() {
231 None => "N/A".to_owned(),
232 Some(cio) => cio.to_string(),
233 };
234 writeln!(out, "column index offset: {column_index_offset_str}");
235 let column_index_length_str = match cc_metadata.column_index_length() {
236 None => "N/A".to_owned(),
237 Some(cil) => cil.to_string(),
238 };
239 writeln!(out, "column index length: {column_index_length_str}");
240 writeln!(out);
241}
242
243#[expect(unused_must_use)]
244fn print_dashes(out: &mut dyn io::Write, num: i32) {
245 for _ in 0..num {
246 write!(out, "-");
247 }
248 writeln!(out);
249}
250
251const INDENT_WIDTH: i32 = 2;
252
253struct Printer<'a> {
255 output: &'a mut dyn fmt::Write,
256 indent: i32,
257}
258
259#[expect(unused_must_use)]
260impl<'a> Printer<'a> {
261 fn new(output: &'a mut dyn fmt::Write) -> Self {
262 Printer { output, indent: 0 }
263 }
264
265 fn print_indent(&mut self) {
266 for _ in 0..self.indent {
267 write!(self.output, " ");
268 }
269 }
270}
271
272#[inline]
273fn print_timeunit(unit: &TimeUnit) -> &str {
274 match unit {
275 TimeUnit::MILLIS => "MILLIS",
276 TimeUnit::MICROS => "MICROS",
277 TimeUnit::NANOS => "NANOS",
278 }
279}
280
281#[inline]
282fn print_logical_and_converted(
283 logical_type: Option<&LogicalType>,
284 converted_type: ConvertedType,
285 precision: i32,
286 scale: i32,
287) -> String {
288 match logical_type {
289 Some(logical_type) => match logical_type {
290 LogicalType::Integer(IntType {
291 bit_width,
292 is_signed,
293 }) => {
294 format!("INTEGER({bit_width},{is_signed})")
295 }
296 LogicalType::Decimal(DecimalType { scale, precision }) => {
297 format!("DECIMAL({precision},{scale})")
298 }
299 LogicalType::Timestamp(timestamp) => {
300 format!(
301 "TIMESTAMP({},{})",
302 print_timeunit(×tamp.unit),
303 timestamp.is_adjusted_to_u_t_c
304 )
305 }
306 LogicalType::Time(time) => {
307 format!(
308 "TIME({},{})",
309 print_timeunit(&time.unit),
310 time.is_adjusted_to_u_t_c
311 )
312 }
313 LogicalType::Date => "DATE".to_string(),
314 LogicalType::Bson => "BSON".to_string(),
315 LogicalType::Json => "JSON".to_string(),
316 LogicalType::String => "STRING".to_string(),
317 LogicalType::Uuid => "UUID".to_string(),
318 LogicalType::Enum => "ENUM".to_string(),
319 LogicalType::List => "LIST".to_string(),
320 LogicalType::Map => "MAP".to_string(),
321 LogicalType::Float16 => "FLOAT16".to_string(),
322 LogicalType::Variant(VariantType {
323 specification_version,
324 }) => format!("VARIANT({specification_version:?})"),
325 LogicalType::Geometry(GeometryType { crs }) => {
326 if let Some(crs) = crs {
327 format!("GEOMETRY({crs})")
328 } else {
329 "GEOMETRY".to_string()
330 }
331 }
332 LogicalType::Geography(GeographyType { crs, algorithm }) => {
333 let algorithm = algorithm.unwrap_or_default();
334 if let Some(crs) = crs {
335 format!("GEOGRAPHY({algorithm}, {crs})")
336 } else {
337 format!("GEOGRAPHY({algorithm})")
338 }
339 }
340 LogicalType::File => "FILE".to_string(),
341 LogicalType::Unknown => "UNKNOWN".to_string(),
342 LogicalType::_Unknown { field_id } => format!("_Unknown({field_id})"),
343 },
344 None => {
345 match converted_type {
347 ConvertedType::NONE => String::new(),
348 decimal @ ConvertedType::DECIMAL => {
349 let precision_scale = match (precision, scale) {
353 (p, s) if p > 0 && s > 0 => {
354 format!("({p},{s})")
355 }
356 (p, 0) if p > 0 => format!("({p})"),
357 _ => String::new(),
358 };
359 format!("{decimal}{precision_scale}")
360 }
361 other_converted_type => {
362 format!("{other_converted_type}")
363 }
364 }
365 }
366 }
367}
368
369#[expect(unused_must_use)]
370impl Printer<'_> {
371 pub fn print(&mut self, tp: &Type) {
372 self.print_indent();
373 match *tp {
374 Type::PrimitiveType {
375 ref basic_info,
376 physical_type,
377 type_length,
378 scale,
379 precision,
380 } => {
381 let phys_type_str = match physical_type {
382 PhysicalType::FIXED_LEN_BYTE_ARRAY => {
383 format!("{physical_type} ({type_length})")
385 }
386 _ => format!("{physical_type}"),
387 };
388 write!(
389 self.output,
390 "{} {} {}",
391 basic_info.repetition(),
392 phys_type_str,
393 basic_info.name()
394 );
395 if basic_info.has_id() {
396 write!(self.output, " [{}]", basic_info.id());
397 }
398 let logical_type_str = print_logical_and_converted(
401 basic_info.logical_type_ref(),
402 basic_info.converted_type(),
403 precision,
404 scale,
405 );
406 if !logical_type_str.is_empty() {
407 write!(self.output, " ({logical_type_str});");
408 } else {
409 write!(self.output, ";");
410 }
411 }
412 Type::GroupType {
413 ref basic_info,
414 ref fields,
415 } => {
416 if basic_info.has_repetition() {
417 write!(
418 self.output,
419 "{} group {} ",
420 basic_info.repetition(),
421 basic_info.name()
422 );
423 if basic_info.has_id() {
424 write!(self.output, "[{}] ", basic_info.id());
425 }
426 let logical_str = print_logical_and_converted(
427 basic_info.logical_type_ref(),
428 basic_info.converted_type(),
429 0,
430 0,
431 );
432 if !logical_str.is_empty() {
433 write!(self.output, "({logical_str}) ");
434 }
435 } else {
436 write!(self.output, "message {} ", basic_info.name());
437 if basic_info.has_id() {
438 write!(self.output, "[{}] ", basic_info.id());
439 }
440 }
441 writeln!(self.output, "{{");
442
443 self.indent += INDENT_WIDTH;
444 for c in fields {
445 self.print(c);
446 writeln!(self.output);
447 }
448 self.indent -= INDENT_WIDTH;
449 self.print_indent();
450 write!(self.output, "}}");
451 }
452 }
453 }
454}
455
456#[cfg(test)]
457mod tests {
458 use super::*;
459
460 use std::sync::Arc;
461
462 use crate::basic::{Repetition, Type as PhysicalType};
463 use crate::errors::Result;
464 use crate::schema::parser::parse_message_type;
465
466 fn assert_print_parse_message(message: Type) {
467 let mut s = String::new();
468 {
469 let mut p = Printer::new(&mut s);
470 p.print(&message);
471 }
472 println!("{s}");
473 let parsed = parse_message_type(&s).unwrap();
474 assert_eq!(message, parsed);
475 }
476
477 #[test]
478 fn test_print_primitive_type() {
479 let types_and_strings = vec![
480 (
481 Type::primitive_type_builder("field", PhysicalType::INT32)
482 .with_repetition(Repetition::REQUIRED)
483 .with_converted_type(ConvertedType::INT_32)
484 .build()
485 .unwrap(),
486 "REQUIRED INT32 field (INT_32);",
487 ),
488 (
489 Type::primitive_type_builder("field", PhysicalType::INT32)
490 .with_repetition(Repetition::REQUIRED)
491 .with_converted_type(ConvertedType::INT_32)
492 .with_id(Some(42))
493 .build()
494 .unwrap(),
495 "REQUIRED INT32 field [42] (INT_32);",
496 ),
497 (
498 Type::primitive_type_builder("field", PhysicalType::INT32)
499 .with_repetition(Repetition::REQUIRED)
500 .build()
501 .unwrap(),
502 "REQUIRED INT32 field;",
503 ),
504 (
505 Type::primitive_type_builder("field", PhysicalType::INT32)
506 .with_repetition(Repetition::REQUIRED)
507 .with_id(Some(42))
508 .build()
509 .unwrap(),
510 "REQUIRED INT32 field [42];",
511 ),
512 ];
513 types_and_strings.into_iter().for_each(|(field, expected)| {
514 let mut s = String::new();
515 {
516 let mut p = Printer::new(&mut s);
517 p.print(&field);
518 }
519 assert_eq!(&s, expected)
520 });
521 }
522
523 #[inline]
524 fn build_primitive_type(
525 name: &str,
526 id: Option<i32>,
527 physical_type: PhysicalType,
528 logical_type: Option<LogicalType>,
529 converted_type: ConvertedType,
530 repetition: Repetition,
531 ) -> Result<Type> {
532 Type::primitive_type_builder(name, physical_type)
533 .with_id(id)
534 .with_repetition(repetition)
535 .with_logical_type(logical_type)
536 .with_converted_type(converted_type)
537 .build()
538 }
539
540 #[test]
541 fn test_print_logical_types() {
542 let types_and_strings = vec![
543 (
544 build_primitive_type(
545 "field",
546 None,
547 PhysicalType::INT32,
548 Some(LogicalType::integer(32, true)),
549 ConvertedType::NONE,
550 Repetition::REQUIRED,
551 )
552 .unwrap(),
553 "REQUIRED INT32 field (INTEGER(32,true));",
554 ),
555 (
556 build_primitive_type(
557 "field",
558 None,
559 PhysicalType::INT32,
560 Some(LogicalType::integer(8, false)),
561 ConvertedType::NONE,
562 Repetition::OPTIONAL,
563 )
564 .unwrap(),
565 "OPTIONAL INT32 field (INTEGER(8,false));",
566 ),
567 (
568 build_primitive_type(
569 "field",
570 None,
571 PhysicalType::INT32,
572 Some(LogicalType::integer(16, true)),
573 ConvertedType::INT_16,
574 Repetition::REPEATED,
575 )
576 .unwrap(),
577 "REPEATED INT32 field (INTEGER(16,true));",
578 ),
579 (
580 build_primitive_type(
581 "field",
582 Some(42),
583 PhysicalType::INT32,
584 Some(LogicalType::integer(16, true)),
585 ConvertedType::INT_16,
586 Repetition::REPEATED,
587 )
588 .unwrap(),
589 "REPEATED INT32 field [42] (INTEGER(16,true));",
590 ),
591 (
592 build_primitive_type(
593 "field",
594 None,
595 PhysicalType::INT64,
596 None,
597 ConvertedType::NONE,
598 Repetition::REPEATED,
599 )
600 .unwrap(),
601 "REPEATED INT64 field;",
602 ),
603 (
604 build_primitive_type(
605 "field",
606 None,
607 PhysicalType::FLOAT,
608 None,
609 ConvertedType::NONE,
610 Repetition::REQUIRED,
611 )
612 .unwrap(),
613 "REQUIRED FLOAT field;",
614 ),
615 (
616 build_primitive_type(
617 "booleans",
618 None,
619 PhysicalType::BOOLEAN,
620 None,
621 ConvertedType::NONE,
622 Repetition::OPTIONAL,
623 )
624 .unwrap(),
625 "OPTIONAL BOOLEAN booleans;",
626 ),
627 (
628 build_primitive_type(
629 "booleans",
630 Some(42),
631 PhysicalType::BOOLEAN,
632 None,
633 ConvertedType::NONE,
634 Repetition::OPTIONAL,
635 )
636 .unwrap(),
637 "OPTIONAL BOOLEAN booleans [42];",
638 ),
639 (
640 build_primitive_type(
641 "field",
642 None,
643 PhysicalType::INT64,
644 Some(LogicalType::timestamp(true, TimeUnit::MILLIS)),
645 ConvertedType::NONE,
646 Repetition::REQUIRED,
647 )
648 .unwrap(),
649 "REQUIRED INT64 field (TIMESTAMP(MILLIS,true));",
650 ),
651 (
652 build_primitive_type(
653 "field",
654 None,
655 PhysicalType::INT32,
656 Some(LogicalType::Date),
657 ConvertedType::NONE,
658 Repetition::OPTIONAL,
659 )
660 .unwrap(),
661 "OPTIONAL INT32 field (DATE);",
662 ),
663 (
664 build_primitive_type(
665 "field",
666 None,
667 PhysicalType::INT32,
668 Some(LogicalType::time(false, TimeUnit::MILLIS)),
669 ConvertedType::TIME_MILLIS,
670 Repetition::REQUIRED,
671 )
672 .unwrap(),
673 "REQUIRED INT32 field (TIME(MILLIS,false));",
674 ),
675 (
676 build_primitive_type(
677 "field",
678 Some(42),
679 PhysicalType::INT32,
680 Some(LogicalType::time(false, TimeUnit::MILLIS)),
681 ConvertedType::TIME_MILLIS,
682 Repetition::REQUIRED,
683 )
684 .unwrap(),
685 "REQUIRED INT32 field [42] (TIME(MILLIS,false));",
686 ),
687 (
688 build_primitive_type(
689 "field",
690 None,
691 PhysicalType::BYTE_ARRAY,
692 None,
693 ConvertedType::NONE,
694 Repetition::REQUIRED,
695 )
696 .unwrap(),
697 "REQUIRED BYTE_ARRAY field;",
698 ),
699 (
700 build_primitive_type(
701 "field",
702 Some(42),
703 PhysicalType::BYTE_ARRAY,
704 None,
705 ConvertedType::NONE,
706 Repetition::REQUIRED,
707 )
708 .unwrap(),
709 "REQUIRED BYTE_ARRAY field [42];",
710 ),
711 (
712 build_primitive_type(
713 "field",
714 None,
715 PhysicalType::BYTE_ARRAY,
716 None,
717 ConvertedType::UTF8,
718 Repetition::REQUIRED,
719 )
720 .unwrap(),
721 "REQUIRED BYTE_ARRAY field (UTF8);",
722 ),
723 (
724 build_primitive_type(
725 "field",
726 None,
727 PhysicalType::BYTE_ARRAY,
728 Some(LogicalType::Json),
729 ConvertedType::JSON,
730 Repetition::REQUIRED,
731 )
732 .unwrap(),
733 "REQUIRED BYTE_ARRAY field (JSON);",
734 ),
735 (
736 build_primitive_type(
737 "field",
738 None,
739 PhysicalType::BYTE_ARRAY,
740 Some(LogicalType::Bson),
741 ConvertedType::BSON,
742 Repetition::REQUIRED,
743 )
744 .unwrap(),
745 "REQUIRED BYTE_ARRAY field (BSON);",
746 ),
747 (
748 build_primitive_type(
749 "field",
750 None,
751 PhysicalType::BYTE_ARRAY,
752 Some(LogicalType::String),
753 ConvertedType::NONE,
754 Repetition::REQUIRED,
755 )
756 .unwrap(),
757 "REQUIRED BYTE_ARRAY field (STRING);",
758 ),
759 (
760 build_primitive_type(
761 "field",
762 Some(42),
763 PhysicalType::BYTE_ARRAY,
764 Some(LogicalType::String),
765 ConvertedType::NONE,
766 Repetition::REQUIRED,
767 )
768 .unwrap(),
769 "REQUIRED BYTE_ARRAY field [42] (STRING);",
770 ),
771 (
772 build_primitive_type(
773 "field",
774 None,
775 PhysicalType::BYTE_ARRAY,
776 Some(LogicalType::geometry(None)),
777 ConvertedType::NONE,
778 Repetition::REQUIRED,
779 )
780 .unwrap(),
781 "REQUIRED BYTE_ARRAY field (GEOMETRY);",
782 ),
783 (
784 build_primitive_type(
785 "field",
786 None,
787 PhysicalType::BYTE_ARRAY,
788 Some(LogicalType::geometry(Some("non-missing CRS".to_string()))),
789 ConvertedType::NONE,
790 Repetition::REQUIRED,
791 )
792 .unwrap(),
793 "REQUIRED BYTE_ARRAY field (GEOMETRY(non-missing CRS));",
794 ),
795 (
796 build_primitive_type(
797 "field",
798 None,
799 PhysicalType::BYTE_ARRAY,
800 Some(LogicalType::geography(None, Some(Default::default()))),
801 ConvertedType::NONE,
802 Repetition::REQUIRED,
803 )
804 .unwrap(),
805 "REQUIRED BYTE_ARRAY field (GEOGRAPHY(SPHERICAL));",
806 ),
807 (
808 build_primitive_type(
809 "field",
810 None,
811 PhysicalType::BYTE_ARRAY,
812 Some(LogicalType::geography(
813 Some("non-missing CRS".to_string()),
814 Some(Default::default()),
815 )),
816 ConvertedType::NONE,
817 Repetition::REQUIRED,
818 )
819 .unwrap(),
820 "REQUIRED BYTE_ARRAY field (GEOGRAPHY(SPHERICAL, non-missing CRS));",
821 ),
822 ];
823
824 types_and_strings.into_iter().for_each(|(field, expected)| {
825 let mut s = String::new();
826 {
827 let mut p = Printer::new(&mut s);
828 p.print(&field);
829 }
830 assert_eq!(&s, expected)
831 });
832 }
833
834 #[inline]
835 fn decimal_length_from_precision(precision: usize) -> i32 {
836 let max_val = 10.0_f64.powi(precision as i32) - 1.0;
837 let bits_unsigned = max_val.log2().ceil();
838 let bits_signed = bits_unsigned + 1.0;
839 (bits_signed / 8.0).ceil() as i32
840 }
841
842 #[test]
843 fn test_print_flba_logical_types() {
844 let types_and_strings = vec![
845 (
846 Type::primitive_type_builder("field", PhysicalType::FIXED_LEN_BYTE_ARRAY)
847 .with_logical_type(None)
848 .with_converted_type(ConvertedType::INTERVAL)
849 .with_length(12)
850 .with_repetition(Repetition::REQUIRED)
851 .build()
852 .unwrap(),
853 "REQUIRED FIXED_LEN_BYTE_ARRAY (12) field (INTERVAL);",
854 ),
855 (
856 Type::primitive_type_builder("field", PhysicalType::FIXED_LEN_BYTE_ARRAY)
857 .with_logical_type(Some(LogicalType::Uuid))
858 .with_length(16)
859 .with_repetition(Repetition::REQUIRED)
860 .build()
861 .unwrap(),
862 "REQUIRED FIXED_LEN_BYTE_ARRAY (16) field (UUID);",
863 ),
864 (
865 Type::primitive_type_builder("decimal", PhysicalType::FIXED_LEN_BYTE_ARRAY)
866 .with_logical_type(Some(LogicalType::decimal(20, 32)))
867 .with_precision(32)
868 .with_scale(20)
869 .with_length(decimal_length_from_precision(32))
870 .with_repetition(Repetition::REPEATED)
871 .build()
872 .unwrap(),
873 "REPEATED FIXED_LEN_BYTE_ARRAY (14) decimal (DECIMAL(32,20));",
874 ),
875 (
876 Type::primitive_type_builder("decimal", PhysicalType::FIXED_LEN_BYTE_ARRAY)
877 .with_converted_type(ConvertedType::DECIMAL)
878 .with_precision(19)
879 .with_scale(4)
880 .with_length(decimal_length_from_precision(19))
881 .with_repetition(Repetition::OPTIONAL)
882 .build()
883 .unwrap(),
884 "OPTIONAL FIXED_LEN_BYTE_ARRAY (9) decimal (DECIMAL(19,4));",
885 ),
886 (
887 Type::primitive_type_builder("float16", PhysicalType::FIXED_LEN_BYTE_ARRAY)
888 .with_logical_type(Some(LogicalType::Float16))
889 .with_length(2)
890 .with_repetition(Repetition::REQUIRED)
891 .build()
892 .unwrap(),
893 "REQUIRED FIXED_LEN_BYTE_ARRAY (2) float16 (FLOAT16);",
894 ),
895 ];
896
897 types_and_strings.into_iter().for_each(|(field, expected)| {
898 let mut s = String::new();
899 {
900 let mut p = Printer::new(&mut s);
901 p.print(&field);
902 }
903 assert_eq!(&s, expected)
904 });
905 }
906
907 #[test]
908 fn test_print_schema_documentation() {
909 let mut s = String::new();
910 {
911 let mut p = Printer::new(&mut s);
912 let field_a = Type::primitive_type_builder("a", PhysicalType::BYTE_ARRAY)
913 .with_id(Some(42))
914 .with_converted_type(ConvertedType::UTF8)
915 .build()
916 .unwrap();
917
918 let field_b = Type::primitive_type_builder("b", PhysicalType::INT32)
919 .with_repetition(Repetition::REQUIRED)
920 .build()
921 .unwrap();
922
923 let field_d = Type::primitive_type_builder("d", PhysicalType::INT64)
924 .with_id(Some(99))
925 .build()
926 .unwrap();
927
928 let field_c = Type::group_type_builder("c")
929 .with_id(Some(43))
930 .with_fields(vec![Arc::new(field_d)])
931 .build()
932 .unwrap();
933
934 let schema = Type::group_type_builder("schema")
935 .with_fields(vec![
936 Arc::new(field_a),
937 Arc::new(field_b),
938 Arc::new(field_c),
939 ])
940 .build()
941 .unwrap();
942 p.print(&schema);
943 }
944 let expected = "message schema {
945 OPTIONAL BYTE_ARRAY a [42] (UTF8);
946 REQUIRED INT32 b;
947 message c [43] {
948 OPTIONAL INT64 d [99];
949 }
950}";
951 assert_eq!(&mut s, expected);
952 }
953
954 #[test]
955 fn test_print_group_type() {
956 let mut s = String::new();
957 {
958 let mut p = Printer::new(&mut s);
959 let f1 = Type::primitive_type_builder("f1", PhysicalType::INT32)
960 .with_repetition(Repetition::REQUIRED)
961 .with_converted_type(ConvertedType::INT_32)
962 .build();
963 let f2 = Type::primitive_type_builder("f2", PhysicalType::BYTE_ARRAY)
964 .with_converted_type(ConvertedType::UTF8)
965 .build();
966 let f3 = Type::primitive_type_builder("f3", PhysicalType::BYTE_ARRAY)
967 .with_logical_type(Some(LogicalType::String))
968 .build();
969 let f4 = Type::primitive_type_builder("f4", PhysicalType::FIXED_LEN_BYTE_ARRAY)
970 .with_repetition(Repetition::REPEATED)
971 .with_converted_type(ConvertedType::INTERVAL)
972 .with_length(12)
973 .build();
974
975 let struct_fields = vec![
976 Arc::new(f1.unwrap()),
977 Arc::new(f2.unwrap()),
978 Arc::new(f3.unwrap()),
979 ];
980 let field = Type::group_type_builder("field")
981 .with_repetition(Repetition::OPTIONAL)
982 .with_fields(struct_fields)
983 .build()
984 .unwrap();
985
986 let fields = vec![Arc::new(field), Arc::new(f4.unwrap())];
987 let message = Type::group_type_builder("schema")
988 .with_fields(fields)
989 .build()
990 .unwrap();
991 p.print(&message);
992 }
993 let expected = "message schema {
994 OPTIONAL group field {
995 REQUIRED INT32 f1 (INT_32);
996 OPTIONAL BYTE_ARRAY f2 (UTF8);
997 OPTIONAL BYTE_ARRAY f3 (STRING);
998 }
999 REPEATED FIXED_LEN_BYTE_ARRAY (12) f4 (INTERVAL);
1000}";
1001 assert_eq!(&mut s, expected);
1002 }
1003
1004 #[test]
1005 fn test_print_group_type_with_ids() {
1006 let mut s = String::new();
1007 {
1008 let mut p = Printer::new(&mut s);
1009 let f1 = Type::primitive_type_builder("f1", PhysicalType::INT32)
1010 .with_repetition(Repetition::REQUIRED)
1011 .with_converted_type(ConvertedType::INT_32)
1012 .with_id(Some(0))
1013 .build();
1014 let f2 = Type::primitive_type_builder("f2", PhysicalType::BYTE_ARRAY)
1015 .with_converted_type(ConvertedType::UTF8)
1016 .with_id(Some(1))
1017 .build();
1018 let f3 = Type::primitive_type_builder("f3", PhysicalType::BYTE_ARRAY)
1019 .with_logical_type(Some(LogicalType::String))
1020 .with_id(Some(1))
1021 .build();
1022 let f4 = Type::primitive_type_builder("f4", PhysicalType::FIXED_LEN_BYTE_ARRAY)
1023 .with_repetition(Repetition::REPEATED)
1024 .with_converted_type(ConvertedType::INTERVAL)
1025 .with_length(12)
1026 .with_id(Some(2))
1027 .build();
1028
1029 let struct_fields = vec![
1030 Arc::new(f1.unwrap()),
1031 Arc::new(f2.unwrap()),
1032 Arc::new(f3.unwrap()),
1033 ];
1034 let field = Type::group_type_builder("field")
1035 .with_repetition(Repetition::OPTIONAL)
1036 .with_fields(struct_fields)
1037 .with_id(Some(1))
1038 .build()
1039 .unwrap();
1040
1041 let fields = vec![Arc::new(field), Arc::new(f4.unwrap())];
1042 let message = Type::group_type_builder("schema")
1043 .with_fields(fields)
1044 .with_id(Some(2))
1045 .build()
1046 .unwrap();
1047 p.print(&message);
1048 }
1049 let expected = "message schema [2] {
1050 OPTIONAL group field [1] {
1051 REQUIRED INT32 f1 [0] (INT_32);
1052 OPTIONAL BYTE_ARRAY f2 [1] (UTF8);
1053 OPTIONAL BYTE_ARRAY f3 [1] (STRING);
1054 }
1055 REPEATED FIXED_LEN_BYTE_ARRAY (12) f4 [2] (INTERVAL);
1056}";
1057 assert_eq!(&mut s, expected);
1058 }
1059
1060 #[test]
1061 fn test_print_and_parse_primitive() {
1062 let a2 = Type::primitive_type_builder("a2", PhysicalType::BYTE_ARRAY)
1063 .with_repetition(Repetition::REPEATED)
1064 .with_converted_type(ConvertedType::UTF8)
1065 .build()
1066 .unwrap();
1067
1068 let a1 = Type::group_type_builder("a1")
1069 .with_repetition(Repetition::OPTIONAL)
1070 .with_logical_type(Some(LogicalType::List))
1071 .with_converted_type(ConvertedType::LIST)
1072 .with_fields(vec![Arc::new(a2)])
1073 .build()
1074 .unwrap();
1075
1076 let b3 = Type::primitive_type_builder("b3", PhysicalType::INT32)
1077 .with_repetition(Repetition::OPTIONAL)
1078 .build()
1079 .unwrap();
1080
1081 let b4 = Type::primitive_type_builder("b4", PhysicalType::DOUBLE)
1082 .with_repetition(Repetition::OPTIONAL)
1083 .build()
1084 .unwrap();
1085
1086 let b2 = Type::group_type_builder("b2")
1087 .with_repetition(Repetition::REPEATED)
1088 .with_converted_type(ConvertedType::NONE)
1089 .with_fields(vec![Arc::new(b3), Arc::new(b4)])
1090 .build()
1091 .unwrap();
1092
1093 let b1 = Type::group_type_builder("b1")
1094 .with_repetition(Repetition::OPTIONAL)
1095 .with_logical_type(Some(LogicalType::List))
1096 .with_converted_type(ConvertedType::LIST)
1097 .with_fields(vec![Arc::new(b2)])
1098 .build()
1099 .unwrap();
1100
1101 let a0 = Type::group_type_builder("a0")
1102 .with_repetition(Repetition::REQUIRED)
1103 .with_fields(vec![Arc::new(a1), Arc::new(b1)])
1104 .build()
1105 .unwrap();
1106
1107 let message = Type::group_type_builder("root")
1108 .with_fields(vec![Arc::new(a0)])
1109 .build()
1110 .unwrap();
1111
1112 assert_print_parse_message(message);
1113 }
1114
1115 #[test]
1116 fn test_print_and_parse_nested() {
1117 let f1 = Type::primitive_type_builder("f1", PhysicalType::INT32)
1118 .with_repetition(Repetition::REQUIRED)
1119 .with_converted_type(ConvertedType::INT_32)
1120 .build()
1121 .unwrap();
1122
1123 let f2 = Type::primitive_type_builder("f2", PhysicalType::BYTE_ARRAY)
1124 .with_repetition(Repetition::OPTIONAL)
1125 .with_converted_type(ConvertedType::UTF8)
1126 .build()
1127 .unwrap();
1128
1129 let field = Type::group_type_builder("field")
1130 .with_repetition(Repetition::OPTIONAL)
1131 .with_fields(vec![Arc::new(f1), Arc::new(f2)])
1132 .build()
1133 .unwrap();
1134
1135 let f3 = Type::primitive_type_builder("f3", PhysicalType::FIXED_LEN_BYTE_ARRAY)
1136 .with_repetition(Repetition::REPEATED)
1137 .with_converted_type(ConvertedType::INTERVAL)
1138 .with_length(12)
1139 .build()
1140 .unwrap();
1141
1142 let message = Type::group_type_builder("schema")
1143 .with_fields(vec![Arc::new(field), Arc::new(f3)])
1144 .build()
1145 .unwrap();
1146
1147 assert_print_parse_message(message);
1148 }
1149
1150 #[test]
1151 fn test_print_and_parse_decimal() {
1152 let f1 = Type::primitive_type_builder("f1", PhysicalType::INT32)
1153 .with_repetition(Repetition::OPTIONAL)
1154 .with_logical_type(Some(LogicalType::decimal(2, 9)))
1155 .with_converted_type(ConvertedType::DECIMAL)
1156 .with_precision(9)
1157 .with_scale(2)
1158 .build()
1159 .unwrap();
1160
1161 let f2 = Type::primitive_type_builder("f2", PhysicalType::INT32)
1162 .with_repetition(Repetition::OPTIONAL)
1163 .with_logical_type(Some(LogicalType::decimal(0, 9)))
1164 .with_converted_type(ConvertedType::DECIMAL)
1165 .with_precision(9)
1166 .with_scale(0)
1167 .build()
1168 .unwrap();
1169
1170 let message = Type::group_type_builder("schema")
1171 .with_fields(vec![Arc::new(f1), Arc::new(f2)])
1172 .build()
1173 .unwrap();
1174
1175 assert_print_parse_message(message);
1176 }
1177
1178 #[test]
1179 fn test_print_file_type() {
1180 let mut s = String::new();
1181 {
1182 let mut p = Printer::new(&mut s);
1183 let uri_field = Arc::new(
1184 Type::primitive_type_builder("uri", PhysicalType::BYTE_ARRAY)
1185 .with_repetition(Repetition::OPTIONAL)
1186 .with_logical_type(Some(LogicalType::String))
1187 .build()
1188 .unwrap(),
1189 );
1190 let size_field = Arc::new(
1191 Type::primitive_type_builder("size", PhysicalType::INT64)
1192 .with_repetition(Repetition::OPTIONAL)
1193 .build()
1194 .unwrap(),
1195 );
1196 let file_field = Type::group_type_builder("f")
1197 .with_repetition(Repetition::REQUIRED)
1198 .with_logical_type(Some(LogicalType::File))
1199 .with_fields(vec![uri_field, size_field])
1200 .build()
1201 .unwrap();
1202 let message = Type::group_type_builder("schema")
1203 .with_fields(vec![Arc::new(file_field)])
1204 .build()
1205 .unwrap();
1206 p.print(&message);
1207 }
1208 let expected = "message schema {
1209 REQUIRED group f (FILE) {
1210 OPTIONAL BYTE_ARRAY uri (STRING);
1211 OPTIONAL INT64 size;
1212 }
1213}";
1214 assert_eq!(&mut s, expected);
1215 }
1216}