Skip to main content

parquet/
basic.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18//! Contains Rust mappings for Thrift definition. This module contains only mappings for thrift
19//! enums and unions. Thrift structs are handled elsewhere.
20//! Refer to [`parquet.thrift`](https://github.com/apache/parquet-format/blob/master/src/main/thrift/parquet.thrift)
21//! file to see raw definitions.
22
23use std::io::Write;
24use std::str::FromStr;
25use std::{fmt, str};
26
27pub use crate::compression::{BrotliLevel, GzipLevel, ZstdLevel};
28use crate::file::metadata::HeapSize;
29use crate::parquet_thrift::{
30    ElementType, FieldType, ReadThrift, ThriftCompactInputProtocol, ThriftCompactOutputProtocol,
31    WriteThrift, WriteThriftField, validate_list_type,
32};
33use crate::{
34    thrift_enum, thrift_struct, thrift_union_all_empty, thrift_union_with_unknown,
35    write_thrift_field,
36};
37
38use crate::errors::{ParquetError, Result};
39
40// ----------------------------------------------------------------------
41// Types from the Thrift definition
42
43// ----------------------------------------------------------------------
44// Mirrors thrift enum `Type`
45
46thrift_enum!(
47/// Types supported by Parquet.
48///
49/// These physical types are intended to be used in combination with the encodings to
50/// control the on disk storage format.
51/// For example INT16 is not included as a type since a good encoding of INT32
52/// would handle this.
53enum Type {
54  BOOLEAN = 0;
55  INT32 = 1;
56  INT64 = 2;
57  INT96 = 3;  // deprecated, only used by legacy implementations.
58  FLOAT = 4;
59  DOUBLE = 5;
60  BYTE_ARRAY = 6;
61  FIXED_LEN_BYTE_ARRAY = 7;
62}
63);
64
65// ----------------------------------------------------------------------
66// Mirrors thrift enum `ConvertedType`
67
68// TODO(ets): Adding the `NONE` variant to this enum is a bit awkward. We should
69// look into removing it and using `Option<ConvertedType>` instead.
70thrift_enum!(
71/// Common types (converted types) used by frameworks when using Parquet.
72///
73/// This helps map between types in those frameworks to the base types in Parquet.
74/// This is only metadata and not needed to read or write the data.
75///
76/// This struct was renamed from `LogicalType` in version 4.0.0.
77/// If targeting Parquet format 2.4.0 or above, please use [LogicalType] instead.
78enum ConvertedType {
79  /// Not defined in the spec, used internally to indicate no type conversion
80  NONE = -1;
81
82  /// A BYTE_ARRAY actually contains UTF8 encoded chars.
83  UTF8 = 0;
84
85  /// A map is converted as an optional field containing a repeated key/value pair.
86  MAP = 1;
87
88  /// A key/value pair is converted into a group of two fields.
89  MAP_KEY_VALUE = 2;
90
91  /// A list is converted into an optional field containing a repeated field for its
92  /// values.
93  LIST = 3;
94
95  /// An enum is converted into a BYTE_ARRAY field
96  ENUM = 4;
97
98  /// A decimal value.
99  ///
100  /// This may be used to annotate BYTE_ARRAY or FIXED_LEN_BYTE_ARRAY primitive
101  /// types. The underlying byte array stores the unscaled value encoded as two's
102  /// complement using big-endian byte order (the most significant byte is the
103  /// zeroth element). The value of the decimal is the value * 10^{-scale}.
104  ///
105  /// This must be accompanied by a (maximum) precision and a scale in the
106  /// SchemaElement. The precision specifies the number of digits in the decimal
107  /// and the scale stores the location of the decimal point. For example 1.23
108  /// would have precision 3 (3 total digits) and scale 2 (the decimal point is
109  /// 2 digits over).
110  DECIMAL = 5;
111
112  /// A date stored as days since Unix epoch, encoded as the INT32 physical type.
113  DATE = 6;
114
115  /// The total number of milliseconds since midnight. The value is stored as an INT32
116  /// physical type.
117  TIME_MILLIS = 7;
118
119  /// The total number of microseconds since midnight. The value is stored as an INT64
120  /// physical type.
121  TIME_MICROS = 8;
122
123  /// Date and time recorded as milliseconds since the Unix epoch.
124  /// Recorded as a physical type of INT64.
125  TIMESTAMP_MILLIS = 9;
126
127  /// Date and time recorded as microseconds since the Unix epoch.
128  /// The value is stored as an INT64 physical type.
129  TIMESTAMP_MICROS = 10;
130
131  /// An unsigned 8 bit integer value stored as INT32 physical type.
132  UINT_8 = 11;
133
134  /// An unsigned 16 bit integer value stored as INT32 physical type.
135  UINT_16 = 12;
136
137  /// An unsigned 32 bit integer value stored as INT32 physical type.
138  UINT_32 = 13;
139
140  /// An unsigned 64 bit integer value stored as INT64 physical type.
141  UINT_64 = 14;
142
143  /// A signed 8 bit integer value stored as INT32 physical type.
144  INT_8 = 15;
145
146  /// A signed 16 bit integer value stored as INT32 physical type.
147  INT_16 = 16;
148
149  /// A signed 32 bit integer value stored as INT32 physical type.
150  INT_32 = 17;
151
152  /// A signed 64 bit integer value stored as INT64 physical type.
153  INT_64 = 18;
154
155  /// A JSON document embedded within a single UTF8 column.
156  JSON = 19;
157
158   /// A BSON document embedded within a single BINARY column.
159  BSON = 20;
160
161  /// An interval of time
162  ///
163  /// This type annotates data stored as a FIXED_LEN_BYTE_ARRAY of length 12.
164  /// This data is composed of three separate little endian unsigned integers.
165  /// Each stores a component of a duration of time. The first integer identifies
166  /// the number of months associated with the duration, the second identifies
167  /// the number of days associated with the duration and the third identifies
168  /// the number of milliseconds associated with the provided duration.
169  /// This duration of time is independent of any particular timezone or date.
170  INTERVAL = 21;
171}
172);
173
174// ----------------------------------------------------------------------
175// Mirrors thrift union `TimeUnit`
176
177thrift_union_all_empty!(
178/// Time unit for `Time` and `Timestamp` logical types.
179union TimeUnit {
180  1: MilliSeconds MILLIS
181  2: MicroSeconds MICROS
182  3: NanoSeconds NANOS
183}
184);
185
186// ----------------------------------------------------------------------
187// Mirrors thrift union `LogicalType`
188
189thrift_struct!(
190pub struct DecimalType {
191  /// The number of digits in the decimal.
192  1: required i32 scale
193  /// The location of the decimal point.
194  2: required i32 precision
195}
196);
197
198thrift_struct!(
199pub struct TimestampType {
200  /// Whether the timestamp is adjusted to UTC.
201  1: required bool is_adjusted_to_u_t_c
202  /// The unit of time.
203  2: required TimeUnit unit
204}
205);
206
207/// Identical to [`TimestampType`]
208pub use TimestampType as TimeType;
209
210thrift_struct!(
211pub struct IntType {
212  /// The number of bits in the integer.
213  1: required i8 bit_width
214  /// Whether the integer is signed.
215  2: required bool is_signed
216}
217);
218
219thrift_struct!(
220pub struct VariantType {
221  /// The version of the variant specification that the variant was
222  /// written with.
223  1: optional i8 specification_version
224}
225);
226
227thrift_struct!(
228pub struct GeometryType {
229  /// A custom CRS. If unset the CRS `OGC:CRS84` should be used, which means that the geometries
230  /// must be stored in longitude, latitude based on the WGS84 datum.
231  1: optional string crs;
232}
233);
234
235thrift_struct!(
236pub struct GeographyType {
237  /// A custom CRS. If unset the CRS `OGC:CRS84` should be used.
238  1: optional string crs;
239  /// An optional algorithm can be set to correctly interpret edges interpolation
240  /// of the geometries. If unset, the `SPHERICAL` algorithm should be used.
241  2: optional EdgeInterpolationAlgorithm algorithm;
242}
243);
244
245impl GeographyType {
246    /// Accessor for the `GeographyType::algorithm` field. If this field is not set, this
247    /// function returns the default value (currently [`EdgeInterpolationAlgorithm::SPHERICAL`]
248    /// per the Parquet [specification]).
249    ///
250    /// [specification]: https://github.com/apache/parquet-format/blob/master/LogicalTypes.md#geography
251    pub fn algorithm(&self) -> Option<EdgeInterpolationAlgorithm> {
252        self.algorithm.or(Some(Default::default()))
253    }
254}
255
256thrift_union_with_unknown!(
257/// Logical types used by version 2.4.0+ of the Parquet format.
258///
259/// This is an *entirely new* struct as of version
260/// 4.0.0. The struct previously named `LogicalType` was renamed to
261/// [`ConvertedType`]. Please see the README.md for more details.
262union LogicalType {
263   /// A UTF8 encoded string.
264   1:  String
265   /// A map of key-value pairs.
266   2:  Map
267   /// A list of elements.
268   3:  List
269   /// A set of predefined values.
270   4:  Enum
271   /// A decimal value with a specified scale and precision.
272   5:  (DecimalType) Decimal
273   /// A date stored as days since Unix epoch.
274   6:  Date
275   /// A time stored as [`TimeUnit`] since midnight.
276   7:  (TimeType) Time
277   /// A timestamp stored as [`TimeUnit`] since Unix epoch.
278   8:  (TimestampType) Timestamp
279   // 9: reserved for INTERVAL
280   /// An integer with a specified bit width and signedness.
281   10: (IntType) Integer
282   /// An unknown logical type.
283   11: Unknown
284   /// A JSON document.
285   12: Json
286   /// A BSON document.
287   13: Bson
288   /// A UUID.
289   14: Uuid
290   /// A 16-bit floating point number.
291   15: Float16
292   /// A Variant value.
293   16: (VariantType) Variant
294   /// A geospatial feature in the Well-Known Binary (WKB) format with linear/planar edges interpolation.
295   17: (GeometryType) Geometry
296   /// A geospatial feature in the WKB format with an explicit (non-linear/non-planar) edges interpolation.
297   18: (GeographyType) Geography
298}
299);
300
301impl LogicalType {
302    /// Create a [`LogicalType::Integer`] variant with the given `bit_width` and `is_signed`
303    pub fn integer(bit_width: i8, is_signed: bool) -> Self {
304        Self::Integer(IntType {
305            bit_width,
306            is_signed,
307        })
308    }
309
310    /// Create a [`LogicalType::Decimal`] variant with the given `scale` and `precision`
311    pub fn decimal(scale: i32, precision: i32) -> Self {
312        Self::Decimal(DecimalType { scale, precision })
313    }
314
315    /// Create a [`LogicalType::Time`] variant with the given `is_adjusted_to_u_t_c` and `unit`
316    pub fn time(is_adjusted_to_u_t_c: bool, unit: TimeUnit) -> Self {
317        Self::Time(TimeType {
318            is_adjusted_to_u_t_c,
319            unit,
320        })
321    }
322
323    /// Create a [`LogicalType::Timestamp`] variant with the given `is_adjusted_to_u_t_c` and `unit`
324    pub fn timestamp(is_adjusted_to_u_t_c: bool, unit: TimeUnit) -> Self {
325        Self::Timestamp(TimestampType {
326            is_adjusted_to_u_t_c,
327            unit,
328        })
329    }
330
331    /// Create a [`LogicalType::Variant`] variant with the given `specification_version`
332    pub fn variant(specification_version: Option<i8>) -> Self {
333        Self::Variant(VariantType {
334            specification_version,
335        })
336    }
337
338    /// Create a [`LogicalType::Geometry`] variant with the given `crs`
339    pub fn geometry(crs: Option<String>) -> Self {
340        Self::Geometry(GeometryType { crs })
341    }
342
343    /// Create a [`LogicalType::Geography`] variant with the given `crs` and `algorithm`
344    pub fn geography(crs: Option<String>, algorithm: Option<EdgeInterpolationAlgorithm>) -> Self {
345        Self::Geography(GeographyType { crs, algorithm })
346    }
347}
348
349// ----------------------------------------------------------------------
350// Mirrors thrift enum `FieldRepetitionType`
351//
352
353thrift_enum!(
354/// Representation of field types in schema.
355enum FieldRepetitionType {
356  /// This field is required (can not be null) and each row has exactly 1 value.
357  REQUIRED = 0;
358  /// The field is optional (can be null) and each row has 0 or 1 values.
359  OPTIONAL = 1;
360  /// The field is repeated and can contain 0 or more values.
361  REPEATED = 2;
362}
363);
364
365/// Type alias for thrift `FieldRepetitionType`
366pub type Repetition = FieldRepetitionType;
367
368// ----------------------------------------------------------------------
369// Mirrors thrift enum `Encoding`
370
371thrift_enum!(
372/// Encodings supported by Parquet.
373///
374/// Not all encodings are valid for all types. These enums are also used to specify the
375/// encoding of definition and repetition levels.
376///
377/// By default this crate uses [Encoding::PLAIN], [Encoding::RLE], and [Encoding::RLE_DICTIONARY].
378/// These provide very good encode and decode performance, whilst yielding reasonable storage
379/// efficiency and being supported by all major parquet readers.
380///
381/// The delta encodings are also supported and will be used if a newer [WriterVersion] is
382/// configured, however, it should be noted that these sacrifice encode and decode performance for
383/// improved storage efficiency. This performance regression is particularly pronounced in the case
384/// of record skipping as occurs during predicate push-down. It is recommended users assess the
385/// performance impact when evaluating these encodings.
386///
387/// [WriterVersion]: crate::file::properties::WriterVersion
388enum Encoding {
389  /// Default encoding.
390  /// - BOOLEAN - 1 bit per value. 0 is false; 1 is true.
391  /// - INT32 - 4 bytes per value.  Stored as little-endian.
392  /// - INT64 - 8 bytes per value.  Stored as little-endian.
393  /// - FLOAT - 4 bytes per value.  IEEE. Stored as little-endian.
394  /// - DOUBLE - 8 bytes per value.  IEEE. Stored as little-endian.
395  /// - BYTE_ARRAY - 4 byte length stored as little endian, followed by bytes.
396  /// - FIXED_LEN_BYTE_ARRAY - Just the bytes.
397  PLAIN = 0;
398  //  GROUP_VAR_INT = 1;
399  /// **Deprecated** dictionary encoding.
400  ///
401  /// The values in the dictionary are encoded using PLAIN encoding.
402  /// Since it is deprecated, RLE_DICTIONARY encoding is used for a data page, and
403  /// PLAIN encoding is used for dictionary page.
404  PLAIN_DICTIONARY = 2;
405  /// Group packed run length encoding.
406  ///
407  /// Usable for definition/repetition levels encoding and boolean values.
408  RLE = 3;
409  /// **Deprecated** Bit-packed encoding.
410  ///
411  /// This can only be used if the data has a known max width.
412  /// Usable for definition/repetition levels encoding.
413  ///
414  /// There are compatibility issues with files using this encoding.
415  /// The parquet standard specifies the bits to be packed starting from the
416  /// most-significant bit, several implementations do not follow this bit order.
417  /// Several other implementations also have issues reading this encoding
418  /// because of incorrect assumptions about the length of the encoded data.
419  ///
420  /// The RLE/bit-packing hybrid is more cpu and memory efficient and should be used instead.
421  #[deprecated(
422      since = "51.0.0",
423      note = "Please see documentation for compatibility issues and use the RLE/bit-packing hybrid encoding instead"
424  )]
425  BIT_PACKED = 4;
426  /// Delta encoding for integers, either INT32 or INT64.
427  ///
428  /// Works best on sorted data.
429  DELTA_BINARY_PACKED = 5;
430  /// Encoding for byte arrays to separate the length values and the data.
431  ///
432  /// The lengths are encoded using DELTA_BINARY_PACKED encoding.
433  DELTA_LENGTH_BYTE_ARRAY = 6;
434  /// Incremental encoding for byte arrays.
435  ///
436  /// Prefix lengths are encoded using DELTA_BINARY_PACKED encoding.
437  /// Suffixes are stored using DELTA_LENGTH_BYTE_ARRAY encoding.
438  DELTA_BYTE_ARRAY = 7;
439  /// Dictionary encoding.
440  ///
441  /// The ids are encoded using the RLE encoding.
442  RLE_DICTIONARY = 8;
443  /// Encoding for fixed-width data.
444  ///
445  /// K byte-streams are created where K is the size in bytes of the data type.
446  /// The individual bytes of a value are scattered to the corresponding stream and
447  /// the streams are concatenated.
448  /// This itself does not reduce the size of the data but can lead to better compression
449  /// afterwards. Note that the use of this encoding with FIXED_LEN_BYTE_ARRAY(N) data may
450  /// perform poorly for large values of N.
451  BYTE_STREAM_SPLIT = 9;
452}
453);
454
455impl FromStr for Encoding {
456    type Err = ParquetError;
457
458    fn from_str(s: &str) -> Result<Self, Self::Err> {
459        match s {
460            "PLAIN" | "plain" => Ok(Encoding::PLAIN),
461            "PLAIN_DICTIONARY" | "plain_dictionary" => Ok(Encoding::PLAIN_DICTIONARY),
462            "RLE" | "rle" => Ok(Encoding::RLE),
463            #[allow(deprecated)]
464            "BIT_PACKED" | "bit_packed" => Ok(Encoding::BIT_PACKED),
465            "DELTA_BINARY_PACKED" | "delta_binary_packed" => Ok(Encoding::DELTA_BINARY_PACKED),
466            "DELTA_LENGTH_BYTE_ARRAY" | "delta_length_byte_array" => {
467                Ok(Encoding::DELTA_LENGTH_BYTE_ARRAY)
468            }
469            "DELTA_BYTE_ARRAY" | "delta_byte_array" => Ok(Encoding::DELTA_BYTE_ARRAY),
470            "RLE_DICTIONARY" | "rle_dictionary" => Ok(Encoding::RLE_DICTIONARY),
471            "BYTE_STREAM_SPLIT" | "byte_stream_split" => Ok(Encoding::BYTE_STREAM_SPLIT),
472            _ => Err(general_err!("unknown encoding: {}", s)),
473        }
474    }
475}
476
477/// A bitmask representing the [`Encoding`]s employed while encoding a Parquet column chunk.
478///
479/// The Parquet [`ColumnMetaData`] struct contains an array that indicates what encodings were
480/// used when writing that column chunk. For memory and performance reasons, this crate reduces
481/// that array to bitmask, where each bit position represents a different [`Encoding`]. This
482/// struct contains that bitmask, and provides methods to interact with the data.
483///
484/// # Example
485/// ```no_run
486/// # use parquet::file::metadata::ParquetMetaDataReader;
487/// # use parquet::basic::Encoding;
488/// # fn open_parquet_file(path: &str) -> std::fs::File { unimplemented!(); }
489/// // read parquet metadata from a file
490/// let file = open_parquet_file("some_path.parquet");
491/// let mut reader = ParquetMetaDataReader::new();
492/// reader.try_parse(&file).unwrap();
493/// let metadata = reader.finish().unwrap();
494///
495/// // find the encodings used by the first column chunk in the first row group
496/// let col_meta = metadata.row_group(0).column(0);
497/// let encodings = col_meta.encodings_mask();
498///
499/// // check to see if a particular encoding was used
500/// let used_rle = encodings.is_set(Encoding::RLE);
501///
502/// // check to see if all of a set of encodings were used
503/// let used_all = encodings.all_set([Encoding::RLE, Encoding::PLAIN].iter());
504///
505/// // convert mask to a Vec<Encoding>
506/// let encodings_vec = encodings.encodings().collect::<Vec<_>>();
507/// ```
508///
509/// [`ColumnMetaData`]: https://github.com/apache/parquet-format/blob/9fd57b59e0ce1a82a69237dcf8977d3e72a2965d/src/main/thrift/parquet.thrift#L875
510#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
511pub struct EncodingMask(i32);
512
513impl EncodingMask {
514    /// Highest valued discriminant in the [`Encoding`] enum
515    const MAX_ENCODING: i32 = Encoding::MAX_DISCRIMINANT;
516    /// A mask consisting of unused bit positions, used for validation. This includes the never
517    /// used GROUP_VAR_INT encoding value of `1`.
518    const ALLOWED_MASK: u32 =
519        !(1u32 << (EncodingMask::MAX_ENCODING as u32 + 1)).wrapping_sub(1) | (1 << 1);
520
521    /// Attempt to create a new `EncodingMask` from an integer.
522    ///
523    /// This will return an error if a bit outside the allowable range is set.
524    pub fn try_new(val: i32) -> Result<Self> {
525        if val as u32 & Self::ALLOWED_MASK != 0 {
526            return Err(general_err!("Attempt to create invalid mask: 0x{:x}", val));
527        }
528        Ok(Self(val))
529    }
530
531    /// Return an integer representation of this `EncodingMask`.
532    pub fn as_i32(&self) -> i32 {
533        self.0
534    }
535
536    /// Create a new `EncodingMask` from a collection of [`Encoding`]s.
537    pub fn new_from_encodings<'a>(encodings: impl Iterator<Item = &'a Encoding>) -> Self {
538        let mut mask = 0;
539        for &e in encodings {
540            mask |= 1 << (e as i32);
541        }
542        Self(mask)
543    }
544
545    /// Mark the given [`Encoding`] as present in this mask.
546    pub fn insert(&mut self, val: Encoding) {
547        self.0 |= 1 << (val as i32);
548    }
549
550    /// Test if a given [`Encoding`] is present in this mask.
551    pub fn is_set(&self, val: Encoding) -> bool {
552        self.0 & (1 << (val as i32)) != 0
553    }
554
555    /// Test if this mask has only the bit for the given [`Encoding`] set.
556    pub fn is_only(&self, val: Encoding) -> bool {
557        self.0 == (1 << (val as i32))
558    }
559
560    /// Test if all [`Encoding`]s in a given set are present in this mask.
561    pub fn all_set<'a>(&self, mut encodings: impl Iterator<Item = &'a Encoding>) -> bool {
562        encodings.all(|&e| self.is_set(e))
563    }
564
565    /// Return an iterator over all [`Encoding`]s present in this mask.
566    pub fn encodings(&self) -> impl Iterator<Item = Encoding> {
567        Self::mask_to_encodings_iter(self.0)
568    }
569
570    fn mask_to_encodings_iter(mask: i32) -> impl Iterator<Item = Encoding> {
571        (0..=Self::MAX_ENCODING)
572            .filter(move |i| mask & (1 << i) != 0)
573            .map(i32_to_encoding)
574    }
575}
576
577impl HeapSize for EncodingMask {
578    fn heap_size(&self) -> usize {
579        0 // no heap allocations
580    }
581}
582
583impl<'a, R: ThriftCompactInputProtocol<'a>> ReadThrift<'a, R> for EncodingMask {
584    fn read_thrift(prot: &mut R) -> Result<Self> {
585        let mut mask = 0;
586
587        // This reads a Thrift `list<Encoding>` and turns it into a bitmask
588        let list_ident = prot.read_list_begin()?;
589        // check for enum (encoded as I32)
590        validate_list_type(ElementType::I32, &list_ident)?;
591        for _ in 0..list_ident.size {
592            let val = Encoding::read_thrift(prot)?;
593            mask |= 1 << val as i32;
594        }
595        Ok(Self(mask))
596    }
597}
598
599#[allow(deprecated)]
600fn i32_to_encoding(val: i32) -> Encoding {
601    match val {
602        0 => Encoding::PLAIN,
603        2 => Encoding::PLAIN_DICTIONARY,
604        3 => Encoding::RLE,
605        4 => Encoding::BIT_PACKED,
606        5 => Encoding::DELTA_BINARY_PACKED,
607        6 => Encoding::DELTA_LENGTH_BYTE_ARRAY,
608        7 => Encoding::DELTA_BYTE_ARRAY,
609        8 => Encoding::RLE_DICTIONARY,
610        9 => Encoding::BYTE_STREAM_SPLIT,
611        _ => panic!("Impossible encoding {val}"),
612    }
613}
614
615// ----------------------------------------------------------------------
616// Mirrors thrift enum `CompressionCodec`
617
618thrift_enum!(
619/// Supported compression algorithms.
620///
621/// Codecs added in format version X.Y can be read by readers based on X.Y and later.
622/// Codec support may vary between readers based on the format version and
623/// libraries available at runtime.
624///
625/// See [Compression.md] for a detailed specification of these algorithms.
626///
627/// [Compression.md]: https://github.com/apache/parquet-format/blob/master/Compression.md
628enum CompressionCodec {
629  UNCOMPRESSED = 0;
630  SNAPPY = 1;
631  GZIP = 2;
632  LZO = 3;
633  BROTLI = 4;  // Added in 2.4
634  LZ4 = 5;     // DEPRECATED (Added in 2.4)
635  ZSTD = 6;    // Added in 2.4
636  LZ4_RAW = 7; // Added in 2.9
637}
638);
639
640// NOTE: This enum likely belongs in file::properties now, but moving it there would be a
641// breaking API change, that's probably not worth the pain. If a new codec is added to the
642// Parquet specification, or any other breaking changes are made to this enum, this can be
643// revisited.
644
645/// Supported block compression algorithms.
646///
647/// Block compression can yield non-trivial improvements to storage efficiency at the expense
648/// of potentially significantly worse encode and decode performance. Many applications,
649/// especially those making use of high-throughput and low-cost commodity object storage,
650/// may find storage efficiency less important than decode throughput, and therefore may
651/// wish to not make use of block compression.
652///
653/// The writers in this crate default to no block compression for this reason.
654///
655/// Applications that do still wish to use block compression, will find [`Compression::ZSTD`]
656/// to provide a good balance of compression, performance, and ecosystem support. Alternatively,
657/// [`Compression::LZ4_RAW`] provides much faster decompression speeds, at the cost of typically
658/// worse compression ratios. However, it is not as widely supported by the ecosystem, with the
659/// Hadoop ecosystem historically favoring the non-standard and now deprecated [`Compression::LZ4`].
660#[derive(Debug, Clone, Copy, PartialEq, Eq)]
661#[allow(non_camel_case_types)]
662pub enum Compression {
663    /// No compression.
664    UNCOMPRESSED,
665    /// [Snappy compression](https://en.wikipedia.org/wiki/Snappy_(compression))
666    SNAPPY,
667    /// [Gzip compression](https://www.ietf.org/rfc/rfc1952.txt)
668    GZIP(GzipLevel),
669    /// [LZO compression](https://en.wikipedia.org/wiki/Lempel%E2%80%93Ziv%E2%80%93Oberhumer)
670    LZO,
671    /// [Brotli compression](https://datatracker.ietf.org/doc/html/rfc7932)
672    BROTLI(BrotliLevel),
673    /// [LZ4 compression](https://lz4.org/), [(deprecated)](https://issues.apache.org/jira/browse/PARQUET-2032)
674    LZ4,
675    /// [ZSTD compression](https://datatracker.ietf.org/doc/html/rfc8878)
676    ZSTD(ZstdLevel),
677    /// [LZ4 compression](https://lz4.org/).
678    LZ4_RAW,
679}
680
681impl From<CompressionCodec> for Compression {
682    fn from(value: CompressionCodec) -> Self {
683        match value {
684            CompressionCodec::UNCOMPRESSED => Compression::UNCOMPRESSED,
685            CompressionCodec::SNAPPY => Compression::SNAPPY,
686            CompressionCodec::GZIP => Compression::GZIP(Default::default()),
687            CompressionCodec::LZO => Compression::LZO,
688            CompressionCodec::BROTLI => Compression::BROTLI(Default::default()),
689            CompressionCodec::LZ4 => Compression::LZ4,
690            CompressionCodec::ZSTD => Compression::ZSTD(Default::default()),
691            CompressionCodec::LZ4_RAW => Compression::LZ4_RAW,
692        }
693    }
694}
695
696impl From<Compression> for CompressionCodec {
697    fn from(value: Compression) -> Self {
698        match value {
699            Compression::UNCOMPRESSED => CompressionCodec::UNCOMPRESSED,
700            Compression::SNAPPY => CompressionCodec::SNAPPY,
701            Compression::GZIP(_) => CompressionCodec::GZIP,
702            Compression::LZO => CompressionCodec::LZO,
703            Compression::BROTLI(_) => CompressionCodec::BROTLI,
704            Compression::LZ4 => CompressionCodec::LZ4,
705            Compression::ZSTD(_) => CompressionCodec::ZSTD,
706            Compression::LZ4_RAW => CompressionCodec::LZ4_RAW,
707        }
708    }
709}
710
711fn split_compression_string(str_setting: &str) -> Result<(&str, Option<i32>), ParquetError> {
712    let split_setting = str_setting.split_once('(');
713
714    match split_setting {
715        Some((codec, level_str)) => {
716            let level = &level_str[..level_str.len() - 1]
717                .parse::<i32>()
718                .map_err(|_| {
719                    ParquetError::General(format!("invalid compression level: {level_str}"))
720                })?;
721            Ok((codec, Some(*level)))
722        }
723        None => Ok((str_setting, None)),
724    }
725}
726
727fn check_level_is_none(level: &Option<i32>) -> Result<(), ParquetError> {
728    if level.is_some() {
729        return Err(ParquetError::General(
730            "compression level is not supported".to_string(),
731        ));
732    }
733
734    Ok(())
735}
736
737fn require_level(codec: &str, level: Option<i32>) -> Result<i32, ParquetError> {
738    level.ok_or(ParquetError::General(format!(
739        "{codec} requires a compression level",
740    )))
741}
742
743impl FromStr for Compression {
744    type Err = ParquetError;
745
746    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
747        let (codec, level) = split_compression_string(s)?;
748
749        let c = match codec {
750            "UNCOMPRESSED" | "uncompressed" => {
751                check_level_is_none(&level)?;
752                Compression::UNCOMPRESSED
753            }
754            "SNAPPY" | "snappy" => {
755                check_level_is_none(&level)?;
756                Compression::SNAPPY
757            }
758            "GZIP" | "gzip" => {
759                let level = require_level(codec, level)?;
760                Compression::GZIP(GzipLevel::try_new(level.try_into()?)?)
761            }
762            "LZO" | "lzo" => {
763                check_level_is_none(&level)?;
764                Compression::LZO
765            }
766            "BROTLI" | "brotli" => {
767                let level = require_level(codec, level)?;
768                Compression::BROTLI(BrotliLevel::try_new(level.try_into()?)?)
769            }
770            "LZ4" | "lz4" => {
771                check_level_is_none(&level)?;
772                Compression::LZ4
773            }
774            "ZSTD" | "zstd" => {
775                let level = require_level(codec, level)?;
776                Compression::ZSTD(ZstdLevel::try_new(level)?)
777            }
778            "LZ4_RAW" | "lz4_raw" => {
779                check_level_is_none(&level)?;
780                Compression::LZ4_RAW
781            }
782            _ => {
783                return Err(ParquetError::General(format!(
784                    "unsupport compression {codec}"
785                )));
786            }
787        };
788
789        Ok(c)
790    }
791}
792
793// ----------------------------------------------------------------------
794// Mirrors thrift enum `PageType`
795
796thrift_enum!(
797/// Available data pages for Parquet file format.
798/// Note that some of the page types may not be supported.
799enum PageType {
800  DATA_PAGE = 0;
801  INDEX_PAGE = 1;
802  DICTIONARY_PAGE = 2;
803  DATA_PAGE_V2 = 3;
804}
805);
806
807// ----------------------------------------------------------------------
808// Mirrors thrift enum `BoundaryOrder`
809
810thrift_enum!(
811/// Enum to annotate whether lists of min/max elements inside ColumnIndex
812/// are ordered and if so, in which direction.
813enum BoundaryOrder {
814  UNORDERED = 0;
815  ASCENDING = 1;
816  DESCENDING = 2;
817}
818);
819
820// ----------------------------------------------------------------------
821// Mirrors thrift enum `EdgeInterpolationAlgorithm`
822
823// this is hand coded to allow for the _Unknown variant (allows this to be forward compatible)
824
825/// Edge interpolation algorithm for [`LogicalType::Geography`]
826#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
827#[repr(i32)]
828#[derive(Default)]
829pub enum EdgeInterpolationAlgorithm {
830    /// Edges are interpolated as geodesics on a sphere.
831    #[default]
832    SPHERICAL = 0,
833    /// <https://en.wikipedia.org/wiki/Vincenty%27s_formulae>
834    VINCENTY = 1,
835    /// Thomas, Paul D. Spheroidal geodesics, reference systems, & local geometry. US Naval Oceanographic Office, 1970
836    THOMAS = 2,
837    /// Thomas, Paul D. Mathematical models for navigation systems. US Naval Oceanographic Office, 1965.
838    ANDOYER = 3,
839    /// Karney, Charles FF. "Algorithms for geodesics." Journal of Geodesy 87 (2013): 43-55
840    KARNEY = 4,
841    /// Unknown algorithm
842    _Unknown(i32),
843}
844
845#[cfg(feature = "geospatial")]
846impl EdgeInterpolationAlgorithm {
847    /// Converts an [`EdgeInterpolationAlgorithm`] into its corresponding algorithm defined by
848    /// [`parquet_geospatial::WkbEdges`].
849    ///
850    /// This method will only return an Err if the [`EdgeInterpolationAlgorithm`] is the `_Unknown`
851    /// variant.
852    pub fn try_as_edges(&self) -> Result<parquet_geospatial::WkbEdges> {
853        match &self {
854            Self::SPHERICAL => Ok(parquet_geospatial::WkbEdges::Spherical),
855            Self::VINCENTY => Ok(parquet_geospatial::WkbEdges::Vincenty),
856            Self::THOMAS => Ok(parquet_geospatial::WkbEdges::Thomas),
857            Self::ANDOYER => Ok(parquet_geospatial::WkbEdges::Andoyer),
858            Self::KARNEY => Ok(parquet_geospatial::WkbEdges::Karney),
859            unknown => Err(general_err!(
860                "Unknown edge interpolation algorithm: {}",
861                unknown
862            )),
863        }
864    }
865}
866
867impl fmt::Display for EdgeInterpolationAlgorithm {
868    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
869        f.write_fmt(format_args!("{0:?}", self))
870    }
871}
872
873#[cfg(feature = "geospatial")]
874impl From<parquet_geospatial::WkbEdges> for EdgeInterpolationAlgorithm {
875    fn from(value: parquet_geospatial::WkbEdges) -> Self {
876        match value {
877            parquet_geospatial::WkbEdges::Spherical => Self::SPHERICAL,
878            parquet_geospatial::WkbEdges::Vincenty => Self::VINCENTY,
879            parquet_geospatial::WkbEdges::Thomas => Self::THOMAS,
880            parquet_geospatial::WkbEdges::Andoyer => Self::ANDOYER,
881            parquet_geospatial::WkbEdges::Karney => Self::KARNEY,
882        }
883    }
884}
885
886impl FromStr for EdgeInterpolationAlgorithm {
887    type Err = ParquetError;
888
889    fn from_str(s: &str) -> Result<Self> {
890        match s.to_ascii_uppercase().as_str() {
891            "SPHERICAL" => Ok(EdgeInterpolationAlgorithm::SPHERICAL),
892            "VINCENTY" => Ok(EdgeInterpolationAlgorithm::VINCENTY),
893            "THOMAS" => Ok(EdgeInterpolationAlgorithm::THOMAS),
894            "ANDOYER" => Ok(EdgeInterpolationAlgorithm::ANDOYER),
895            "KARNEY" => Ok(EdgeInterpolationAlgorithm::KARNEY),
896            unknown => Err(general_err!(
897                "Unknown edge interpolation algorithm: {}",
898                unknown
899            )),
900        }
901    }
902}
903
904impl<'a, R: ThriftCompactInputProtocol<'a>> ReadThrift<'a, R> for EdgeInterpolationAlgorithm {
905    fn read_thrift(prot: &mut R) -> Result<Self> {
906        let val = prot.read_i32()?;
907        match val {
908            0 => Ok(Self::SPHERICAL),
909            1 => Ok(Self::VINCENTY),
910            2 => Ok(Self::THOMAS),
911            3 => Ok(Self::ANDOYER),
912            4 => Ok(Self::KARNEY),
913            _ => Ok(Self::_Unknown(val)),
914        }
915    }
916}
917
918impl WriteThrift for EdgeInterpolationAlgorithm {
919    const ELEMENT_TYPE: ElementType = ElementType::I32;
920    fn write_thrift<W: Write>(&self, writer: &mut ThriftCompactOutputProtocol<W>) -> Result<()> {
921        let val: i32 = match *self {
922            Self::SPHERICAL => 0,
923            Self::VINCENTY => 1,
924            Self::THOMAS => 2,
925            Self::ANDOYER => 3,
926            Self::KARNEY => 4,
927            Self::_Unknown(i) => i,
928        };
929        writer.write_i32(val)
930    }
931}
932
933write_thrift_field!(EdgeInterpolationAlgorithm, FieldType::I32);
934
935// ----------------------------------------------------------------------
936// Mirrors thrift union `BloomFilterAlgorithm`
937
938thrift_union_all_empty!(
939/// The algorithm used in Bloom filter.
940union BloomFilterAlgorithm {
941  /// Block-based Bloom filter.
942  1: SplitBlockAlgorithm BLOCK;
943}
944);
945
946// ----------------------------------------------------------------------
947// Mirrors thrift union `BloomFilterHash`
948
949thrift_union_all_empty!(
950/// The hash function used in Bloom filter. This function takes the hash of a column value
951/// using plain encoding.
952union BloomFilterHash {
953  /// xxHash Strategy.
954  1: XxHash XXHASH;
955}
956);
957
958// ----------------------------------------------------------------------
959// Mirrors thrift union `BloomFilterCompression`
960
961thrift_union_all_empty!(
962/// The compression used in the Bloom filter.
963union BloomFilterCompression {
964  1: Uncompressed UNCOMPRESSED;
965}
966);
967
968// ----------------------------------------------------------------------
969// Mirrors thrift union `ColumnOrder`
970
971/// Sort order for page and column statistics.
972///
973/// Types are associated with sort orders and column stats are aggregated using a sort
974/// order, and a sort order should be considered when comparing values with statistics
975/// min/max.
976///
977/// See [`ColumnOrder`] for more information.
978#[derive(Debug, Clone, Copy, PartialEq, Eq)]
979#[allow(non_camel_case_types)]
980pub enum SortOrder {
981    /// Signed (either value or legacy byte-wise) comparison.
982    SIGNED,
983    /// Unsigned (depending on physical type either value or byte-wise) comparison.
984    UNSIGNED,
985    /// Comparison is undefined.
986    UNDEFINED,
987    /// Use IEEE 754 total order.
988    TOTAL_ORDER,
989    /// Use INT96 timestamp order (see [parquet-format/#584] and the [Thrift spec]).
990    ///
991    /// [parquet-format/#584]: https://github.com/apache/parquet-format/pull/584
992    /// [Thrift spec]: https://github.com/apache/parquet-format/blob/2076361bb64e2de9ca6a8d06eda025a6fa4e9df6/src/main/thrift/parquet.thrift#L1230-L1233
993    INT96_TIMESTAMP,
994}
995
996impl SortOrder {
997    /// Returns true if this is [`Self::SIGNED`]
998    pub fn is_signed(&self) -> bool {
999        matches!(self, Self::SIGNED)
1000    }
1001}
1002
1003/// Column order that specifies what method was used to aggregate min/max values for
1004/// statistics.
1005///
1006/// Prior to version 2.4.0, Parquet used signed comparisons when computing min and max
1007/// values for statistics. This caused problems for UTF8 encoded strings, so the
1008/// [`ColumnOrder`] union was added, initially with a single variant `TYPE_ORDER`. The
1009/// sort order for columns was then defined based on the logical or physical type of
1010/// the column, and could use either signed comparison, unsigned comparison, or for some
1011/// types be left undefined. Since then several new `ColumnOrder`s have been added to the
1012/// specification.
1013///
1014/// In this crate, the `ColumnOrder` found in the footer is represented by this enum. To
1015/// convey what actual sort order to use, this crate maps the `ColumnOrder` along with the
1016/// physical and logical type to a [`SortOrder`]. It is this [`SortOrder`] that is used
1017/// internally when deciding how to compute the min/max statistics.
1018///
1019/// If column order is undefined, then it is the legacy behaviour and all values should
1020/// be compared as signed values/bytes.
1021///
1022/// [`ColumnOrder`]: https://github.com/apache/parquet-format/blob/2076361bb64e2de9ca6a8d06eda025a6fa4e9df6/src/main/thrift/parquet.thrift#L1103
1023#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1024#[allow(non_camel_case_types)]
1025pub enum ColumnOrder {
1026    /// Column uses the order defined by its logical or physical type
1027    /// (if there is no logical type), parquet-format 2.4.0+.
1028    TYPE_DEFINED_ORDER(SortOrder),
1029    /// Column ordering to use for floating point types.
1030    IEEE_754_TOTAL_ORDER,
1031    /// Column ordering to use for INT96 types.
1032    INT96_TIMESTAMP_ORDER,
1033    // The following are not defined in the Parquet spec and should always be last.
1034    /// Undefined column order, means legacy behaviour before parquet-format 2.4.0.
1035    /// Sort order is always SIGNED.
1036    UNDEFINED,
1037    /// An unknown but present ColumnOrder. Statistics with an unknown `ColumnOrder`
1038    /// will be ignored.
1039    UNKNOWN,
1040}
1041
1042impl ColumnOrder {
1043    /// Returns the `ColumnOrder` for a physical/logical type.
1044    pub fn column_order_for_type(
1045        logical_type: Option<&LogicalType>,
1046        converted_type: ConvertedType,
1047        physical_type: Type,
1048    ) -> ColumnOrder {
1049        if Some(&LogicalType::Float16) == logical_type
1050            || matches!(physical_type, Type::FLOAT | Type::DOUBLE)
1051        {
1052            ColumnOrder::IEEE_754_TOTAL_ORDER
1053        } else if matches!(physical_type, Type::INT96) {
1054            ColumnOrder::INT96_TIMESTAMP_ORDER
1055        } else {
1056            let sort_order =
1057                Self::get_sort_order_for_type(logical_type, converted_type, physical_type, true);
1058            ColumnOrder::TYPE_DEFINED_ORDER(sort_order)
1059        }
1060    }
1061
1062    /// Returns sort order for a physical/logical type.
1063    ///
1064    /// `is_type_defined` indicates whether the column order for this type is
1065    /// [`ColumnOrder::TYPE_DEFINED_ORDER`].
1066    ///
1067    /// It is now preferred to obtain this via [`Self::sort_order`].
1068    #[deprecated(since = "60.0.0", note = "use `ColumnOrder::sort_order` instead")]
1069    pub fn sort_order_for_type(
1070        logical_type: Option<&LogicalType>,
1071        converted_type: ConvertedType,
1072        physical_type: Type,
1073        is_type_defined: bool,
1074    ) -> SortOrder {
1075        ColumnOrder::get_sort_order_for_type(
1076            logical_type,
1077            converted_type,
1078            physical_type,
1079            is_type_defined,
1080        )
1081    }
1082
1083    // this is pub(crate) so it can be used in the thrift parser to correctly instantiate
1084    // the column_orders vec
1085    pub(crate) fn get_sort_order_for_type(
1086        logical_type: Option<&LogicalType>,
1087        converted_type: ConvertedType,
1088        physical_type: Type,
1089        is_type_defined: bool,
1090    ) -> SortOrder {
1091        match logical_type {
1092            Some(logical) => match logical {
1093                LogicalType::String | LogicalType::Enum | LogicalType::Json | LogicalType::Bson => {
1094                    SortOrder::UNSIGNED
1095                }
1096                LogicalType::Integer(int) => match int.is_signed {
1097                    true => SortOrder::SIGNED,
1098                    false => SortOrder::UNSIGNED,
1099                },
1100                LogicalType::Map | LogicalType::List => SortOrder::UNDEFINED,
1101                LogicalType::Decimal(_) => SortOrder::SIGNED,
1102                LogicalType::Date => SortOrder::SIGNED,
1103                LogicalType::Time(_) => SortOrder::SIGNED,
1104                LogicalType::Timestamp(_) => SortOrder::SIGNED,
1105                LogicalType::Unknown => SortOrder::UNDEFINED,
1106                LogicalType::Uuid => SortOrder::UNSIGNED,
1107                LogicalType::Float16 => {
1108                    if is_type_defined {
1109                        SortOrder::SIGNED
1110                    } else {
1111                        SortOrder::TOTAL_ORDER
1112                    }
1113                }
1114                LogicalType::Variant(_)
1115                | LogicalType::Geometry(_)
1116                | LogicalType::Geography(_)
1117                | LogicalType::_Unknown { .. } => SortOrder::UNDEFINED,
1118            },
1119            // Fall back to converted type
1120            None => Self::get_converted_sort_order(converted_type, physical_type, is_type_defined),
1121        }
1122    }
1123
1124    fn get_converted_sort_order(
1125        converted_type: ConvertedType,
1126        physical_type: Type,
1127        is_type_defined: bool,
1128    ) -> SortOrder {
1129        match converted_type {
1130            // Unsigned byte-wise comparison.
1131            ConvertedType::UTF8
1132            | ConvertedType::JSON
1133            | ConvertedType::BSON
1134            | ConvertedType::ENUM => SortOrder::UNSIGNED,
1135
1136            ConvertedType::INT_8
1137            | ConvertedType::INT_16
1138            | ConvertedType::INT_32
1139            | ConvertedType::INT_64 => SortOrder::SIGNED,
1140
1141            ConvertedType::UINT_8
1142            | ConvertedType::UINT_16
1143            | ConvertedType::UINT_32
1144            | ConvertedType::UINT_64 => SortOrder::UNSIGNED,
1145
1146            // Signed comparison of the represented value.
1147            ConvertedType::DECIMAL => SortOrder::SIGNED,
1148
1149            ConvertedType::DATE => SortOrder::SIGNED,
1150
1151            ConvertedType::TIME_MILLIS
1152            | ConvertedType::TIME_MICROS
1153            | ConvertedType::TIMESTAMP_MILLIS
1154            | ConvertedType::TIMESTAMP_MICROS => SortOrder::SIGNED,
1155
1156            ConvertedType::INTERVAL => SortOrder::UNDEFINED,
1157
1158            ConvertedType::LIST | ConvertedType::MAP | ConvertedType::MAP_KEY_VALUE => {
1159                SortOrder::UNDEFINED
1160            }
1161
1162            // Fall back to physical type.
1163            ConvertedType::NONE => Self::get_default_sort_order(physical_type, is_type_defined),
1164        }
1165    }
1166
1167    /// Returns default sort order based on physical type.
1168    fn get_default_sort_order(physical_type: Type, is_type_defined: bool) -> SortOrder {
1169        match physical_type {
1170            // Order: false, true
1171            Type::BOOLEAN => SortOrder::UNSIGNED,
1172            Type::INT32 | Type::INT64 => SortOrder::SIGNED,
1173            Type::INT96 => {
1174                if is_type_defined {
1175                    SortOrder::UNDEFINED
1176                } else {
1177                    SortOrder::INT96_TIMESTAMP
1178                }
1179            }
1180            // Notes to remember when comparing float/double values:
1181            // If legacy TYPE_DEFINED_ORDER is specified:
1182            //   If the min is a NaN, it should be ignored.
1183            //   If the max is a NaN, it should be ignored.
1184            //   If the min is +0, the row group may contain -0 values as well.
1185            //   If the max is -0, the row group may contain +0 values as well.
1186            //   When looking for NaN values, min and max should be ignored.
1187            // If IEEE_754_TOTAL_ORDER:
1188            //   Examine nan_count to see if NaNs are present.
1189            //   If min/max are NaN, that means only NaNs are present.
1190            //   If min/max are not NaN, they are ordered according to total order.
1191            Type::FLOAT | Type::DOUBLE => {
1192                if is_type_defined {
1193                    SortOrder::SIGNED
1194                } else {
1195                    SortOrder::TOTAL_ORDER
1196                }
1197            }
1198            // Unsigned byte-wise comparison
1199            Type::BYTE_ARRAY | Type::FIXED_LEN_BYTE_ARRAY => SortOrder::UNSIGNED,
1200        }
1201    }
1202
1203    /// Returns sort order associated with this column order.
1204    pub fn sort_order(&self) -> SortOrder {
1205        match *self {
1206            ColumnOrder::TYPE_DEFINED_ORDER(order) => order,
1207            ColumnOrder::IEEE_754_TOTAL_ORDER => SortOrder::TOTAL_ORDER,
1208            ColumnOrder::INT96_TIMESTAMP_ORDER => SortOrder::INT96_TIMESTAMP,
1209            ColumnOrder::UNDEFINED => SortOrder::SIGNED,
1210            ColumnOrder::UNKNOWN => SortOrder::UNDEFINED,
1211        }
1212    }
1213}
1214
1215impl<'a, R: ThriftCompactInputProtocol<'a>> ReadThrift<'a, R> for ColumnOrder {
1216    fn read_thrift(prot: &mut R) -> Result<Self> {
1217        let field_ident = prot.read_field_begin(0)?;
1218        if field_ident.field_type == FieldType::Stop {
1219            return Err(general_err!("Received empty union from remote ColumnOrder"));
1220        }
1221        let ret = match field_ident.id {
1222            1 => {
1223                // NOTE: the sort order needs to be set correctly after parsing.
1224                prot.skip_empty_struct()?;
1225                Self::TYPE_DEFINED_ORDER(SortOrder::SIGNED)
1226            }
1227            2 => {
1228                prot.skip_empty_struct()?;
1229                Self::IEEE_754_TOTAL_ORDER
1230            }
1231            3 => {
1232                prot.skip_empty_struct()?;
1233                Self::INT96_TIMESTAMP_ORDER
1234            }
1235            _ => {
1236                prot.skip(field_ident.field_type)?;
1237                Self::UNKNOWN
1238            }
1239        };
1240        let field_ident = prot.read_field_begin(field_ident.id)?;
1241        if field_ident.field_type != FieldType::Stop {
1242            return Err(general_err!(
1243                "Received multiple fields for union from remote ColumnOrder"
1244            ));
1245        }
1246        Ok(ret)
1247    }
1248}
1249
1250impl WriteThrift for ColumnOrder {
1251    const ELEMENT_TYPE: ElementType = ElementType::Struct;
1252
1253    fn write_thrift<W: Write>(&self, writer: &mut ThriftCompactOutputProtocol<W>) -> Result<()> {
1254        match *self {
1255            Self::TYPE_DEFINED_ORDER(_) => {
1256                writer.write_field_begin(FieldType::Struct, 1, 0)?;
1257                writer.write_struct_end()?;
1258            }
1259            Self::IEEE_754_TOTAL_ORDER => {
1260                writer.write_field_begin(FieldType::Struct, 2, 0)?;
1261                writer.write_struct_end()?;
1262            }
1263            Self::INT96_TIMESTAMP_ORDER => {
1264                writer.write_field_begin(FieldType::Struct, 3, 0)?;
1265                writer.write_struct_end()?;
1266            }
1267            _ => return Err(general_err!("Attempt to write undefined ColumnOrder")),
1268        }
1269        // write end of struct for this union
1270        writer.write_struct_end()
1271    }
1272}
1273
1274// ----------------------------------------------------------------------
1275// Display handlers
1276
1277impl fmt::Display for Compression {
1278    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1279        write!(f, "{self:?}")
1280    }
1281}
1282
1283impl fmt::Display for SortOrder {
1284    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1285        write!(f, "{self:?}")
1286    }
1287}
1288
1289impl fmt::Display for ColumnOrder {
1290    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1291        write!(f, "{self:?}")
1292    }
1293}
1294
1295// ----------------------------------------------------------------------
1296// LogicalType <=> ConvertedType conversion
1297
1298// Note: To prevent type loss when converting from ConvertedType to LogicalType,
1299// the conversion from ConvertedType -> LogicalType is not implemented.
1300// Such type loss includes:
1301// - Not knowing the decimal scale and precision of ConvertedType
1302// - Time and timestamp nanosecond precision, that is not supported in ConvertedType.
1303
1304impl From<Option<LogicalType>> for ConvertedType {
1305    fn from(value: Option<LogicalType>) -> Self {
1306        match value {
1307            Some(value) => match value {
1308                LogicalType::String => ConvertedType::UTF8,
1309                LogicalType::Map => ConvertedType::MAP,
1310                LogicalType::List => ConvertedType::LIST,
1311                LogicalType::Enum => ConvertedType::ENUM,
1312                LogicalType::Decimal { .. } => ConvertedType::DECIMAL,
1313                LogicalType::Date => ConvertedType::DATE,
1314                LogicalType::Time(time) => match time.unit {
1315                    TimeUnit::MILLIS => ConvertedType::TIME_MILLIS,
1316                    TimeUnit::MICROS => ConvertedType::TIME_MICROS,
1317                    TimeUnit::NANOS => ConvertedType::NONE,
1318                },
1319                LogicalType::Timestamp(time) => match time.unit {
1320                    TimeUnit::MILLIS => ConvertedType::TIMESTAMP_MILLIS,
1321                    TimeUnit::MICROS => ConvertedType::TIMESTAMP_MICROS,
1322                    TimeUnit::NANOS => ConvertedType::NONE,
1323                },
1324                LogicalType::Integer(int_type) => match (int_type.bit_width, int_type.is_signed) {
1325                    (8, true) => ConvertedType::INT_8,
1326                    (16, true) => ConvertedType::INT_16,
1327                    (32, true) => ConvertedType::INT_32,
1328                    (64, true) => ConvertedType::INT_64,
1329                    (8, false) => ConvertedType::UINT_8,
1330                    (16, false) => ConvertedType::UINT_16,
1331                    (32, false) => ConvertedType::UINT_32,
1332                    (64, false) => ConvertedType::UINT_64,
1333                    (bit_width, is_signed) => panic!(
1334                        "Integer type bit_width={bit_width}, signed={is_signed} is not supported"
1335                    ),
1336                },
1337                LogicalType::Json => ConvertedType::JSON,
1338                LogicalType::Bson => ConvertedType::BSON,
1339                LogicalType::Uuid
1340                | LogicalType::Float16
1341                | LogicalType::Variant(_)
1342                | LogicalType::Geometry(_)
1343                | LogicalType::Geography(_)
1344                | LogicalType::_Unknown { .. }
1345                | LogicalType::Unknown => ConvertedType::NONE,
1346            },
1347            None => ConvertedType::NONE,
1348        }
1349    }
1350}
1351
1352// ----------------------------------------------------------------------
1353// String conversions for schema parsing.
1354
1355impl str::FromStr for Repetition {
1356    type Err = ParquetError;
1357
1358    fn from_str(s: &str) -> Result<Self> {
1359        match s {
1360            "REQUIRED" => Ok(Repetition::REQUIRED),
1361            "OPTIONAL" => Ok(Repetition::OPTIONAL),
1362            "REPEATED" => Ok(Repetition::REPEATED),
1363            other => Err(general_err!("Invalid parquet repetition {}", other)),
1364        }
1365    }
1366}
1367
1368impl str::FromStr for Type {
1369    type Err = ParquetError;
1370
1371    fn from_str(s: &str) -> Result<Self> {
1372        match s {
1373            "BOOLEAN" => Ok(Type::BOOLEAN),
1374            "INT32" => Ok(Type::INT32),
1375            "INT64" => Ok(Type::INT64),
1376            "INT96" => Ok(Type::INT96),
1377            "FLOAT" => Ok(Type::FLOAT),
1378            "DOUBLE" => Ok(Type::DOUBLE),
1379            "BYTE_ARRAY" | "BINARY" => Ok(Type::BYTE_ARRAY),
1380            "FIXED_LEN_BYTE_ARRAY" => Ok(Type::FIXED_LEN_BYTE_ARRAY),
1381            other => Err(general_err!("Invalid parquet type {}", other)),
1382        }
1383    }
1384}
1385
1386impl str::FromStr for ConvertedType {
1387    type Err = ParquetError;
1388
1389    fn from_str(s: &str) -> Result<Self> {
1390        match s {
1391            "NONE" => Ok(ConvertedType::NONE),
1392            "UTF8" => Ok(ConvertedType::UTF8),
1393            "MAP" => Ok(ConvertedType::MAP),
1394            "MAP_KEY_VALUE" => Ok(ConvertedType::MAP_KEY_VALUE),
1395            "LIST" => Ok(ConvertedType::LIST),
1396            "ENUM" => Ok(ConvertedType::ENUM),
1397            "DECIMAL" => Ok(ConvertedType::DECIMAL),
1398            "DATE" => Ok(ConvertedType::DATE),
1399            "TIME_MILLIS" => Ok(ConvertedType::TIME_MILLIS),
1400            "TIME_MICROS" => Ok(ConvertedType::TIME_MICROS),
1401            "TIMESTAMP_MILLIS" => Ok(ConvertedType::TIMESTAMP_MILLIS),
1402            "TIMESTAMP_MICROS" => Ok(ConvertedType::TIMESTAMP_MICROS),
1403            "UINT_8" => Ok(ConvertedType::UINT_8),
1404            "UINT_16" => Ok(ConvertedType::UINT_16),
1405            "UINT_32" => Ok(ConvertedType::UINT_32),
1406            "UINT_64" => Ok(ConvertedType::UINT_64),
1407            "INT_8" => Ok(ConvertedType::INT_8),
1408            "INT_16" => Ok(ConvertedType::INT_16),
1409            "INT_32" => Ok(ConvertedType::INT_32),
1410            "INT_64" => Ok(ConvertedType::INT_64),
1411            "JSON" => Ok(ConvertedType::JSON),
1412            "BSON" => Ok(ConvertedType::BSON),
1413            "INTERVAL" => Ok(ConvertedType::INTERVAL),
1414            other => Err(general_err!("Invalid parquet converted type {}", other)),
1415        }
1416    }
1417}
1418
1419impl str::FromStr for LogicalType {
1420    type Err = ParquetError;
1421
1422    fn from_str(s: &str) -> Result<Self> {
1423        match s {
1424            // The type is a placeholder that gets updated elsewhere
1425            "INTEGER" => Ok(LogicalType::integer(8, false)),
1426            "MAP" => Ok(LogicalType::Map),
1427            "LIST" => Ok(LogicalType::List),
1428            "ENUM" => Ok(LogicalType::Enum),
1429            "DECIMAL" => Ok(LogicalType::decimal(-1, -1)),
1430            "DATE" => Ok(LogicalType::Date),
1431            "TIME" => Ok(LogicalType::time(false, TimeUnit::MILLIS)),
1432            "TIMESTAMP" => Ok(LogicalType::timestamp(false, TimeUnit::MILLIS)),
1433            "STRING" => Ok(LogicalType::String),
1434            "JSON" => Ok(LogicalType::Json),
1435            "BSON" => Ok(LogicalType::Bson),
1436            "UUID" => Ok(LogicalType::Uuid),
1437            "UNKNOWN" => Ok(LogicalType::Unknown),
1438            "INTERVAL" => Err(general_err!(
1439                "Interval parquet logical type not yet supported"
1440            )),
1441            "FLOAT16" => Ok(LogicalType::Float16),
1442            "VARIANT" => Ok(LogicalType::variant(None)),
1443            "GEOMETRY" => Ok(LogicalType::geometry(None)),
1444            "GEOGRAPHY" => Ok(LogicalType::geography(
1445                None,
1446                Some(EdgeInterpolationAlgorithm::SPHERICAL),
1447            )),
1448            other => Err(general_err!("Invalid parquet logical type {}", other)),
1449        }
1450    }
1451}
1452
1453#[cfg(test)]
1454#[allow(deprecated)] // allow BIT_PACKED encoding for the whole test module
1455mod tests {
1456    use super::*;
1457    use crate::parquet_thrift::{ThriftSliceInputProtocol, tests::test_roundtrip};
1458
1459    #[test]
1460    fn test_display_type() {
1461        assert_eq!(Type::BOOLEAN.to_string(), "BOOLEAN");
1462        assert_eq!(Type::INT32.to_string(), "INT32");
1463        assert_eq!(Type::INT64.to_string(), "INT64");
1464        assert_eq!(Type::INT96.to_string(), "INT96");
1465        assert_eq!(Type::FLOAT.to_string(), "FLOAT");
1466        assert_eq!(Type::DOUBLE.to_string(), "DOUBLE");
1467        assert_eq!(Type::BYTE_ARRAY.to_string(), "BYTE_ARRAY");
1468        assert_eq!(
1469            Type::FIXED_LEN_BYTE_ARRAY.to_string(),
1470            "FIXED_LEN_BYTE_ARRAY"
1471        );
1472    }
1473
1474    #[test]
1475    fn test_from_string_into_type() {
1476        assert_eq!(
1477            Type::BOOLEAN.to_string().parse::<Type>().unwrap(),
1478            Type::BOOLEAN
1479        );
1480        assert_eq!(
1481            Type::INT32.to_string().parse::<Type>().unwrap(),
1482            Type::INT32
1483        );
1484        assert_eq!(
1485            Type::INT64.to_string().parse::<Type>().unwrap(),
1486            Type::INT64
1487        );
1488        assert_eq!(
1489            Type::INT96.to_string().parse::<Type>().unwrap(),
1490            Type::INT96
1491        );
1492        assert_eq!(
1493            Type::FLOAT.to_string().parse::<Type>().unwrap(),
1494            Type::FLOAT
1495        );
1496        assert_eq!(
1497            Type::DOUBLE.to_string().parse::<Type>().unwrap(),
1498            Type::DOUBLE
1499        );
1500        assert_eq!(
1501            Type::BYTE_ARRAY.to_string().parse::<Type>().unwrap(),
1502            Type::BYTE_ARRAY
1503        );
1504        assert_eq!("BINARY".parse::<Type>().unwrap(), Type::BYTE_ARRAY);
1505        assert_eq!(
1506            Type::FIXED_LEN_BYTE_ARRAY
1507                .to_string()
1508                .parse::<Type>()
1509                .unwrap(),
1510            Type::FIXED_LEN_BYTE_ARRAY
1511        );
1512    }
1513
1514    #[test]
1515    fn test_converted_type_roundtrip() {
1516        test_roundtrip(ConvertedType::UTF8);
1517        test_roundtrip(ConvertedType::MAP);
1518        test_roundtrip(ConvertedType::MAP_KEY_VALUE);
1519        test_roundtrip(ConvertedType::LIST);
1520        test_roundtrip(ConvertedType::ENUM);
1521        test_roundtrip(ConvertedType::DECIMAL);
1522        test_roundtrip(ConvertedType::DATE);
1523        test_roundtrip(ConvertedType::TIME_MILLIS);
1524        test_roundtrip(ConvertedType::TIME_MICROS);
1525        test_roundtrip(ConvertedType::TIMESTAMP_MILLIS);
1526        test_roundtrip(ConvertedType::TIMESTAMP_MICROS);
1527        test_roundtrip(ConvertedType::UINT_8);
1528        test_roundtrip(ConvertedType::UINT_16);
1529        test_roundtrip(ConvertedType::UINT_32);
1530        test_roundtrip(ConvertedType::UINT_64);
1531        test_roundtrip(ConvertedType::INT_8);
1532        test_roundtrip(ConvertedType::INT_16);
1533        test_roundtrip(ConvertedType::INT_32);
1534        test_roundtrip(ConvertedType::INT_64);
1535        test_roundtrip(ConvertedType::JSON);
1536        test_roundtrip(ConvertedType::BSON);
1537        test_roundtrip(ConvertedType::INTERVAL);
1538    }
1539
1540    #[test]
1541    fn test_read_invalid_converted_type() {
1542        let mut prot = ThriftSliceInputProtocol::new(&[0x7eu8]);
1543        let res = ConvertedType::read_thrift(&mut prot);
1544        assert!(res.is_err());
1545        assert_eq!(
1546            res.unwrap_err().to_string(),
1547            "Parquet error: Unexpected ConvertedType 63"
1548        );
1549    }
1550
1551    #[test]
1552    fn test_display_converted_type() {
1553        assert_eq!(ConvertedType::NONE.to_string(), "NONE");
1554        assert_eq!(ConvertedType::UTF8.to_string(), "UTF8");
1555        assert_eq!(ConvertedType::MAP.to_string(), "MAP");
1556        assert_eq!(ConvertedType::MAP_KEY_VALUE.to_string(), "MAP_KEY_VALUE");
1557        assert_eq!(ConvertedType::LIST.to_string(), "LIST");
1558        assert_eq!(ConvertedType::ENUM.to_string(), "ENUM");
1559        assert_eq!(ConvertedType::DECIMAL.to_string(), "DECIMAL");
1560        assert_eq!(ConvertedType::DATE.to_string(), "DATE");
1561        assert_eq!(ConvertedType::TIME_MILLIS.to_string(), "TIME_MILLIS");
1562        assert_eq!(ConvertedType::DATE.to_string(), "DATE");
1563        assert_eq!(ConvertedType::TIME_MICROS.to_string(), "TIME_MICROS");
1564        assert_eq!(
1565            ConvertedType::TIMESTAMP_MILLIS.to_string(),
1566            "TIMESTAMP_MILLIS"
1567        );
1568        assert_eq!(
1569            ConvertedType::TIMESTAMP_MICROS.to_string(),
1570            "TIMESTAMP_MICROS"
1571        );
1572        assert_eq!(ConvertedType::UINT_8.to_string(), "UINT_8");
1573        assert_eq!(ConvertedType::UINT_16.to_string(), "UINT_16");
1574        assert_eq!(ConvertedType::UINT_32.to_string(), "UINT_32");
1575        assert_eq!(ConvertedType::UINT_64.to_string(), "UINT_64");
1576        assert_eq!(ConvertedType::INT_8.to_string(), "INT_8");
1577        assert_eq!(ConvertedType::INT_16.to_string(), "INT_16");
1578        assert_eq!(ConvertedType::INT_32.to_string(), "INT_32");
1579        assert_eq!(ConvertedType::INT_64.to_string(), "INT_64");
1580        assert_eq!(ConvertedType::JSON.to_string(), "JSON");
1581        assert_eq!(ConvertedType::BSON.to_string(), "BSON");
1582        assert_eq!(ConvertedType::INTERVAL.to_string(), "INTERVAL");
1583        assert_eq!(ConvertedType::DECIMAL.to_string(), "DECIMAL")
1584    }
1585
1586    #[test]
1587    fn test_from_string_into_converted_type() {
1588        assert_eq!(
1589            ConvertedType::NONE
1590                .to_string()
1591                .parse::<ConvertedType>()
1592                .unwrap(),
1593            ConvertedType::NONE
1594        );
1595        assert_eq!(
1596            ConvertedType::UTF8
1597                .to_string()
1598                .parse::<ConvertedType>()
1599                .unwrap(),
1600            ConvertedType::UTF8
1601        );
1602        assert_eq!(
1603            ConvertedType::MAP
1604                .to_string()
1605                .parse::<ConvertedType>()
1606                .unwrap(),
1607            ConvertedType::MAP
1608        );
1609        assert_eq!(
1610            ConvertedType::MAP_KEY_VALUE
1611                .to_string()
1612                .parse::<ConvertedType>()
1613                .unwrap(),
1614            ConvertedType::MAP_KEY_VALUE
1615        );
1616        assert_eq!(
1617            ConvertedType::LIST
1618                .to_string()
1619                .parse::<ConvertedType>()
1620                .unwrap(),
1621            ConvertedType::LIST
1622        );
1623        assert_eq!(
1624            ConvertedType::ENUM
1625                .to_string()
1626                .parse::<ConvertedType>()
1627                .unwrap(),
1628            ConvertedType::ENUM
1629        );
1630        assert_eq!(
1631            ConvertedType::DECIMAL
1632                .to_string()
1633                .parse::<ConvertedType>()
1634                .unwrap(),
1635            ConvertedType::DECIMAL
1636        );
1637        assert_eq!(
1638            ConvertedType::DATE
1639                .to_string()
1640                .parse::<ConvertedType>()
1641                .unwrap(),
1642            ConvertedType::DATE
1643        );
1644        assert_eq!(
1645            ConvertedType::TIME_MILLIS
1646                .to_string()
1647                .parse::<ConvertedType>()
1648                .unwrap(),
1649            ConvertedType::TIME_MILLIS
1650        );
1651        assert_eq!(
1652            ConvertedType::TIME_MICROS
1653                .to_string()
1654                .parse::<ConvertedType>()
1655                .unwrap(),
1656            ConvertedType::TIME_MICROS
1657        );
1658        assert_eq!(
1659            ConvertedType::TIMESTAMP_MILLIS
1660                .to_string()
1661                .parse::<ConvertedType>()
1662                .unwrap(),
1663            ConvertedType::TIMESTAMP_MILLIS
1664        );
1665        assert_eq!(
1666            ConvertedType::TIMESTAMP_MICROS
1667                .to_string()
1668                .parse::<ConvertedType>()
1669                .unwrap(),
1670            ConvertedType::TIMESTAMP_MICROS
1671        );
1672        assert_eq!(
1673            ConvertedType::UINT_8
1674                .to_string()
1675                .parse::<ConvertedType>()
1676                .unwrap(),
1677            ConvertedType::UINT_8
1678        );
1679        assert_eq!(
1680            ConvertedType::UINT_16
1681                .to_string()
1682                .parse::<ConvertedType>()
1683                .unwrap(),
1684            ConvertedType::UINT_16
1685        );
1686        assert_eq!(
1687            ConvertedType::UINT_32
1688                .to_string()
1689                .parse::<ConvertedType>()
1690                .unwrap(),
1691            ConvertedType::UINT_32
1692        );
1693        assert_eq!(
1694            ConvertedType::UINT_64
1695                .to_string()
1696                .parse::<ConvertedType>()
1697                .unwrap(),
1698            ConvertedType::UINT_64
1699        );
1700        assert_eq!(
1701            ConvertedType::INT_8
1702                .to_string()
1703                .parse::<ConvertedType>()
1704                .unwrap(),
1705            ConvertedType::INT_8
1706        );
1707        assert_eq!(
1708            ConvertedType::INT_16
1709                .to_string()
1710                .parse::<ConvertedType>()
1711                .unwrap(),
1712            ConvertedType::INT_16
1713        );
1714        assert_eq!(
1715            ConvertedType::INT_32
1716                .to_string()
1717                .parse::<ConvertedType>()
1718                .unwrap(),
1719            ConvertedType::INT_32
1720        );
1721        assert_eq!(
1722            ConvertedType::INT_64
1723                .to_string()
1724                .parse::<ConvertedType>()
1725                .unwrap(),
1726            ConvertedType::INT_64
1727        );
1728        assert_eq!(
1729            ConvertedType::JSON
1730                .to_string()
1731                .parse::<ConvertedType>()
1732                .unwrap(),
1733            ConvertedType::JSON
1734        );
1735        assert_eq!(
1736            ConvertedType::BSON
1737                .to_string()
1738                .parse::<ConvertedType>()
1739                .unwrap(),
1740            ConvertedType::BSON
1741        );
1742        assert_eq!(
1743            ConvertedType::INTERVAL
1744                .to_string()
1745                .parse::<ConvertedType>()
1746                .unwrap(),
1747            ConvertedType::INTERVAL
1748        );
1749        assert_eq!(
1750            ConvertedType::DECIMAL
1751                .to_string()
1752                .parse::<ConvertedType>()
1753                .unwrap(),
1754            ConvertedType::DECIMAL
1755        )
1756    }
1757
1758    #[test]
1759    fn test_logical_to_converted_type() {
1760        let logical_none: Option<LogicalType> = None;
1761        assert_eq!(ConvertedType::from(logical_none), ConvertedType::NONE);
1762        assert_eq!(
1763            ConvertedType::from(Some(LogicalType::decimal(5, 20))),
1764            ConvertedType::DECIMAL
1765        );
1766        assert_eq!(
1767            ConvertedType::from(Some(LogicalType::Bson)),
1768            ConvertedType::BSON
1769        );
1770        assert_eq!(
1771            ConvertedType::from(Some(LogicalType::Json)),
1772            ConvertedType::JSON
1773        );
1774        assert_eq!(
1775            ConvertedType::from(Some(LogicalType::String)),
1776            ConvertedType::UTF8
1777        );
1778        assert_eq!(
1779            ConvertedType::from(Some(LogicalType::Date)),
1780            ConvertedType::DATE
1781        );
1782        assert_eq!(
1783            ConvertedType::from(Some(LogicalType::time(true, TimeUnit::MILLIS))),
1784            ConvertedType::TIME_MILLIS
1785        );
1786        assert_eq!(
1787            ConvertedType::from(Some(LogicalType::time(true, TimeUnit::MICROS))),
1788            ConvertedType::TIME_MICROS
1789        );
1790        assert_eq!(
1791            ConvertedType::from(Some(LogicalType::time(false, TimeUnit::NANOS))),
1792            ConvertedType::NONE
1793        );
1794        assert_eq!(
1795            ConvertedType::from(Some(LogicalType::timestamp(true, TimeUnit::MILLIS))),
1796            ConvertedType::TIMESTAMP_MILLIS
1797        );
1798        assert_eq!(
1799            ConvertedType::from(Some(LogicalType::timestamp(false, TimeUnit::MICROS))),
1800            ConvertedType::TIMESTAMP_MICROS
1801        );
1802        assert_eq!(
1803            ConvertedType::from(Some(LogicalType::timestamp(false, TimeUnit::NANOS))),
1804            ConvertedType::NONE
1805        );
1806        assert_eq!(
1807            ConvertedType::from(Some(LogicalType::integer(8, false))),
1808            ConvertedType::UINT_8
1809        );
1810        assert_eq!(
1811            ConvertedType::from(Some(LogicalType::integer(8, true))),
1812            ConvertedType::INT_8
1813        );
1814        assert_eq!(
1815            ConvertedType::from(Some(LogicalType::integer(16, false))),
1816            ConvertedType::UINT_16
1817        );
1818        assert_eq!(
1819            ConvertedType::from(Some(LogicalType::integer(16, true))),
1820            ConvertedType::INT_16
1821        );
1822        assert_eq!(
1823            ConvertedType::from(Some(LogicalType::integer(32, false))),
1824            ConvertedType::UINT_32
1825        );
1826        assert_eq!(
1827            ConvertedType::from(Some(LogicalType::integer(32, true))),
1828            ConvertedType::INT_32
1829        );
1830        assert_eq!(
1831            ConvertedType::from(Some(LogicalType::integer(64, false))),
1832            ConvertedType::UINT_64
1833        );
1834        assert_eq!(
1835            ConvertedType::from(Some(LogicalType::integer(64, true))),
1836            ConvertedType::INT_64
1837        );
1838        assert_eq!(
1839            ConvertedType::from(Some(LogicalType::List)),
1840            ConvertedType::LIST
1841        );
1842        assert_eq!(
1843            ConvertedType::from(Some(LogicalType::Map)),
1844            ConvertedType::MAP
1845        );
1846        assert_eq!(
1847            ConvertedType::from(Some(LogicalType::Uuid)),
1848            ConvertedType::NONE
1849        );
1850        assert_eq!(
1851            ConvertedType::from(Some(LogicalType::Enum)),
1852            ConvertedType::ENUM
1853        );
1854        assert_eq!(
1855            ConvertedType::from(Some(LogicalType::Float16)),
1856            ConvertedType::NONE
1857        );
1858        assert_eq!(
1859            ConvertedType::from(Some(LogicalType::variant(None))),
1860            ConvertedType::NONE
1861        );
1862        assert_eq!(
1863            ConvertedType::from(Some(LogicalType::geometry(None))),
1864            ConvertedType::NONE
1865        );
1866        assert_eq!(
1867            ConvertedType::from(Some(LogicalType::geography(None, Some(Default::default())))),
1868            ConvertedType::NONE
1869        );
1870        assert_eq!(
1871            ConvertedType::from(Some(LogicalType::Unknown)),
1872            ConvertedType::NONE
1873        );
1874    }
1875
1876    #[test]
1877    fn test_logical_type_roundtrip() {
1878        test_roundtrip(LogicalType::String);
1879        test_roundtrip(LogicalType::Map);
1880        test_roundtrip(LogicalType::List);
1881        test_roundtrip(LogicalType::Enum);
1882        test_roundtrip(LogicalType::decimal(0, 20));
1883        test_roundtrip(LogicalType::Date);
1884        test_roundtrip(LogicalType::time(true, TimeUnit::MICROS));
1885        test_roundtrip(LogicalType::time(false, TimeUnit::MILLIS));
1886        test_roundtrip(LogicalType::time(false, TimeUnit::NANOS));
1887        test_roundtrip(LogicalType::timestamp(false, TimeUnit::MICROS));
1888        test_roundtrip(LogicalType::timestamp(true, TimeUnit::MILLIS));
1889        test_roundtrip(LogicalType::timestamp(true, TimeUnit::NANOS));
1890        test_roundtrip(LogicalType::integer(8, true));
1891        test_roundtrip(LogicalType::integer(16, false));
1892        test_roundtrip(LogicalType::integer(32, true));
1893        test_roundtrip(LogicalType::integer(64, false));
1894        test_roundtrip(LogicalType::Json);
1895        test_roundtrip(LogicalType::Bson);
1896        test_roundtrip(LogicalType::Uuid);
1897        test_roundtrip(LogicalType::Float16);
1898        test_roundtrip(LogicalType::variant(Some(1)));
1899        test_roundtrip(LogicalType::variant(None));
1900        test_roundtrip(LogicalType::geometry(Some("foo".to_owned())));
1901        test_roundtrip(LogicalType::geometry(None));
1902        test_roundtrip(LogicalType::geography(
1903            Some("foo".to_owned()),
1904            Some(EdgeInterpolationAlgorithm::ANDOYER),
1905        ));
1906        test_roundtrip(LogicalType::geography(
1907            None,
1908            Some(EdgeInterpolationAlgorithm::KARNEY),
1909        ));
1910        test_roundtrip(LogicalType::geography(
1911            Some("foo".to_owned()),
1912            Some(EdgeInterpolationAlgorithm::SPHERICAL),
1913        ));
1914        test_roundtrip(LogicalType::geography(
1915            None,
1916            Some(EdgeInterpolationAlgorithm::SPHERICAL),
1917        ));
1918    }
1919
1920    #[test]
1921    fn test_display_repetition() {
1922        assert_eq!(Repetition::REQUIRED.to_string(), "REQUIRED");
1923        assert_eq!(Repetition::OPTIONAL.to_string(), "OPTIONAL");
1924        assert_eq!(Repetition::REPEATED.to_string(), "REPEATED");
1925    }
1926
1927    #[test]
1928    fn test_from_string_into_repetition() {
1929        assert_eq!(
1930            Repetition::REQUIRED
1931                .to_string()
1932                .parse::<Repetition>()
1933                .unwrap(),
1934            Repetition::REQUIRED
1935        );
1936        assert_eq!(
1937            Repetition::OPTIONAL
1938                .to_string()
1939                .parse::<Repetition>()
1940                .unwrap(),
1941            Repetition::OPTIONAL
1942        );
1943        assert_eq!(
1944            Repetition::REPEATED
1945                .to_string()
1946                .parse::<Repetition>()
1947                .unwrap(),
1948            Repetition::REPEATED
1949        );
1950    }
1951
1952    #[test]
1953    fn test_display_encoding() {
1954        assert_eq!(Encoding::PLAIN.to_string(), "PLAIN");
1955        assert_eq!(Encoding::PLAIN_DICTIONARY.to_string(), "PLAIN_DICTIONARY");
1956        assert_eq!(Encoding::RLE.to_string(), "RLE");
1957        assert_eq!(Encoding::BIT_PACKED.to_string(), "BIT_PACKED");
1958        assert_eq!(
1959            Encoding::DELTA_BINARY_PACKED.to_string(),
1960            "DELTA_BINARY_PACKED"
1961        );
1962        assert_eq!(
1963            Encoding::DELTA_LENGTH_BYTE_ARRAY.to_string(),
1964            "DELTA_LENGTH_BYTE_ARRAY"
1965        );
1966        assert_eq!(Encoding::DELTA_BYTE_ARRAY.to_string(), "DELTA_BYTE_ARRAY");
1967        assert_eq!(Encoding::RLE_DICTIONARY.to_string(), "RLE_DICTIONARY");
1968    }
1969
1970    #[test]
1971    fn test_compression_conversion() {
1972        assert_eq!(
1973            CompressionCodec::from(Compression::UNCOMPRESSED),
1974            CompressionCodec::UNCOMPRESSED
1975        );
1976        assert_eq!(
1977            CompressionCodec::from(Compression::SNAPPY),
1978            CompressionCodec::SNAPPY
1979        );
1980        assert_eq!(
1981            CompressionCodec::from(Compression::GZIP(Default::default())),
1982            CompressionCodec::GZIP
1983        );
1984        assert_eq!(
1985            CompressionCodec::from(Compression::LZO),
1986            CompressionCodec::LZO
1987        );
1988        assert_eq!(
1989            CompressionCodec::from(Compression::BROTLI(Default::default())),
1990            CompressionCodec::BROTLI
1991        );
1992        assert_eq!(
1993            CompressionCodec::from(Compression::LZ4),
1994            CompressionCodec::LZ4
1995        );
1996        assert_eq!(
1997            CompressionCodec::from(Compression::ZSTD(Default::default())),
1998            CompressionCodec::ZSTD
1999        );
2000        assert_eq!(
2001            CompressionCodec::from(Compression::LZ4_RAW),
2002            CompressionCodec::LZ4_RAW
2003        );
2004
2005        assert_eq!(
2006            Compression::from(CompressionCodec::UNCOMPRESSED),
2007            Compression::UNCOMPRESSED
2008        );
2009        assert_eq!(
2010            Compression::from(CompressionCodec::SNAPPY),
2011            Compression::SNAPPY
2012        );
2013        assert_eq!(
2014            Compression::from(CompressionCodec::GZIP),
2015            Compression::GZIP(Default::default())
2016        );
2017        assert_eq!(Compression::from(CompressionCodec::LZO), Compression::LZO);
2018        assert_eq!(
2019            Compression::from(CompressionCodec::BROTLI),
2020            Compression::BROTLI(Default::default())
2021        );
2022        assert_eq!(Compression::from(CompressionCodec::LZ4), Compression::LZ4);
2023        assert_eq!(
2024            Compression::from(CompressionCodec::ZSTD),
2025            Compression::ZSTD(Default::default())
2026        );
2027        assert_eq!(
2028            Compression::from(CompressionCodec::LZ4_RAW),
2029            Compression::LZ4_RAW
2030        );
2031    }
2032
2033    #[test]
2034    fn test_display_compression() {
2035        assert_eq!(Compression::UNCOMPRESSED.to_string(), "UNCOMPRESSED");
2036        assert_eq!(Compression::SNAPPY.to_string(), "SNAPPY");
2037        assert_eq!(
2038            Compression::GZIP(Default::default()).to_string(),
2039            "GZIP(GzipLevel(6))"
2040        );
2041        assert_eq!(Compression::LZO.to_string(), "LZO");
2042        assert_eq!(
2043            Compression::BROTLI(Default::default()).to_string(),
2044            "BROTLI(BrotliLevel(1))"
2045        );
2046        assert_eq!(Compression::LZ4.to_string(), "LZ4");
2047        assert_eq!(
2048            Compression::ZSTD(Default::default()).to_string(),
2049            "ZSTD(ZstdLevel(1))"
2050        );
2051    }
2052
2053    #[test]
2054    fn test_display_page_type() {
2055        assert_eq!(PageType::DATA_PAGE.to_string(), "DATA_PAGE");
2056        assert_eq!(PageType::INDEX_PAGE.to_string(), "INDEX_PAGE");
2057        assert_eq!(PageType::DICTIONARY_PAGE.to_string(), "DICTIONARY_PAGE");
2058        assert_eq!(PageType::DATA_PAGE_V2.to_string(), "DATA_PAGE_V2");
2059    }
2060
2061    #[test]
2062    fn test_display_sort_order() {
2063        assert_eq!(SortOrder::SIGNED.to_string(), "SIGNED");
2064        assert_eq!(SortOrder::UNSIGNED.to_string(), "UNSIGNED");
2065        assert_eq!(SortOrder::UNDEFINED.to_string(), "UNDEFINED");
2066        assert_eq!(SortOrder::TOTAL_ORDER.to_string(), "TOTAL_ORDER");
2067        assert_eq!(SortOrder::INT96_TIMESTAMP.to_string(), "INT96_TIMESTAMP");
2068    }
2069
2070    #[test]
2071    fn test_display_column_order() {
2072        assert_eq!(
2073            ColumnOrder::TYPE_DEFINED_ORDER(SortOrder::SIGNED).to_string(),
2074            "TYPE_DEFINED_ORDER(SIGNED)"
2075        );
2076        assert_eq!(
2077            ColumnOrder::TYPE_DEFINED_ORDER(SortOrder::UNSIGNED).to_string(),
2078            "TYPE_DEFINED_ORDER(UNSIGNED)"
2079        );
2080        assert_eq!(
2081            ColumnOrder::TYPE_DEFINED_ORDER(SortOrder::UNDEFINED).to_string(),
2082            "TYPE_DEFINED_ORDER(UNDEFINED)"
2083        );
2084        assert_eq!(
2085            ColumnOrder::IEEE_754_TOTAL_ORDER.to_string(),
2086            "IEEE_754_TOTAL_ORDER"
2087        );
2088        assert_eq!(
2089            ColumnOrder::INT96_TIMESTAMP_ORDER.to_string(),
2090            "INT96_TIMESTAMP_ORDER"
2091        );
2092        assert_eq!(ColumnOrder::UNDEFINED.to_string(), "UNDEFINED");
2093    }
2094
2095    #[test]
2096    fn test_column_order_roundtrip() {
2097        // SortOrder::SIGNED is the default on read.
2098        test_roundtrip(ColumnOrder::TYPE_DEFINED_ORDER(SortOrder::SIGNED))
2099    }
2100
2101    #[test]
2102    fn test_column_order_get_logical_type_sort_order() {
2103        // Helper to check the order in a list of values.
2104        // Only logical type is checked.
2105        fn check_sort_order(types: Vec<LogicalType>, expected_order: SortOrder) {
2106            for tpe in types {
2107                assert_eq!(
2108                    ColumnOrder::column_order_for_type(
2109                        Some(&tpe),
2110                        ConvertedType::NONE,
2111                        Type::BYTE_ARRAY
2112                    )
2113                    .sort_order(),
2114                    expected_order
2115                );
2116            }
2117        }
2118
2119        // Unsigned comparison (physical type does not matter)
2120        let unsigned = vec![
2121            LogicalType::String,
2122            LogicalType::Json,
2123            LogicalType::Bson,
2124            LogicalType::Enum,
2125            LogicalType::Uuid,
2126            LogicalType::integer(8, false),
2127            LogicalType::integer(16, false),
2128            LogicalType::integer(32, false),
2129            LogicalType::integer(64, false),
2130        ];
2131        check_sort_order(unsigned, SortOrder::UNSIGNED);
2132
2133        // Signed comparison (physical type does not matter)
2134        let signed = vec![
2135            LogicalType::integer(8, true),
2136            LogicalType::integer(16, true),
2137            LogicalType::integer(32, true),
2138            LogicalType::integer(64, true),
2139            LogicalType::decimal(20, 4),
2140            LogicalType::Date,
2141            LogicalType::time(false, TimeUnit::MILLIS),
2142            LogicalType::time(false, TimeUnit::MICROS),
2143            LogicalType::time(true, TimeUnit::NANOS),
2144            LogicalType::timestamp(false, TimeUnit::MILLIS),
2145            LogicalType::timestamp(false, TimeUnit::MICROS),
2146            LogicalType::timestamp(true, TimeUnit::NANOS),
2147        ];
2148        check_sort_order(signed, SortOrder::SIGNED);
2149
2150        let float = vec![LogicalType::Float16];
2151        check_sort_order(float, SortOrder::TOTAL_ORDER);
2152
2153        // Undefined comparison
2154        let undefined = vec![
2155            LogicalType::List,
2156            LogicalType::Map,
2157            LogicalType::variant(None),
2158            LogicalType::geometry(None),
2159            LogicalType::geography(None, Some(Default::default())),
2160        ];
2161        check_sort_order(undefined, SortOrder::UNDEFINED);
2162    }
2163
2164    #[test]
2165    fn test_column_order_get_converted_type_sort_order() {
2166        // Helper to check the order in a list of values.
2167        // Only converted type is checked.
2168        fn check_sort_order(types: Vec<ConvertedType>, expected_order: SortOrder) {
2169            for tpe in types {
2170                assert_eq!(
2171                    ColumnOrder::column_order_for_type(None, tpe, Type::BYTE_ARRAY).sort_order(),
2172                    expected_order
2173                );
2174            }
2175        }
2176
2177        // Unsigned comparison (physical type does not matter)
2178        let unsigned = vec![
2179            ConvertedType::UTF8,
2180            ConvertedType::JSON,
2181            ConvertedType::BSON,
2182            ConvertedType::ENUM,
2183            ConvertedType::UINT_8,
2184            ConvertedType::UINT_16,
2185            ConvertedType::UINT_32,
2186            ConvertedType::UINT_64,
2187        ];
2188        check_sort_order(unsigned, SortOrder::UNSIGNED);
2189
2190        // Signed comparison (physical type does not matter)
2191        let signed = vec![
2192            ConvertedType::INT_8,
2193            ConvertedType::INT_16,
2194            ConvertedType::INT_32,
2195            ConvertedType::INT_64,
2196            ConvertedType::DECIMAL,
2197            ConvertedType::DATE,
2198            ConvertedType::TIME_MILLIS,
2199            ConvertedType::TIME_MICROS,
2200            ConvertedType::TIMESTAMP_MILLIS,
2201            ConvertedType::TIMESTAMP_MICROS,
2202        ];
2203        check_sort_order(signed, SortOrder::SIGNED);
2204
2205        // Undefined comparison
2206        let undefined = vec![
2207            ConvertedType::LIST,
2208            ConvertedType::MAP,
2209            ConvertedType::MAP_KEY_VALUE,
2210            ConvertedType::INTERVAL,
2211        ];
2212        check_sort_order(undefined, SortOrder::UNDEFINED);
2213
2214        // Check None logical type
2215        // This should return a sort order for byte array type.
2216        check_sort_order(vec![ConvertedType::NONE], SortOrder::UNSIGNED);
2217    }
2218
2219    #[test]
2220    fn test_column_order_get_default_sort_order() {
2221        // Comparison based on physical type
2222        assert_eq!(
2223            ColumnOrder::get_default_sort_order(Type::BOOLEAN, true),
2224            SortOrder::UNSIGNED
2225        );
2226        assert_eq!(
2227            ColumnOrder::get_default_sort_order(Type::INT32, true),
2228            SortOrder::SIGNED
2229        );
2230        assert_eq!(
2231            ColumnOrder::get_default_sort_order(Type::INT64, true),
2232            SortOrder::SIGNED
2233        );
2234        assert_eq!(
2235            ColumnOrder::get_default_sort_order(Type::INT96, true),
2236            SortOrder::UNDEFINED
2237        );
2238        assert_eq!(
2239            ColumnOrder::get_default_sort_order(Type::INT96, false),
2240            SortOrder::INT96_TIMESTAMP
2241        );
2242        assert_eq!(
2243            ColumnOrder::get_default_sort_order(Type::FLOAT, false),
2244            SortOrder::TOTAL_ORDER
2245        );
2246        assert_eq!(
2247            ColumnOrder::get_default_sort_order(Type::DOUBLE, false),
2248            SortOrder::TOTAL_ORDER
2249        );
2250        assert_eq!(
2251            ColumnOrder::get_default_sort_order(Type::FLOAT, true),
2252            SortOrder::SIGNED
2253        );
2254        assert_eq!(
2255            ColumnOrder::get_default_sort_order(Type::DOUBLE, true),
2256            SortOrder::SIGNED
2257        );
2258        assert_eq!(
2259            ColumnOrder::get_default_sort_order(Type::BYTE_ARRAY, true),
2260            SortOrder::UNSIGNED
2261        );
2262        assert_eq!(
2263            ColumnOrder::get_default_sort_order(Type::FIXED_LEN_BYTE_ARRAY, true),
2264            SortOrder::UNSIGNED
2265        );
2266    }
2267
2268    #[test]
2269    fn test_column_order_sort_order() {
2270        assert_eq!(
2271            ColumnOrder::TYPE_DEFINED_ORDER(SortOrder::SIGNED).sort_order(),
2272            SortOrder::SIGNED
2273        );
2274        assert_eq!(
2275            ColumnOrder::TYPE_DEFINED_ORDER(SortOrder::UNSIGNED).sort_order(),
2276            SortOrder::UNSIGNED
2277        );
2278        assert_eq!(
2279            ColumnOrder::TYPE_DEFINED_ORDER(SortOrder::UNDEFINED).sort_order(),
2280            SortOrder::UNDEFINED
2281        );
2282        assert_eq!(
2283            ColumnOrder::IEEE_754_TOTAL_ORDER.sort_order(),
2284            SortOrder::TOTAL_ORDER
2285        );
2286        assert_eq!(
2287            ColumnOrder::INT96_TIMESTAMP_ORDER.sort_order(),
2288            SortOrder::INT96_TIMESTAMP
2289        );
2290        assert_eq!(ColumnOrder::UNDEFINED.sort_order(), SortOrder::SIGNED);
2291    }
2292
2293    #[test]
2294    fn test_parse_encoding() {
2295        let mut encoding: Encoding = "PLAIN".parse().unwrap();
2296        assert_eq!(encoding, Encoding::PLAIN);
2297        encoding = "PLAIN_DICTIONARY".parse().unwrap();
2298        assert_eq!(encoding, Encoding::PLAIN_DICTIONARY);
2299        encoding = "RLE".parse().unwrap();
2300        assert_eq!(encoding, Encoding::RLE);
2301        encoding = "BIT_PACKED".parse().unwrap();
2302        assert_eq!(encoding, Encoding::BIT_PACKED);
2303        encoding = "DELTA_BINARY_PACKED".parse().unwrap();
2304        assert_eq!(encoding, Encoding::DELTA_BINARY_PACKED);
2305        encoding = "DELTA_LENGTH_BYTE_ARRAY".parse().unwrap();
2306        assert_eq!(encoding, Encoding::DELTA_LENGTH_BYTE_ARRAY);
2307        encoding = "DELTA_BYTE_ARRAY".parse().unwrap();
2308        assert_eq!(encoding, Encoding::DELTA_BYTE_ARRAY);
2309        encoding = "RLE_DICTIONARY".parse().unwrap();
2310        assert_eq!(encoding, Encoding::RLE_DICTIONARY);
2311        encoding = "BYTE_STREAM_SPLIT".parse().unwrap();
2312        assert_eq!(encoding, Encoding::BYTE_STREAM_SPLIT);
2313
2314        // test lowercase
2315        encoding = "byte_stream_split".parse().unwrap();
2316        assert_eq!(encoding, Encoding::BYTE_STREAM_SPLIT);
2317
2318        // test unknown string
2319        match "plain_xxx".parse::<Encoding>() {
2320            Ok(e) => {
2321                panic!("Should not be able to parse {e:?}");
2322            }
2323            Err(e) => {
2324                assert_eq!(e.to_string(), "Parquet error: unknown encoding: plain_xxx");
2325            }
2326        }
2327    }
2328
2329    #[test]
2330    fn test_parse_compression() {
2331        let mut compress: Compression = "snappy".parse().unwrap();
2332        assert_eq!(compress, Compression::SNAPPY);
2333        compress = "lzo".parse().unwrap();
2334        assert_eq!(compress, Compression::LZO);
2335        compress = "zstd(3)".parse().unwrap();
2336        assert_eq!(compress, Compression::ZSTD(ZstdLevel::try_new(3).unwrap()));
2337        compress = "zstd(-3)".parse().unwrap();
2338        assert_eq!(compress, Compression::ZSTD(ZstdLevel::try_new(-3).unwrap()));
2339        compress = "LZ4_RAW".parse().unwrap();
2340        assert_eq!(compress, Compression::LZ4_RAW);
2341        compress = "uncompressed".parse().unwrap();
2342        assert_eq!(compress, Compression::UNCOMPRESSED);
2343        compress = "snappy".parse().unwrap();
2344        assert_eq!(compress, Compression::SNAPPY);
2345        compress = "gzip(9)".parse().unwrap();
2346        assert_eq!(compress, Compression::GZIP(GzipLevel::try_new(9).unwrap()));
2347        compress = "lzo".parse().unwrap();
2348        assert_eq!(compress, Compression::LZO);
2349        compress = "brotli(3)".parse().unwrap();
2350        assert_eq!(
2351            compress,
2352            Compression::BROTLI(BrotliLevel::try_new(3).unwrap())
2353        );
2354        compress = "lz4".parse().unwrap();
2355        assert_eq!(compress, Compression::LZ4);
2356
2357        // test unknown compression
2358        let mut err = "plain_xxx".parse::<Encoding>().unwrap_err();
2359        assert_eq!(
2360            err.to_string(),
2361            "Parquet error: unknown encoding: plain_xxx"
2362        );
2363
2364        // test invalid compress level
2365        err = "gzip(-10)".parse::<Encoding>().unwrap_err();
2366        assert_eq!(
2367            err.to_string(),
2368            "Parquet error: unknown encoding: gzip(-10)"
2369        );
2370    }
2371
2372    #[test]
2373    fn test_display_boundary_order() {
2374        assert_eq!(BoundaryOrder::ASCENDING.to_string(), "ASCENDING");
2375        assert_eq!(BoundaryOrder::DESCENDING.to_string(), "DESCENDING");
2376        assert_eq!(BoundaryOrder::UNORDERED.to_string(), "UNORDERED");
2377    }
2378
2379    #[test]
2380    fn test_display_edge_algo() {
2381        assert_eq!(
2382            EdgeInterpolationAlgorithm::SPHERICAL.to_string(),
2383            "SPHERICAL"
2384        );
2385        assert_eq!(EdgeInterpolationAlgorithm::VINCENTY.to_string(), "VINCENTY");
2386        assert_eq!(EdgeInterpolationAlgorithm::THOMAS.to_string(), "THOMAS");
2387        assert_eq!(EdgeInterpolationAlgorithm::ANDOYER.to_string(), "ANDOYER");
2388        assert_eq!(EdgeInterpolationAlgorithm::KARNEY.to_string(), "KARNEY");
2389    }
2390
2391    #[test]
2392    fn test_from_str_edge_algo() {
2393        assert_eq!(
2394            "spHErical".parse::<EdgeInterpolationAlgorithm>().unwrap(),
2395            EdgeInterpolationAlgorithm::SPHERICAL
2396        );
2397        assert_eq!(
2398            "vinceNTY".parse::<EdgeInterpolationAlgorithm>().unwrap(),
2399            EdgeInterpolationAlgorithm::VINCENTY
2400        );
2401        assert_eq!(
2402            "tHOmas".parse::<EdgeInterpolationAlgorithm>().unwrap(),
2403            EdgeInterpolationAlgorithm::THOMAS
2404        );
2405        assert_eq!(
2406            "anDOYEr".parse::<EdgeInterpolationAlgorithm>().unwrap(),
2407            EdgeInterpolationAlgorithm::ANDOYER
2408        );
2409        assert_eq!(
2410            "kaRNey".parse::<EdgeInterpolationAlgorithm>().unwrap(),
2411            EdgeInterpolationAlgorithm::KARNEY
2412        );
2413        assert!(
2414            "does not exist"
2415                .parse::<EdgeInterpolationAlgorithm>()
2416                .is_err()
2417        );
2418    }
2419
2420    fn encodings_roundtrip(mut encodings: Vec<Encoding>) {
2421        encodings.sort();
2422        let mask = EncodingMask::new_from_encodings(encodings.iter());
2423        assert!(mask.all_set(encodings.iter()));
2424        let v = mask.encodings().collect::<Vec<_>>();
2425        assert_eq!(v, encodings);
2426    }
2427
2428    #[test]
2429    fn test_encoding_roundtrip() {
2430        encodings_roundtrip(
2431            [
2432                Encoding::RLE,
2433                Encoding::PLAIN,
2434                Encoding::DELTA_BINARY_PACKED,
2435            ]
2436            .into(),
2437        );
2438        encodings_roundtrip([Encoding::RLE_DICTIONARY, Encoding::PLAIN_DICTIONARY].into());
2439        encodings_roundtrip([].into());
2440        let encodings = [
2441            Encoding::PLAIN,
2442            Encoding::BIT_PACKED,
2443            Encoding::RLE,
2444            Encoding::DELTA_BINARY_PACKED,
2445            Encoding::DELTA_BYTE_ARRAY,
2446            Encoding::DELTA_LENGTH_BYTE_ARRAY,
2447            Encoding::PLAIN_DICTIONARY,
2448            Encoding::RLE_DICTIONARY,
2449            Encoding::BYTE_STREAM_SPLIT,
2450        ];
2451        encodings_roundtrip(encodings.into());
2452    }
2453
2454    #[test]
2455    fn test_invalid_encoding_mask() {
2456        // any set bits higher than the max should trigger an error
2457        let res = EncodingMask::try_new(-1);
2458        assert!(res.is_err());
2459        let err = res.unwrap_err();
2460        assert_eq!(
2461            err.to_string(),
2462            "Parquet error: Attempt to create invalid mask: 0xffffffff"
2463        );
2464
2465        // test that GROUP_VAR_INT is disallowed
2466        let res = EncodingMask::try_new(2);
2467        assert!(res.is_err());
2468        let err = res.unwrap_err();
2469        assert_eq!(
2470            err.to_string(),
2471            "Parquet error: Attempt to create invalid mask: 0x2"
2472        );
2473    }
2474
2475    #[test]
2476    fn test_encoding_mask_is_only() {
2477        let mask = EncodingMask::new_from_encodings([Encoding::PLAIN].iter());
2478        assert!(mask.is_only(Encoding::PLAIN));
2479
2480        let mask =
2481            EncodingMask::new_from_encodings([Encoding::PLAIN, Encoding::PLAIN_DICTIONARY].iter());
2482        assert!(!mask.is_only(Encoding::PLAIN));
2483    }
2484}