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