parquet_variant/
variant.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
18pub use self::decimal::{VariantDecimal4, VariantDecimal8, VariantDecimal16, VariantDecimalType};
19pub use self::list::VariantList;
20pub use self::metadata::{EMPTY_VARIANT_METADATA, EMPTY_VARIANT_METADATA_BYTES, VariantMetadata};
21pub use self::object::VariantObject;
22
23// Publically export types used in the API
24pub use half::f16;
25pub use uuid::Uuid;
26
27use crate::decoder::{
28    self, VariantBasicType, VariantPrimitiveType, get_basic_type, get_primitive_type,
29};
30use crate::path::{VariantPath, VariantPathElement};
31use crate::utils::{first_byte_from_slice, fits_precision, slice_from_slice};
32use std::ops::Deref;
33
34use arrow_schema::ArrowError;
35use chrono::{DateTime, NaiveDate, NaiveDateTime, NaiveTime, Timelike, Utc};
36
37mod decimal;
38mod list;
39mod metadata;
40mod object;
41
42const MAX_SHORT_STRING_BYTES: usize = 0x3F;
43
44/// A Variant [`ShortString`]
45///
46/// This implementation is a zero cost wrapper over `&str` that ensures
47/// the length of the underlying string is a valid Variant short string (63 bytes or less)
48#[derive(Debug, Clone, Copy, PartialEq)]
49pub struct ShortString<'a>(pub(crate) &'a str);
50
51impl<'a> ShortString<'a> {
52    /// Attempts to interpret `value` as a variant short string value.
53    ///
54    /// # Errors
55    ///
56    /// Returns an error if  `value` is longer than the maximum allowed length
57    /// of a Variant short string (63 bytes).
58    pub fn try_new(value: &'a str) -> Result<Self, ArrowError> {
59        if value.len() > MAX_SHORT_STRING_BYTES {
60            return Err(ArrowError::InvalidArgumentError(format!(
61                "value is larger than {MAX_SHORT_STRING_BYTES} bytes"
62            )));
63        }
64
65        Ok(Self(value))
66    }
67
68    /// Returns the underlying Variant short string as a &str
69    pub fn as_str(&self) -> &'a str {
70        self.0
71    }
72}
73
74impl<'a> From<ShortString<'a>> for &'a str {
75    fn from(value: ShortString<'a>) -> Self {
76        value.0
77    }
78}
79
80impl<'a> TryFrom<&'a str> for ShortString<'a> {
81    type Error = ArrowError;
82
83    fn try_from(value: &'a str) -> Result<Self, Self::Error> {
84        Self::try_new(value)
85    }
86}
87
88impl AsRef<str> for ShortString<'_> {
89    fn as_ref(&self) -> &str {
90        self.0
91    }
92}
93
94impl Deref for ShortString<'_> {
95    type Target = str;
96
97    fn deref(&self) -> &Self::Target {
98        self.0
99    }
100}
101
102/// Represents a [Parquet Variant]
103///
104/// The lifetimes `'m` and `'v` are for metadata and value buffers, respectively.
105///
106/// # Background
107///
108/// The [specification] says:
109///
110/// The Variant Binary Encoding allows representation of semi-structured data
111/// (e.g. JSON) in a form that can be efficiently queried by path. The design is
112/// intended to allow efficient access to nested data even in the presence of
113/// very wide or deep structures.
114///
115/// Another motivation for the representation is that (aside from metadata) each
116/// nested Variant value is contiguous and self-contained. For example, in a
117/// Variant containing an Array of Variant values, the representation of an
118/// inner Variant value, when paired with the metadata of the full variant, is
119/// itself a valid Variant.
120///
121/// When stored in Parquet files, Variant fields can also be *shredded*. Shredding
122/// refers to extracting some elements of the variant into separate columns for
123/// more efficient extraction/filter pushdown. The [Variant Shredding
124/// specification] describes the details of shredding Variant values as typed
125/// Parquet columns.
126///
127/// A Variant represents a type that contains one of:
128///
129/// * Primitive: A type and corresponding value (e.g. INT, STRING)
130///
131/// * Array: An ordered list of Variant values
132///
133/// * Object: An unordered collection of string/Variant pairs (i.e. key/value
134///   pairs). An object may not contain duplicate keys.
135///
136/// # Encoding
137///
138/// A Variant is encoded with 2 binary values, the value and the metadata. The
139/// metadata stores a header and an optional dictionary of field names which are
140/// referred to by offset in the value. The value is a binary representation of
141/// the actual data, and varies depending on the type.
142///
143/// # Design Goals
144///
145/// The design goals of the Rust API are as follows:
146/// 1. Speed / Zero copy access (no `clone`ing is required)
147/// 2. Safety
148/// 3. Follow standard Rust conventions
149///
150/// [Parquet Variant]: https://github.com/apache/parquet-format/blob/master/VariantEncoding.md
151/// [specification]: https://github.com/apache/parquet-format/blob/master/VariantEncoding.md
152/// [Variant Shredding specification]: https://github.com/apache/parquet-format/blob/master/VariantShredding.md
153///
154/// # Examples:
155///
156/// ## Creating `Variant` from Rust Types
157/// ```
158/// use parquet_variant::Variant;
159/// // variants can be directly constructed
160/// let variant = Variant::Int32(123);
161/// // or constructed via `From` impls
162/// assert_eq!(variant, Variant::from(123i32));
163/// ```
164/// ## Creating `Variant` from metadata and value
165/// ```
166/// # use parquet_variant::{Variant, VariantMetadata};
167/// let metadata = [0x01, 0x00, 0x00];
168/// let value = [0x09, 0x48, 0x49];
169/// // parse the header metadata
170/// assert_eq!(
171///   Variant::from("HI"),
172///   Variant::new(&metadata, &value)
173/// );
174/// ```
175///
176/// ## Using `Variant` values
177/// ```
178/// # use parquet_variant::Variant;
179/// # let variant = Variant::Int32(123);
180/// // variants can be used in match statements like normal enums
181/// match variant {
182///   Variant::Int32(i) => println!("Integer: {}", i),
183///   Variant::String(s) => println!("String: {}", s),
184///   _ => println!("Other variant"),
185/// }
186/// ```
187///
188/// # Validation
189///
190/// Every instance of variant is either _valid_ or _invalid_. depending on whether the
191/// underlying bytes are a valid encoding of a variant value (see below).
192///
193/// Instances produced by [`Self::try_new`], [`Self::try_new_with_metadata`], or [`Self::with_full_validation`]
194/// are fully _validated_. They always contain _valid_ data, and infallible accesses such as
195/// iteration and indexing are panic-free. The validation cost is `O(m + v)` where `m` and
196/// `v` are the number of bytes in the metadata and value buffers, respectively.
197///
198/// Instances produced by [`Self::new`] and [`Self::new_with_metadata`] are _unvalidated_ and so
199/// they may contain either _valid_ or _invalid_ data. Infallible accesses to variant objects and
200/// arrays, such as iteration and indexing will panic if the underlying bytes are _invalid_, and
201/// fallible alternatives are provided as panic-free alternatives. [`Self::with_full_validation`] can also be
202/// used to _validate_ an _unvalidated_ instance, if desired.
203///
204/// _Unvalidated_ instances can be constructed in constant time. This can be useful if the caller
205/// knows the underlying bytes were already validated previously, or if the caller intends to
206/// perform a small number of (fallible) accesses to a large variant value.
207///
208/// A _validated_ variant value guarantees that the associated [metadata] and all nested [object]
209/// and [array] values are _valid_. Primitive variant subtypes are always _valid_ by construction.
210///
211/// # Safety
212///
213/// Even an _invalid_ variant value is still _safe_ to use in the Rust sense. Accessing it with
214/// infallible methods may cause panics but will never lead to undefined behavior.
215///
216/// [metadata]: VariantMetadata#Validation
217/// [object]: VariantObject#Validation
218/// [array]: VariantList#Validation
219#[derive(Clone, PartialEq)]
220pub enum Variant<'m, 'v> {
221    /// Primitive type: Null
222    Null,
223    /// Primitive (type_id=1): INT(8, SIGNED)
224    Int8(i8),
225    /// Primitive (type_id=1): INT(16, SIGNED)
226    Int16(i16),
227    /// Primitive (type_id=1): INT(32, SIGNED)
228    Int32(i32),
229    /// Primitive (type_id=1): INT(64, SIGNED)
230    Int64(i64),
231    /// Primitive (type_id=1): DATE
232    Date(NaiveDate),
233    /// Primitive (type_id=1): TIMESTAMP(isAdjustedToUTC=true, MICROS)
234    TimestampMicros(DateTime<Utc>),
235    /// Primitive (type_id=1): TIMESTAMP(isAdjustedToUTC=false, MICROS)
236    TimestampNtzMicros(NaiveDateTime),
237    /// Primitive (type_id=1): TIMESTAMP(isAdjustedToUTC=true, NANOS)
238    TimestampNanos(DateTime<Utc>),
239    /// Primitive (type_id=1): TIMESTAMP(isAdjustedToUTC=false, NANOS)
240    TimestampNtzNanos(NaiveDateTime),
241    /// Primitive (type_id=1): DECIMAL(precision, scale) 32-bits
242    Decimal4(VariantDecimal4),
243    /// Primitive (type_id=1): DECIMAL(precision, scale) 64-bits
244    Decimal8(VariantDecimal8),
245    /// Primitive (type_id=1): DECIMAL(precision, scale) 128-bits
246    Decimal16(VariantDecimal16),
247    /// Primitive (type_id=1): FLOAT
248    Float(f32),
249    /// Primitive (type_id=1): DOUBLE
250    Double(f64),
251    /// Primitive (type_id=1): BOOLEAN (true)
252    BooleanTrue,
253    /// Primitive (type_id=1): BOOLEAN (false)
254    BooleanFalse,
255    // Note: only need the *value* buffer for these types
256    /// Primitive (type_id=1): BINARY
257    Binary(&'v [u8]),
258    /// Primitive (type_id=1): STRING
259    String(&'v str),
260    /// Primitive (type_id=1): TIME(isAdjustedToUTC=false, MICROS)
261    Time(NaiveTime),
262    /// Primitive (type_id=1): UUID
263    Uuid(Uuid),
264    /// Short String (type_id=2): STRING
265    ShortString(ShortString<'v>),
266    // need both metadata & value
267    /// Object (type_id=3): N/A
268    Object(VariantObject<'m, 'v>),
269    /// Array (type_id=4): N/A
270    List(VariantList<'m, 'v>),
271}
272
273// We don't want this to grow because it could hurt performance of a frequently-created type.
274const _: () = crate::utils::expect_size_of::<Variant>(80);
275
276impl<'m, 'v> Variant<'m, 'v> {
277    /// Attempts to interpret a metadata and value buffer pair as a new `Variant`.
278    ///
279    /// The instance is fully [validated].
280    ///
281    /// # Example
282    /// ```
283    /// use parquet_variant::{Variant, VariantMetadata};
284    /// let metadata = [0x01, 0x00, 0x00];
285    /// let value = [0x09, 0x48, 0x49];
286    /// // parse the header metadata
287    /// assert_eq!(
288    ///   Variant::from("HI"),
289    ///   Variant::try_new(&metadata, &value).unwrap()
290    /// );
291    /// ```
292    ///
293    /// [validated]: Self#Validation
294    pub fn try_new(metadata: &'m [u8], value: &'v [u8]) -> Result<Self, ArrowError> {
295        let metadata = VariantMetadata::try_new(metadata)?;
296        Self::try_new_with_metadata(metadata, value)
297    }
298
299    /// Attempts to interpret a metadata and value buffer pair as a new `Variant`.
300    ///
301    /// The instance is [unvalidated].
302    ///
303    /// # Example
304    /// ```
305    /// use parquet_variant::{Variant, VariantMetadata};
306    /// let metadata = [0x01, 0x00, 0x00];
307    /// let value = [0x09, 0x48, 0x49];
308    /// // parse the header metadata
309    /// assert_eq!(
310    ///   Variant::from("HI"),
311    ///   Variant::new(&metadata, &value)
312    /// );
313    /// ```
314    ///
315    /// [unvalidated]: Self#Validation
316    pub fn new(metadata: &'m [u8], value: &'v [u8]) -> Self {
317        let metadata = VariantMetadata::try_new_with_shallow_validation(metadata)
318            .expect("Invalid variant metadata");
319        Self::try_new_with_metadata_and_shallow_validation(metadata, value)
320            .expect("Invalid variant data")
321    }
322
323    /// Create a new variant with existing metadata.
324    ///
325    /// The instance is fully [validated].
326    ///
327    /// # Example
328    /// ```
329    /// # use parquet_variant::{Variant, VariantMetadata};
330    /// let metadata = [0x01, 0x00, 0x00];
331    /// let value = [0x09, 0x48, 0x49];
332    /// // parse the header metadata first
333    /// let metadata = VariantMetadata::new(&metadata);
334    /// assert_eq!(
335    ///   Variant::from("HI"),
336    ///   Variant::try_new_with_metadata(metadata, &value).unwrap()
337    /// );
338    /// ```
339    ///
340    /// [validated]: Self#Validation
341    pub fn try_new_with_metadata(
342        metadata: VariantMetadata<'m>,
343        value: &'v [u8],
344    ) -> Result<Self, ArrowError> {
345        Self::try_new_with_metadata_and_shallow_validation(metadata, value)?.with_full_validation()
346    }
347
348    /// Similar to [`Self::try_new_with_metadata`], but [unvalidated].
349    ///
350    /// [unvalidated]: Self#Validation
351    pub fn new_with_metadata(metadata: VariantMetadata<'m>, value: &'v [u8]) -> Self {
352        Self::try_new_with_metadata_and_shallow_validation(metadata, value)
353            .expect("Invalid variant")
354    }
355
356    // The actual constructor, which only performs shallow (constant-time) validation.
357    fn try_new_with_metadata_and_shallow_validation(
358        metadata: VariantMetadata<'m>,
359        value: &'v [u8],
360    ) -> Result<Self, ArrowError> {
361        let value_metadata = first_byte_from_slice(value)?;
362        let value_data = slice_from_slice(value, 1..)?;
363        let new_self = match get_basic_type(value_metadata) {
364            VariantBasicType::Primitive => match get_primitive_type(value_metadata)? {
365                VariantPrimitiveType::Null => Variant::Null,
366                VariantPrimitiveType::Int8 => Variant::Int8(decoder::decode_int8(value_data)?),
367                VariantPrimitiveType::Int16 => Variant::Int16(decoder::decode_int16(value_data)?),
368                VariantPrimitiveType::Int32 => Variant::Int32(decoder::decode_int32(value_data)?),
369                VariantPrimitiveType::Int64 => Variant::Int64(decoder::decode_int64(value_data)?),
370                VariantPrimitiveType::Decimal4 => {
371                    let (integer, scale) = decoder::decode_decimal4(value_data)?;
372                    Variant::Decimal4(VariantDecimal4::try_new(integer, scale)?)
373                }
374                VariantPrimitiveType::Decimal8 => {
375                    let (integer, scale) = decoder::decode_decimal8(value_data)?;
376                    Variant::Decimal8(VariantDecimal8::try_new(integer, scale)?)
377                }
378                VariantPrimitiveType::Decimal16 => {
379                    let (integer, scale) = decoder::decode_decimal16(value_data)?;
380                    Variant::Decimal16(VariantDecimal16::try_new(integer, scale)?)
381                }
382                VariantPrimitiveType::Float => Variant::Float(decoder::decode_float(value_data)?),
383                VariantPrimitiveType::Double => {
384                    Variant::Double(decoder::decode_double(value_data)?)
385                }
386                VariantPrimitiveType::BooleanTrue => Variant::BooleanTrue,
387                VariantPrimitiveType::BooleanFalse => Variant::BooleanFalse,
388                VariantPrimitiveType::Date => Variant::Date(decoder::decode_date(value_data)?),
389                VariantPrimitiveType::TimestampMicros => {
390                    Variant::TimestampMicros(decoder::decode_timestamp_micros(value_data)?)
391                }
392                VariantPrimitiveType::TimestampNtzMicros => {
393                    Variant::TimestampNtzMicros(decoder::decode_timestampntz_micros(value_data)?)
394                }
395                VariantPrimitiveType::TimestampNanos => {
396                    Variant::TimestampNanos(decoder::decode_timestamp_nanos(value_data)?)
397                }
398                VariantPrimitiveType::TimestampNtzNanos => {
399                    Variant::TimestampNtzNanos(decoder::decode_timestampntz_nanos(value_data)?)
400                }
401                VariantPrimitiveType::Uuid => Variant::Uuid(decoder::decode_uuid(value_data)?),
402                VariantPrimitiveType::Binary => {
403                    Variant::Binary(decoder::decode_binary(value_data)?)
404                }
405                VariantPrimitiveType::String => {
406                    Variant::String(decoder::decode_long_string(value_data)?)
407                }
408                VariantPrimitiveType::Time => Variant::Time(decoder::decode_time_ntz(value_data)?),
409            },
410            VariantBasicType::ShortString => {
411                Variant::ShortString(decoder::decode_short_string(value_metadata, value_data)?)
412            }
413            VariantBasicType::Object => Variant::Object(
414                VariantObject::try_new_with_shallow_validation(metadata, value)?,
415            ),
416            VariantBasicType::Array => Variant::List(VariantList::try_new_with_shallow_validation(
417                metadata, value,
418            )?),
419        };
420        Ok(new_self)
421    }
422
423    /// True if this variant instance has already been [validated].
424    ///
425    /// [validated]: Self#Validation
426    pub fn is_fully_validated(&self) -> bool {
427        match self {
428            Variant::List(list) => list.is_fully_validated(),
429            Variant::Object(obj) => obj.is_fully_validated(),
430            _ => true,
431        }
432    }
433
434    /// Recursively validates this variant value, ensuring that infallible access will not panic due
435    /// to invalid bytes.
436    ///
437    /// Variant leaf values are always valid by construction, but [objects] and [arrays] can be
438    /// constructed in unvalidated (and potentially invalid) state.
439    ///
440    /// If [`Self::is_fully_validated`] is true, validation is a no-op. Otherwise, the cost is `O(m + v)`
441    /// where `m` and `v` are the sizes of metadata and value buffers, respectively.
442    ///
443    /// [objects]: VariantObject#Validation
444    /// [arrays]: VariantList#Validation
445    pub fn with_full_validation(self) -> Result<Self, ArrowError> {
446        use Variant::*;
447        match self {
448            List(list) => list.with_full_validation().map(List),
449            Object(obj) => obj.with_full_validation().map(Object),
450            _ => Ok(self),
451        }
452    }
453
454    /// Converts this variant to `()` if it is null.
455    ///
456    /// Returns `Some(())` for null variants,
457    /// `None` for non-null variants.
458    ///
459    /// # Examples
460    ///
461    /// ```
462    /// use parquet_variant::Variant;
463    ///
464    /// // you can extract `()` from a null variant
465    /// let v1 = Variant::from(());
466    /// assert_eq!(v1.as_null(), Some(()));
467    ///
468    /// // but not from other variants
469    /// let v2 = Variant::from("hello!");
470    /// assert_eq!(v2.as_null(), None);
471    /// ```
472    pub fn as_null(&self) -> Option<()> {
473        matches!(self, Variant::Null).then_some(())
474    }
475
476    /// Converts this variant to a `bool` if possible.
477    ///
478    /// Returns `Some(bool)` for boolean variants,
479    /// `None` for non-boolean variants.
480    ///
481    /// # Examples
482    ///
483    /// ```
484    /// use parquet_variant::Variant;
485    ///
486    /// // you can extract a bool from the true variant
487    /// let v1 = Variant::from(true);
488    /// assert_eq!(v1.as_boolean(), Some(true));
489    ///
490    /// // and the false variant
491    /// let v2 = Variant::from(false);
492    /// assert_eq!(v2.as_boolean(), Some(false));
493    ///
494    /// // but not from other variants
495    /// let v3 = Variant::from("hello!");
496    /// assert_eq!(v3.as_boolean(), None);
497    /// ```
498    pub fn as_boolean(&self) -> Option<bool> {
499        match self {
500            Variant::BooleanTrue => Some(true),
501            Variant::BooleanFalse => Some(false),
502            _ => None,
503        }
504    }
505
506    /// Converts this variant to a `NaiveDate` if possible.
507    ///
508    /// Returns `Some(NaiveDate)` for date variants,
509    /// `None` for non-date variants.
510    ///
511    /// # Examples
512    ///
513    /// ```
514    /// use parquet_variant::Variant;
515    /// use chrono::NaiveDate;
516    ///
517    /// // you can extract a NaiveDate from a date variant
518    /// let date = NaiveDate::from_ymd_opt(2025, 4, 12).unwrap();
519    /// let v1 = Variant::from(date);
520    /// assert_eq!(v1.as_naive_date(), Some(date));
521    ///
522    /// // but not from other variants
523    /// let v2 = Variant::from("hello!");
524    /// assert_eq!(v2.as_naive_date(), None);
525    /// ```
526    pub fn as_naive_date(&self) -> Option<NaiveDate> {
527        if let Variant::Date(d) = self {
528            Some(*d)
529        } else {
530            None
531        }
532    }
533
534    /// Converts this variant to a `DateTime<Utc>` if possible.
535    ///
536    /// Returns `Some(DateTime<Utc>)` for [`Variant::TimestampMicros`] variants,
537    /// `None` for other variants.
538    ///
539    /// # Examples
540    ///
541    /// ```
542    /// use parquet_variant::Variant;
543    /// use chrono::NaiveDate;
544    ///
545    /// // you can extract a DateTime<Utc> from a UTC-adjusted variant
546    /// let datetime = NaiveDate::from_ymd_opt(2025, 4, 16)
547    ///     .unwrap()
548    ///     .and_hms_milli_opt(12, 34, 56, 780)
549    ///     .unwrap()
550    ///     .and_utc();
551    /// let v1 = Variant::from(datetime);
552    /// assert_eq!(v1.as_timestamp_micros(), Some(datetime));
553    ///
554    /// // but not for other variants.
555    /// let datetime_nanos = NaiveDate::from_ymd_opt(2025, 8, 14)
556    ///     .unwrap()
557    ///     .and_hms_nano_opt(12, 33, 54, 123456789)
558    ///     .unwrap()
559    ///     .and_utc();
560    /// let v2 = Variant::from(datetime_nanos);
561    /// assert_eq!(v2.as_timestamp_micros(), None);
562    /// ```
563    pub fn as_timestamp_micros(&self) -> Option<DateTime<Utc>> {
564        match *self {
565            Variant::TimestampMicros(d) => Some(d),
566            _ => None,
567        }
568    }
569
570    /// Converts this variant to a `NaiveDateTime` if possible.
571    ///
572    /// Returns `Some(NaiveDateTime)` for [`Variant::TimestampNtzMicros`] variants,
573    /// `None` for other variants.
574    ///
575    /// # Examples
576    ///
577    /// ```
578    /// use parquet_variant::Variant;
579    /// use chrono::NaiveDate;
580    ///
581    /// // you can extract a NaiveDateTime from a non-UTC-adjusted variant
582    /// let datetime = NaiveDate::from_ymd_opt(2025, 4, 16)
583    ///     .unwrap()
584    ///     .and_hms_milli_opt(12, 34, 56, 780)
585    ///     .unwrap();
586    /// let v1 = Variant::from(datetime);
587    /// assert_eq!(v1.as_timestamp_ntz_micros(), Some(datetime));
588    ///
589    /// // but not for other variants.
590    /// let datetime_nanos = NaiveDate::from_ymd_opt(2025, 8, 14)
591    ///     .unwrap()
592    ///     .and_hms_nano_opt(12, 33, 54, 123456789)
593    ///     .unwrap();
594    /// let v2 = Variant::from(datetime_nanos);
595    /// assert_eq!(v2.as_timestamp_micros(), None);
596    /// ```
597    pub fn as_timestamp_ntz_micros(&self) -> Option<NaiveDateTime> {
598        match *self {
599            Variant::TimestampNtzMicros(d) => Some(d),
600            _ => None,
601        }
602    }
603
604    /// Converts this variant to a `DateTime<Utc>` if possible.
605    ///
606    /// Returns `Some(DateTime<Utc>)` for timestamp variants,
607    /// `None` for other variants.
608    ///
609    /// # Examples
610    ///
611    /// ```
612    /// use parquet_variant::Variant;
613    /// use chrono::NaiveDate;
614    ///
615    /// // you can extract a DateTime<Utc> from a UTC-adjusted nanosecond-precision variant
616    /// let datetime = NaiveDate::from_ymd_opt(2025, 4, 16)
617    ///     .unwrap()
618    ///     .and_hms_nano_opt(12, 34, 56, 789123456)
619    ///     .unwrap()
620    ///     .and_utc();
621    /// let v1 = Variant::from(datetime);
622    /// assert_eq!(v1.as_timestamp_nanos(), Some(datetime));
623    ///
624    /// // or from UTC-adjusted microsecond-precision variant
625    /// let datetime_micros = NaiveDate::from_ymd_opt(2025, 8, 14)
626    ///     .unwrap()
627    ///     .and_hms_milli_opt(12, 33, 54, 123)
628    ///     .unwrap()
629    ///     .and_utc();
630    /// // this will convert to `Variant::TimestampMicros`.
631    /// let v2 = Variant::from(datetime_micros);
632    /// assert_eq!(v2.as_timestamp_nanos(), Some(datetime_micros));
633    ///
634    /// // but not for other variants.
635    /// let v3 = Variant::from("hello!");
636    /// assert_eq!(v3.as_timestamp_nanos(), None);
637    /// ```
638    pub fn as_timestamp_nanos(&self) -> Option<DateTime<Utc>> {
639        match *self {
640            Variant::TimestampNanos(d) | Variant::TimestampMicros(d) => Some(d),
641            _ => None,
642        }
643    }
644
645    /// Converts this variant to a `NaiveDateTime` if possible.
646    ///
647    /// Returns `Some(NaiveDateTime)` for timestamp variants,
648    /// `None` for other variants.
649    ///
650    /// # Examples
651    ///
652    /// ```
653    /// use parquet_variant::Variant;
654    /// use chrono::NaiveDate;
655    ///
656    /// // you can extract a NaiveDateTime from a non-UTC-adjusted variant
657    /// let datetime = NaiveDate::from_ymd_opt(2025, 4, 16)
658    ///     .unwrap()
659    ///     .and_hms_nano_opt(12, 34, 56, 789123456)
660    ///     .unwrap();
661    /// let v1 = Variant::from(datetime);
662    /// assert_eq!(v1.as_timestamp_ntz_nanos(), Some(datetime));
663    ///
664    /// // or from a microsecond-precision non-UTC-adjusted variant
665    /// let datetime_micros = NaiveDate::from_ymd_opt(2025, 8, 14)
666    ///     .unwrap()
667    ///     .and_hms_milli_opt(12, 33, 54, 123)
668    ///     .unwrap();
669    /// // this will convert to `Variant::TimestampMicros`.
670    /// let v2 = Variant::from(datetime_micros);
671    /// assert_eq!(v2.as_timestamp_ntz_nanos(), Some(datetime_micros));
672    ///
673    /// // but not for other variants.
674    /// let v3 = Variant::from("hello!");
675    /// assert_eq!(v3.as_timestamp_ntz_nanos(), None);
676    /// ```
677    pub fn as_timestamp_ntz_nanos(&self) -> Option<NaiveDateTime> {
678        match *self {
679            Variant::TimestampNtzNanos(d) | Variant::TimestampNtzMicros(d) => Some(d),
680            _ => None,
681        }
682    }
683
684    /// Converts this variant to a `&[u8]` if possible.
685    ///
686    /// Returns `Some(&[u8])` for binary variants,
687    /// `None` for non-binary variants.
688    ///
689    /// # Examples
690    ///
691    /// ```
692    /// use parquet_variant::Variant;
693    ///
694    /// // you can extract a byte slice from a binary variant
695    /// let data = b"hello!";
696    /// let v1 = Variant::Binary(data);
697    /// assert_eq!(v1.as_u8_slice(), Some(data.as_slice()));
698    ///
699    /// // but not from other variant types
700    /// let v2 = Variant::from(123i64);
701    /// assert_eq!(v2.as_u8_slice(), None);
702    /// ```
703    pub fn as_u8_slice(&'v self) -> Option<&'v [u8]> {
704        if let Variant::Binary(d) = self {
705            Some(d)
706        } else {
707            None
708        }
709    }
710
711    /// Converts this variant to a `&str` if possible.
712    ///
713    /// Returns `Some(&str)` for string variants (both regular and short strings),
714    /// `None` for non-string variants.
715    ///
716    /// # Examples
717    ///
718    /// ```
719    /// use parquet_variant::Variant;
720    ///
721    /// // you can extract a string from string variants
722    /// let s = "hello!";
723    /// let v1 = Variant::from(s);
724    /// assert_eq!(v1.as_string(), Some(s));
725    ///
726    /// // but not from other variants
727    /// let v2 = Variant::from(123i64);
728    /// assert_eq!(v2.as_string(), None);
729    /// ```
730    pub fn as_string(&'v self) -> Option<&'v str> {
731        match self {
732            Variant::String(s) | Variant::ShortString(ShortString(s)) => Some(s),
733            _ => None,
734        }
735    }
736
737    /// Converts this variant to a `uuid hyphenated string` if possible.
738    ///
739    /// Returns `Some(String)` for UUID variants, `None` for non-UUID variants.
740    ///
741    /// # Examples
742    ///
743    /// ```
744    /// use parquet_variant::Variant;
745    ///
746    /// // You can extract a UUID from a UUID variant
747    /// let s = uuid::Uuid::parse_str("67e55044-10b1-426f-9247-bb680e5fe0c8").unwrap();
748    /// let v1 = Variant::Uuid(s);
749    /// assert_eq!(s, v1.as_uuid().unwrap());
750    /// assert_eq!("67e55044-10b1-426f-9247-bb680e5fe0c8", v1.as_uuid().unwrap().to_string());
751    ///
752    /// //but not from other variants
753    /// let v2 = Variant::from(1234);
754    /// assert_eq!(None, v2.as_uuid())
755    /// ```
756    pub fn as_uuid(&self) -> Option<Uuid> {
757        match self {
758            Variant::Uuid(u) => Some(*u),
759            _ => None,
760        }
761    }
762
763    /// Converts this variant to an `i8` if possible.
764    ///
765    /// Returns `Some(i8)` for integer variants that fit in `i8` range,
766    /// `None` for non-integer variants or values that would overflow.
767    ///
768    /// # Examples
769    ///
770    /// ```
771    /// use parquet_variant::Variant;
772    ///
773    /// // you can read an int64 variant into an i8 if it fits
774    /// let v1 = Variant::from(123i64);
775    /// assert_eq!(v1.as_int8(), Some(123i8));
776    ///
777    /// // but not if it would overflow
778    /// let v2 = Variant::from(1234i64);
779    /// assert_eq!(v2.as_int8(), None);
780    ///
781    /// // or if the variant cannot be cast into an integer
782    /// let v3 = Variant::from("hello!");
783    /// assert_eq!(v3.as_int8(), None);
784    /// ```
785    pub fn as_int8(&self) -> Option<i8> {
786        match *self {
787            Variant::Int8(i) => Some(i),
788            Variant::Int16(i) => i.try_into().ok(),
789            Variant::Int32(i) => i.try_into().ok(),
790            Variant::Int64(i) => i.try_into().ok(),
791            Variant::Decimal4(d) if d.scale() == 0 => d.integer().try_into().ok(),
792            Variant::Decimal8(d) if d.scale() == 0 => d.integer().try_into().ok(),
793            Variant::Decimal16(d) if d.scale() == 0 => d.integer().try_into().ok(),
794            _ => None,
795        }
796    }
797
798    /// Converts this variant to an `i16` if possible.
799    ///
800    /// Returns `Some(i16)` for integer variants that fit in `i16` range,
801    /// `None` for non-integer variants or values that would overflow.
802    ///
803    /// # Examples
804    ///
805    /// ```
806    /// use parquet_variant::Variant;
807    ///
808    /// // you can read an int64 variant into an i16 if it fits
809    /// let v1 = Variant::from(123i64);
810    /// assert_eq!(v1.as_int16(), Some(123i16));
811    ///
812    /// // but not if it would overflow
813    /// let v2 = Variant::from(123456i64);
814    /// assert_eq!(v2.as_int16(), None);
815    ///
816    /// // or if the variant cannot be cast into an integer
817    /// let v3 = Variant::from("hello!");
818    /// assert_eq!(v3.as_int16(), None);
819    /// ```
820    pub fn as_int16(&self) -> Option<i16> {
821        match *self {
822            Variant::Int8(i) => Some(i.into()),
823            Variant::Int16(i) => Some(i),
824            Variant::Int32(i) => i.try_into().ok(),
825            Variant::Int64(i) => i.try_into().ok(),
826            Variant::Decimal4(d) if d.scale() == 0 => d.integer().try_into().ok(),
827            Variant::Decimal8(d) if d.scale() == 0 => d.integer().try_into().ok(),
828            Variant::Decimal16(d) if d.scale() == 0 => d.integer().try_into().ok(),
829            _ => None,
830        }
831    }
832
833    /// Converts this variant to an `i32` if possible.
834    ///
835    /// Returns `Some(i32)` for integer variants that fit in `i32` range,
836    /// `None` for non-integer variants or values that would overflow.
837    ///
838    /// # Examples
839    ///
840    /// ```
841    /// use parquet_variant::Variant;
842    ///
843    /// // you can read an int64 variant into an i32 if it fits
844    /// let v1 = Variant::from(123i64);
845    /// assert_eq!(v1.as_int32(), Some(123i32));
846    ///
847    /// // but not if it would overflow
848    /// let v2 = Variant::from(12345678901i64);
849    /// assert_eq!(v2.as_int32(), None);
850    ///
851    /// // or if the variant cannot be cast into an integer
852    /// let v3 = Variant::from("hello!");
853    /// assert_eq!(v3.as_int32(), None);
854    /// ```
855    pub fn as_int32(&self) -> Option<i32> {
856        match *self {
857            Variant::Int8(i) => Some(i.into()),
858            Variant::Int16(i) => Some(i.into()),
859            Variant::Int32(i) => Some(i),
860            Variant::Int64(i) => i.try_into().ok(),
861            Variant::Decimal4(d) if d.scale() == 0 => Some(d.integer()),
862            Variant::Decimal8(d) if d.scale() == 0 => d.integer().try_into().ok(),
863            Variant::Decimal16(d) if d.scale() == 0 => d.integer().try_into().ok(),
864            _ => None,
865        }
866    }
867
868    /// Converts this variant to an `i64` if possible.
869    ///
870    /// Returns `Some(i64)` for integer variants that fit in `i64` range,
871    /// `None` for non-integer variants or values that would overflow.
872    ///
873    /// # Examples
874    ///
875    /// ```
876    /// use parquet_variant::Variant;
877    ///
878    /// // you can read an int64 variant into an i64
879    /// let v1 = Variant::from(123i64);
880    /// assert_eq!(v1.as_int64(), Some(123i64));
881    ///
882    /// // but not a variant that cannot be cast into an integer
883    /// let v2 = Variant::from("hello!");
884    /// assert_eq!(v2.as_int64(), None);
885    /// ```
886    pub fn as_int64(&self) -> Option<i64> {
887        match *self {
888            Variant::Int8(i) => Some(i.into()),
889            Variant::Int16(i) => Some(i.into()),
890            Variant::Int32(i) => Some(i.into()),
891            Variant::Int64(i) => Some(i),
892            Variant::Decimal4(d) if d.scale() == 0 => Some(d.integer().into()),
893            Variant::Decimal8(d) if d.scale() == 0 => Some(d.integer()),
894            Variant::Decimal16(d) if d.scale() == 0 => d.integer().try_into().ok(),
895            _ => None,
896        }
897    }
898
899    fn generic_convert_unsigned_primitive<T>(&self) -> Option<T>
900    where
901        T: TryFrom<i8> + TryFrom<i16> + TryFrom<i32> + TryFrom<i64> + TryFrom<i128>,
902    {
903        match *self {
904            Variant::Int8(i) => i.try_into().ok(),
905            Variant::Int16(i) => i.try_into().ok(),
906            Variant::Int32(i) => i.try_into().ok(),
907            Variant::Int64(i) => i.try_into().ok(),
908            Variant::Decimal4(d) if d.scale() == 0 => d.integer().try_into().ok(),
909            Variant::Decimal8(d) if d.scale() == 0 => d.integer().try_into().ok(),
910            Variant::Decimal16(d) if d.scale() == 0 => d.integer().try_into().ok(),
911            _ => None,
912        }
913    }
914
915    /// Converts this variant to a `u8` if possible.
916    ///
917    /// Returns `Some(u8)` for integer variants that fit in `u8`
918    /// `None` for non-integer variants or values that would overflow.
919    ///
920    /// # Examples
921    ///
922    /// ```
923    ///  use parquet_variant::{Variant, VariantDecimal4};
924    ///
925    ///  // you can read an int64 variant into an u8
926    ///  let v1 = Variant::from(123i64);
927    ///  assert_eq!(v1.as_u8(), Some(123u8));
928    ///
929    ///  // or a Decimal4 with scale 0 into u8
930    ///  let d = VariantDecimal4::try_new(26, 0).unwrap();
931    ///  let v2 = Variant::from(d);
932    ///  assert_eq!(v2.as_u8(), Some(26u8));
933    ///
934    ///  // but not a variant that can't fit into the range
935    ///  let v3 = Variant::from(-1);
936    ///  assert_eq!(v3.as_u8(), None);
937    ///
938    ///  // not a variant that decimal with scale not equal to zero
939    ///  let d = VariantDecimal4::try_new(1, 2).unwrap();
940    ///  let v4 = Variant::from(d);
941    ///  assert_eq!(v4.as_u8(), None);
942    ///
943    ///  // or not a variant that cannot be cast into an integer
944    ///  let v5 = Variant::from("hello!");
945    ///  assert_eq!(v5.as_u8(), None);
946    /// ```
947    pub fn as_u8(&self) -> Option<u8> {
948        self.generic_convert_unsigned_primitive::<u8>()
949    }
950
951    /// Converts this variant to an `u16` if possible.
952    ///
953    /// Returns `Some(u16)` for integer variants that fit in `u16`
954    /// `None` for non-integer variants or values that would overflow.
955    ///
956    /// # Examples
957    ///
958    /// ```
959    ///  use parquet_variant::{Variant, VariantDecimal4};
960    ///
961    ///  // you can read an int64 variant into an u16
962    ///  let v1 = Variant::from(123i64);
963    ///  assert_eq!(v1.as_u16(), Some(123u16));
964    ///
965    ///  // or a Decimal4 with scale 0 into u8
966    ///  let d = VariantDecimal4::try_new(u16::MAX as i32, 0).unwrap();
967    ///  let v2 = Variant::from(d);
968    ///  assert_eq!(v2.as_u16(), Some(u16::MAX));
969    ///
970    ///  // but not a variant that can't fit into the range
971    ///  let v3 = Variant::from(-1);
972    ///  assert_eq!(v3.as_u16(), None);
973    ///
974    ///  // not a variant that decimal with scale not equal to zero
975    ///  let d = VariantDecimal4::try_new(1, 2).unwrap();
976    ///  let v4 = Variant::from(d);
977    ///  assert_eq!(v4.as_u16(), None);
978    ///
979    ///  // or not a variant that cannot be cast into an integer
980    ///  let v5 = Variant::from("hello!");
981    ///  assert_eq!(v5.as_u16(), None);
982    /// ```
983    pub fn as_u16(&self) -> Option<u16> {
984        self.generic_convert_unsigned_primitive::<u16>()
985    }
986
987    /// Converts this variant to an `u32` if possible.
988    ///
989    /// Returns `Some(u32)` for integer variants that fit in `u32`
990    /// `None` for non-integer variants or values that would overflow.
991    ///
992    /// # Examples
993    ///
994    /// ```
995    ///  use parquet_variant::{Variant, VariantDecimal8};
996    ///
997    ///  // you can read an int64 variant into an u32
998    ///  let v1 = Variant::from(123i64);
999    ///  assert_eq!(v1.as_u32(), Some(123u32));
1000    ///
1001    ///  // or a Decimal4 with scale 0 into u8
1002    ///  let d = VariantDecimal8::try_new(u32::MAX as i64, 0).unwrap();
1003    ///  let v2 = Variant::from(d);
1004    ///  assert_eq!(v2.as_u32(), Some(u32::MAX));
1005    ///
1006    ///  // but not a variant that can't fit into the range
1007    ///  let v3 = Variant::from(-1);
1008    ///  assert_eq!(v3.as_u32(), None);
1009    ///
1010    ///  // not a variant that decimal with scale not equal to zero
1011    ///  let d = VariantDecimal8::try_new(1, 2).unwrap();
1012    ///  let v4 = Variant::from(d);
1013    ///  assert_eq!(v4.as_u32(), None);
1014    ///
1015    ///  // or not a variant that cannot be cast into an integer
1016    ///  let v5 = Variant::from("hello!");
1017    ///  assert_eq!(v5.as_u32(), None);
1018    /// ```
1019    pub fn as_u32(&self) -> Option<u32> {
1020        self.generic_convert_unsigned_primitive::<u32>()
1021    }
1022
1023    /// Converts this variant to an `u64` if possible.
1024    ///
1025    /// Returns `Some(u64)` for integer variants that fit in `u64`
1026    /// `None` for non-integer variants or values that would overflow.
1027    ///
1028    /// # Examples
1029    ///
1030    /// ```
1031    ///  use parquet_variant::{Variant, VariantDecimal16};
1032    ///
1033    ///  // you can read an int64 variant into an u64
1034    ///  let v1 = Variant::from(123i64);
1035    ///  assert_eq!(v1.as_u64(), Some(123u64));
1036    ///
1037    ///  // or a Decimal16 with scale 0 into u8
1038    ///  let d = VariantDecimal16::try_new(u64::MAX as i128, 0).unwrap();
1039    ///  let v2 = Variant::from(d);
1040    ///  assert_eq!(v2.as_u64(), Some(u64::MAX));
1041    ///
1042    ///  // but not a variant that can't fit into the range
1043    ///  let v3 = Variant::from(-1);
1044    ///  assert_eq!(v3.as_u64(), None);
1045    ///
1046    ///  // not a variant that decimal with scale not equal to zero
1047    /// let d = VariantDecimal16::try_new(1, 2).unwrap();
1048    ///  let v4 = Variant::from(d);
1049    ///  assert_eq!(v4.as_u64(), None);
1050    ///
1051    ///  // or not a variant that cannot be cast into an integer
1052    ///  let v5 = Variant::from("hello!");
1053    ///  assert_eq!(v5.as_u64(), None);
1054    /// ```
1055    pub fn as_u64(&self) -> Option<u64> {
1056        self.generic_convert_unsigned_primitive::<u64>()
1057    }
1058
1059    /// Converts this variant to tuple with a 4-byte unscaled value if possible.
1060    ///
1061    /// Returns `Some((i32, u8))` for decimal variants where the unscaled value
1062    /// fits in `i32` range,
1063    /// `None` for non-decimal variants or decimal values that would overflow.
1064    ///
1065    /// # Examples
1066    ///
1067    /// ```
1068    /// use parquet_variant::{Variant, VariantDecimal4, VariantDecimal8};
1069    ///
1070    /// // you can extract decimal parts from smaller or equally-sized decimal variants
1071    /// let v1 = Variant::from(VariantDecimal4::try_new(1234_i32, 2).unwrap());
1072    /// assert_eq!(v1.as_decimal4(), VariantDecimal4::try_new(1234_i32, 2).ok());
1073    ///
1074    /// // and from larger decimal variants if they fit
1075    /// let v2 = Variant::from(VariantDecimal8::try_new(1234_i64, 2).unwrap());
1076    /// assert_eq!(v2.as_decimal4(), VariantDecimal4::try_new(1234_i32, 2).ok());
1077    ///
1078    /// // but not if the value would overflow i32
1079    /// let v3 = Variant::from(VariantDecimal8::try_new(12345678901i64, 2).unwrap());
1080    /// assert_eq!(v3.as_decimal4(), None);
1081    ///
1082    /// // or if the variant is not a decimal
1083    /// let v4 = Variant::from("hello!");
1084    /// assert_eq!(v4.as_decimal4(), None);
1085    /// ```
1086    pub fn as_decimal4(&self) -> Option<VariantDecimal4> {
1087        match *self {
1088            Variant::Int8(i) => i32::from(i).try_into().ok(),
1089            Variant::Int16(i) => i32::from(i).try_into().ok(),
1090            Variant::Int32(i) => i.try_into().ok(),
1091            Variant::Int64(i) => i32::try_from(i).ok()?.try_into().ok(),
1092            Variant::Decimal4(decimal4) => Some(decimal4),
1093            Variant::Decimal8(decimal8) => decimal8.try_into().ok(),
1094            Variant::Decimal16(decimal16) => decimal16.try_into().ok(),
1095            _ => None,
1096        }
1097    }
1098
1099    /// Converts this variant to tuple with an 8-byte unscaled value if possible.
1100    ///
1101    /// Returns `Some((i64, u8))` for decimal variants where the unscaled value
1102    /// fits in `i64` range,
1103    /// `None` for non-decimal variants or decimal values that would overflow.
1104    ///
1105    /// # Examples
1106    ///
1107    /// ```
1108    /// use parquet_variant::{Variant, VariantDecimal4, VariantDecimal8, VariantDecimal16};
1109    ///
1110    /// // you can extract decimal parts from smaller or equally-sized decimal variants
1111    /// let v1 = Variant::from(VariantDecimal4::try_new(1234_i32, 2).unwrap());
1112    /// assert_eq!(v1.as_decimal8(), VariantDecimal8::try_new(1234_i64, 2).ok());
1113    ///
1114    /// // and from larger decimal variants if they fit
1115    /// let v2 = Variant::from(VariantDecimal16::try_new(1234_i128, 2).unwrap());
1116    /// assert_eq!(v2.as_decimal8(), VariantDecimal8::try_new(1234_i64, 2).ok());
1117    ///
1118    /// // but not if the value would overflow i64
1119    /// let v3 = Variant::from(VariantDecimal16::try_new(2e19 as i128, 2).unwrap());
1120    /// assert_eq!(v3.as_decimal8(), None);
1121    ///
1122    /// // or if the variant is not a decimal
1123    /// let v4 = Variant::from("hello!");
1124    /// assert_eq!(v4.as_decimal8(), None);
1125    /// ```
1126    pub fn as_decimal8(&self) -> Option<VariantDecimal8> {
1127        match *self {
1128            Variant::Int8(i) => i64::from(i).try_into().ok(),
1129            Variant::Int16(i) => i64::from(i).try_into().ok(),
1130            Variant::Int32(i) => i64::from(i).try_into().ok(),
1131            Variant::Int64(i) => i.try_into().ok(),
1132            Variant::Decimal4(decimal4) => Some(decimal4.into()),
1133            Variant::Decimal8(decimal8) => Some(decimal8),
1134            Variant::Decimal16(decimal16) => decimal16.try_into().ok(),
1135            _ => None,
1136        }
1137    }
1138
1139    /// Converts this variant to tuple with a 16-byte unscaled value if possible.
1140    ///
1141    /// Returns `Some((i128, u8))` for decimal variants where the unscaled value
1142    /// fits in `i128` range,
1143    /// `None` for non-decimal variants or decimal values that would overflow.
1144    ///
1145    /// # Examples
1146    ///
1147    /// ```
1148    /// use parquet_variant::{Variant, VariantDecimal16, VariantDecimal4};
1149    ///
1150    /// // you can extract decimal parts from smaller or equally-sized decimal variants
1151    /// let v1 = Variant::from(VariantDecimal4::try_new(1234_i32, 2).unwrap());
1152    /// assert_eq!(v1.as_decimal16(), VariantDecimal16::try_new(1234_i128, 2).ok());
1153    ///
1154    /// // but not if the variant is not a decimal
1155    /// let v2 = Variant::from("hello!");
1156    /// assert_eq!(v2.as_decimal16(), None);
1157    /// ```
1158    pub fn as_decimal16(&self) -> Option<VariantDecimal16> {
1159        match *self {
1160            Variant::Int8(i) => i128::from(i).try_into().ok(),
1161            Variant::Int16(i) => i128::from(i).try_into().ok(),
1162            Variant::Int32(i) => i128::from(i).try_into().ok(),
1163            Variant::Int64(i) => i128::from(i).try_into().ok(),
1164            Variant::Decimal4(decimal4) => Some(decimal4.into()),
1165            Variant::Decimal8(decimal8) => Some(decimal8.into()),
1166            Variant::Decimal16(decimal16) => Some(decimal16),
1167            _ => None,
1168        }
1169    }
1170
1171    /// Converts this variant to an `f16` if possible.
1172    ///
1173    /// Returns `Some(f16)` for floating point values, and integers with up to 11 bits of
1174    /// precision. `None` otherwise.
1175    ///
1176    /// # Example
1177    ///
1178    /// ```
1179    /// use parquet_variant::Variant;
1180    /// use half::f16;
1181    ///
1182    /// // you can extract an f16 from a float variant
1183    /// let v1 = Variant::from(std::f32::consts::PI);
1184    /// assert_eq!(v1.as_f16(), Some(f16::from_f32(std::f32::consts::PI)));
1185    ///
1186    /// // and from a double variant (with loss of precision to nearest f16)
1187    /// let v2 = Variant::from(std::f64::consts::PI);
1188    /// assert_eq!(v2.as_f16(), Some(f16::from_f64(std::f64::consts::PI)));
1189    ///
1190    /// // and from integers with no more than 11 bits of precision
1191    /// let v3 = Variant::from(2047);
1192    /// assert_eq!(v3.as_f16(), Some(f16::from_f32(2047.0)));
1193    ///
1194    /// // but not from other variants
1195    /// let v4 = Variant::from("hello!");
1196    /// assert_eq!(v4.as_f16(), None);
1197    pub fn as_f16(&self) -> Option<f16> {
1198        match *self {
1199            Variant::Float(i) => Some(f16::from_f32(i)),
1200            Variant::Double(i) => Some(f16::from_f64(i)),
1201            Variant::Int8(i) => Some(i.into()),
1202            Variant::Int16(i) if fits_precision::<11>(i) => Some(f16::from_f32(i as _)),
1203            Variant::Int32(i) if fits_precision::<11>(i) => Some(f16::from_f32(i as _)),
1204            Variant::Int64(i) if fits_precision::<11>(i) => Some(f16::from_f32(i as _)),
1205            _ => None,
1206        }
1207    }
1208
1209    /// Converts this variant to an `f32` if possible.
1210    ///
1211    /// Returns `Some(f32)` for floating point values, and integer values with up to 24 bits of
1212    /// precision.  `None` otherwise.
1213    ///
1214    /// # Examples
1215    ///
1216    /// ```
1217    /// use parquet_variant::Variant;
1218    ///
1219    /// // you can extract an f32 from a float variant
1220    /// let v1 = Variant::from(std::f32::consts::PI);
1221    /// assert_eq!(v1.as_f32(), Some(std::f32::consts::PI));
1222    ///
1223    /// // and from a double variant (with loss of precision to nearest f32)
1224    /// let v2 = Variant::from(std::f64::consts::PI);
1225    /// assert_eq!(v2.as_f32(), Some(std::f32::consts::PI));
1226    ///
1227    /// // and from integers with no more than 24 bits of precision
1228    /// let v3 = Variant::from(16777215i64);
1229    /// assert_eq!(v3.as_f32(), Some(16777215.0));
1230    ///
1231    /// // but not from other variants
1232    /// let v4 = Variant::from("hello!");
1233    /// assert_eq!(v4.as_f32(), None);
1234    /// ```
1235    #[allow(clippy::cast_possible_truncation)]
1236    pub fn as_f32(&self) -> Option<f32> {
1237        match *self {
1238            Variant::Float(i) => Some(i),
1239            Variant::Double(i) => Some(i as f32),
1240            Variant::Int8(i) => Some(i.into()),
1241            Variant::Int16(i) => Some(i.into()),
1242            Variant::Int32(i) if fits_precision::<24>(i) => Some(i as _),
1243            Variant::Int64(i) if fits_precision::<24>(i) => Some(i as _),
1244            _ => None,
1245        }
1246    }
1247
1248    /// Converts this variant to an `f64` if possible.
1249    ///
1250    /// Returns `Some(f64)` for floating point values, and integer values with up to 53 bits of
1251    /// precision.  `None` otherwise.
1252    ///
1253    /// # Examples
1254    ///
1255    /// ```
1256    /// use parquet_variant::Variant;
1257    ///
1258    /// // you can extract an f64 from a float variant
1259    /// let v1 = Variant::from(std::f32::consts::PI);
1260    /// assert_eq!(v1.as_f64(), Some(std::f32::consts::PI as f64));
1261    ///
1262    /// // and from a double variant
1263    /// let v2 = Variant::from(std::f64::consts::PI);
1264    /// assert_eq!(v2.as_f64(), Some(std::f64::consts::PI));
1265    ///
1266    /// // and from integers with no more than 53 bits of precision
1267    /// let v3 = Variant::from(9007199254740991i64);
1268    /// assert_eq!(v3.as_f64(), Some(9007199254740991.0));
1269    ///
1270    /// // but not from other variants
1271    /// let v4 = Variant::from("hello!");
1272    /// assert_eq!(v4.as_f64(), None);
1273    /// ```
1274    pub fn as_f64(&self) -> Option<f64> {
1275        match *self {
1276            Variant::Float(i) => Some(i.into()),
1277            Variant::Double(i) => Some(i),
1278            Variant::Int8(i) => Some(i.into()),
1279            Variant::Int16(i) => Some(i.into()),
1280            Variant::Int32(i) => Some(i.into()),
1281            Variant::Int64(i) if fits_precision::<53>(i) => Some(i as _),
1282            _ => None,
1283        }
1284    }
1285
1286    /// Converts this variant to an `Object` if it is an [`VariantObject`].
1287    ///
1288    /// Returns `Some(&VariantObject)` for object variants,
1289    /// `None` for non-object variants.
1290    ///
1291    /// See [`Self::get_path`] to dynamically traverse objects
1292    ///
1293    /// # Examples
1294    /// ```
1295    /// # use parquet_variant::{Variant, VariantBuilder, VariantObject};
1296    /// # let (metadata, value) = {
1297    /// # let mut builder = VariantBuilder::new();
1298    /// #   let mut obj = builder.new_object();
1299    /// #   obj.insert("name", "John");
1300    /// #   obj.finish();
1301    /// #   builder.finish()
1302    /// # };
1303    /// // object that is {"name": "John"}
1304    ///  let variant = Variant::new(&metadata, &value);
1305    /// // use the `as_object` method to access the object
1306    /// let obj = variant.as_object().expect("variant should be an object");
1307    /// assert_eq!(obj.get("name"), Some(Variant::from("John")));
1308    /// ```
1309    pub fn as_object(&'m self) -> Option<&'m VariantObject<'m, 'v>> {
1310        if let Variant::Object(obj) = self {
1311            Some(obj)
1312        } else {
1313            None
1314        }
1315    }
1316
1317    /// If this is an object and the requested field name exists, retrieves the corresponding field
1318    /// value. Otherwise, returns None.
1319    ///
1320    /// This is shorthand for [`Self::as_object`] followed by [`VariantObject::get`].
1321    ///
1322    /// # Examples
1323    /// ```
1324    /// # use parquet_variant::{Variant, VariantBuilder, VariantObject};
1325    /// # let mut builder = VariantBuilder::new();
1326    /// # let mut obj = builder.new_object();
1327    /// # obj.insert("name", "John");
1328    /// # obj.finish();
1329    /// # let (metadata, value) = builder.finish();
1330    /// // object that is {"name": "John"}
1331    ///  let variant = Variant::new(&metadata, &value);
1332    /// // use the `get_object_field` method to access the object
1333    /// let obj = variant.get_object_field("name");
1334    /// assert_eq!(obj, Some(Variant::from("John")));
1335    /// let obj = variant.get_object_field("foo");
1336    /// assert!(obj.is_none());
1337    /// ```
1338    pub fn get_object_field(&self, field_name: &str) -> Option<Self> {
1339        match self {
1340            Variant::Object(object) => object.get(field_name),
1341            _ => None,
1342        }
1343    }
1344
1345    /// Converts this variant to a `List` if it is a [`VariantList`].
1346    ///
1347    /// Returns `Some(&VariantList)` for list variants,
1348    /// `None` for non-list variants.
1349    ///
1350    /// See [`Self::get_path`] to dynamically traverse lists
1351    ///
1352    /// # Examples
1353    /// ```
1354    /// # use parquet_variant::{Variant, VariantBuilder, VariantList};
1355    /// # let (metadata, value) = {
1356    /// # let mut builder = VariantBuilder::new();
1357    /// #   let mut list = builder.new_list();
1358    /// #   list.append_value("John");
1359    /// #   list.append_value("Doe");
1360    /// #   list.finish();
1361    /// #   builder.finish()
1362    /// # };
1363    /// // list that is ["John", "Doe"]
1364    /// let variant = Variant::new(&metadata, &value);
1365    /// // use the `as_list` method to access the list
1366    /// let list = variant.as_list().expect("variant should be a list");
1367    /// assert_eq!(list.len(), 2);
1368    /// assert_eq!(list.get(0).unwrap(), Variant::from("John"));
1369    /// assert_eq!(list.get(1).unwrap(), Variant::from("Doe"));
1370    /// ```
1371    pub fn as_list(&'m self) -> Option<&'m VariantList<'m, 'v>> {
1372        if let Variant::List(list) = self {
1373            Some(list)
1374        } else {
1375            None
1376        }
1377    }
1378
1379    /// Converts this variant to a `NaiveTime` if possible.
1380    ///
1381    /// Returns `Some(NaiveTime)` for `Variant::Time`,
1382    /// `None` for non-Time variants.
1383    ///
1384    /// # Example
1385    ///
1386    /// ```
1387    /// use chrono::NaiveTime;
1388    /// use parquet_variant::Variant;
1389    ///
1390    /// // you can extract a `NaiveTime` from a `Variant::Time`
1391    /// let time = NaiveTime::from_hms_micro_opt(1, 2, 3, 4).unwrap();
1392    /// let v1 = Variant::from(time);
1393    /// assert_eq!(Some(time), v1.as_time_utc());
1394    ///
1395    /// // but not from other variants.
1396    /// let v2 = Variant::from("Hello");
1397    /// assert_eq!(None, v2.as_time_utc());
1398    /// ```
1399    pub fn as_time_utc(&'m self) -> Option<NaiveTime> {
1400        if let Variant::Time(time) = self {
1401            Some(*time)
1402        } else {
1403            None
1404        }
1405    }
1406
1407    /// If this is a list and the requested index is in bounds, retrieves the corresponding
1408    /// element. Otherwise, returns None.
1409    ///
1410    /// This is shorthand for [`Self::as_list`] followed by [`VariantList::get`].
1411    ///
1412    /// # Examples
1413    /// ```
1414    /// # use parquet_variant::{Variant, VariantBuilder, VariantList};
1415    /// # let mut builder = VariantBuilder::new();
1416    /// # let mut list = builder.new_list();
1417    /// # list.append_value("John");
1418    /// # list.append_value("Doe");
1419    /// # list.finish();
1420    /// # let (metadata, value) = builder.finish();
1421    /// // list that is ["John", "Doe"]
1422    /// let variant = Variant::new(&metadata, &value);
1423    /// // use the `get_list_element` method to access the list
1424    /// assert_eq!(variant.get_list_element(0), Some(Variant::from("John")));
1425    /// assert_eq!(variant.get_list_element(1), Some(Variant::from("Doe")));
1426    /// assert!(variant.get_list_element(2).is_none());
1427    /// ```
1428    pub fn get_list_element(&self, index: usize) -> Option<Self> {
1429        match self {
1430            Variant::List(list) => list.get(index),
1431            _ => None,
1432        }
1433    }
1434
1435    /// Return the metadata dictionary associated with this variant value.
1436    pub fn metadata(&self) -> &VariantMetadata<'m> {
1437        match self {
1438            Variant::Object(VariantObject { metadata, .. })
1439            | Variant::List(VariantList { metadata, .. }) => metadata,
1440            _ => &EMPTY_VARIANT_METADATA,
1441        }
1442    }
1443
1444    /// Return a new Variant with the path followed.
1445    ///
1446    /// If the path is not found, `None` is returned.
1447    ///
1448    /// # Example
1449    /// ```
1450    /// # use parquet_variant::{Variant, VariantBuilder, VariantObject, VariantPath};
1451    /// # let mut builder = VariantBuilder::new();
1452    /// # let mut obj = builder.new_object();
1453    /// # let mut list = obj.new_list("foo");
1454    /// # list.append_value("bar");
1455    /// # list.append_value("baz");
1456    /// # list.finish();
1457    /// # obj.finish();
1458    /// # let (metadata, value) = builder.finish();
1459    /// // given a variant like `{"foo": ["bar", "baz"]}`
1460    /// let variant = Variant::new(&metadata, &value);
1461    /// // Accessing a non existent path returns None
1462    /// assert_eq!(variant.get_path(&VariantPath::from("non_existent")), None);
1463    /// // Access obj["foo"]
1464    /// let path = VariantPath::from("foo");
1465    /// let foo = variant.get_path(&path).expect("field `foo` should exist");
1466    /// assert!(foo.as_list().is_some(), "field `foo` should be a list");
1467    /// // Access foo[0]
1468    /// let path = VariantPath::from(0);
1469    /// let bar = foo.get_path(&path).expect("element 0 should exist");
1470    /// // bar is a string
1471    /// assert_eq!(bar.as_string(), Some("bar"));
1472    /// // You can also access nested paths
1473    /// let path = VariantPath::from("foo").join(0);
1474    /// assert_eq!(variant.get_path(&path).unwrap(), bar);
1475    /// ```
1476    pub fn get_path(&self, path: &VariantPath) -> Option<Variant<'_, '_>> {
1477        path.iter()
1478            .try_fold(self.clone(), |output, element| match element {
1479                VariantPathElement::Field { name } => output.get_object_field(name),
1480                VariantPathElement::Index { index } => output.get_list_element(*index),
1481            })
1482    }
1483}
1484
1485impl From<()> for Variant<'_, '_> {
1486    fn from((): ()) -> Self {
1487        Variant::Null
1488    }
1489}
1490
1491impl From<bool> for Variant<'_, '_> {
1492    fn from(value: bool) -> Self {
1493        match value {
1494            true => Variant::BooleanTrue,
1495            false => Variant::BooleanFalse,
1496        }
1497    }
1498}
1499
1500impl From<i8> for Variant<'_, '_> {
1501    fn from(value: i8) -> Self {
1502        Variant::Int8(value)
1503    }
1504}
1505
1506impl From<i16> for Variant<'_, '_> {
1507    fn from(value: i16) -> Self {
1508        Variant::Int16(value)
1509    }
1510}
1511
1512impl From<i32> for Variant<'_, '_> {
1513    fn from(value: i32) -> Self {
1514        Variant::Int32(value)
1515    }
1516}
1517
1518impl From<i64> for Variant<'_, '_> {
1519    fn from(value: i64) -> Self {
1520        Variant::Int64(value)
1521    }
1522}
1523
1524impl From<u8> for Variant<'_, '_> {
1525    fn from(value: u8) -> Self {
1526        // if it fits in i8, use that, otherwise use i16
1527        if let Ok(value) = i8::try_from(value) {
1528            Variant::Int8(value)
1529        } else {
1530            Variant::Int16(i16::from(value))
1531        }
1532    }
1533}
1534
1535impl From<u16> for Variant<'_, '_> {
1536    fn from(value: u16) -> Self {
1537        // if it fits in i16, use that, otherwise use i32
1538        if let Ok(value) = i16::try_from(value) {
1539            Variant::Int16(value)
1540        } else {
1541            Variant::Int32(i32::from(value))
1542        }
1543    }
1544}
1545impl From<u32> for Variant<'_, '_> {
1546    fn from(value: u32) -> Self {
1547        // if it fits in i32, use that, otherwise use i64
1548        if let Ok(value) = i32::try_from(value) {
1549            Variant::Int32(value)
1550        } else {
1551            Variant::Int64(i64::from(value))
1552        }
1553    }
1554}
1555
1556impl From<u64> for Variant<'_, '_> {
1557    fn from(value: u64) -> Self {
1558        // if it fits in i64, use that, otherwise use Decimal16
1559        if let Ok(value) = i64::try_from(value) {
1560            Variant::Int64(value)
1561        } else {
1562            // u64 max is 18446744073709551615, which fits in i128
1563            Variant::Decimal16(VariantDecimal16::try_new(i128::from(value), 0).unwrap())
1564        }
1565    }
1566}
1567
1568impl From<VariantDecimal4> for Variant<'_, '_> {
1569    fn from(value: VariantDecimal4) -> Self {
1570        Variant::Decimal4(value)
1571    }
1572}
1573
1574impl From<VariantDecimal8> for Variant<'_, '_> {
1575    fn from(value: VariantDecimal8) -> Self {
1576        Variant::Decimal8(value)
1577    }
1578}
1579
1580impl From<VariantDecimal16> for Variant<'_, '_> {
1581    fn from(value: VariantDecimal16) -> Self {
1582        Variant::Decimal16(value)
1583    }
1584}
1585
1586impl From<half::f16> for Variant<'_, '_> {
1587    fn from(value: half::f16) -> Self {
1588        Variant::Float(value.into())
1589    }
1590}
1591
1592impl From<f32> for Variant<'_, '_> {
1593    fn from(value: f32) -> Self {
1594        Variant::Float(value)
1595    }
1596}
1597
1598impl From<f64> for Variant<'_, '_> {
1599    fn from(value: f64) -> Self {
1600        Variant::Double(value)
1601    }
1602}
1603
1604impl From<NaiveDate> for Variant<'_, '_> {
1605    fn from(value: NaiveDate) -> Self {
1606        Variant::Date(value)
1607    }
1608}
1609
1610impl From<DateTime<Utc>> for Variant<'_, '_> {
1611    fn from(value: DateTime<Utc>) -> Self {
1612        if value.nanosecond() % 1000 > 0 {
1613            Variant::TimestampNanos(value)
1614        } else {
1615            Variant::TimestampMicros(value)
1616        }
1617    }
1618}
1619
1620impl From<NaiveDateTime> for Variant<'_, '_> {
1621    fn from(value: NaiveDateTime) -> Self {
1622        if value.nanosecond() % 1000 > 0 {
1623            Variant::TimestampNtzNanos(value)
1624        } else {
1625            Variant::TimestampNtzMicros(value)
1626        }
1627    }
1628}
1629
1630impl<'v> From<&'v [u8]> for Variant<'_, 'v> {
1631    fn from(value: &'v [u8]) -> Self {
1632        Variant::Binary(value)
1633    }
1634}
1635
1636impl From<NaiveTime> for Variant<'_, '_> {
1637    fn from(value: NaiveTime) -> Self {
1638        Variant::Time(value)
1639    }
1640}
1641
1642impl From<Uuid> for Variant<'_, '_> {
1643    fn from(value: Uuid) -> Self {
1644        Variant::Uuid(value)
1645    }
1646}
1647
1648impl<'v> From<&'v str> for Variant<'_, 'v> {
1649    fn from(value: &'v str) -> Self {
1650        if value.len() > MAX_SHORT_STRING_BYTES {
1651            Variant::String(value)
1652        } else {
1653            Variant::ShortString(ShortString(value))
1654        }
1655    }
1656}
1657
1658impl TryFrom<(i32, u8)> for Variant<'_, '_> {
1659    type Error = ArrowError;
1660
1661    fn try_from(value: (i32, u8)) -> Result<Self, Self::Error> {
1662        Ok(Variant::Decimal4(VariantDecimal4::try_new(
1663            value.0, value.1,
1664        )?))
1665    }
1666}
1667
1668impl TryFrom<(i64, u8)> for Variant<'_, '_> {
1669    type Error = ArrowError;
1670
1671    fn try_from(value: (i64, u8)) -> Result<Self, Self::Error> {
1672        Ok(Variant::Decimal8(VariantDecimal8::try_new(
1673            value.0, value.1,
1674        )?))
1675    }
1676}
1677
1678impl TryFrom<(i128, u8)> for Variant<'_, '_> {
1679    type Error = ArrowError;
1680
1681    fn try_from(value: (i128, u8)) -> Result<Self, Self::Error> {
1682        Ok(Variant::Decimal16(VariantDecimal16::try_new(
1683            value.0, value.1,
1684        )?))
1685    }
1686}
1687
1688// helper to print <invalid> instead of "<invalid>" in debug mode when a VariantObject or VariantList contains invalid values.
1689struct InvalidVariant;
1690
1691impl std::fmt::Debug for InvalidVariant {
1692    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1693        write!(f, "<invalid>")
1694    }
1695}
1696
1697// helper to print binary data in hex format in debug mode, as space-separated hex byte values.
1698struct HexString<'a>(&'a [u8]);
1699
1700impl<'a> std::fmt::Debug for HexString<'a> {
1701    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1702        if let Some((first, rest)) = self.0.split_first() {
1703            write!(f, "{:02x}", first)?;
1704            for b in rest {
1705                write!(f, " {:02x}", b)?;
1706            }
1707        }
1708        Ok(())
1709    }
1710}
1711
1712impl std::fmt::Debug for Variant<'_, '_> {
1713    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1714        match self {
1715            Variant::Null => write!(f, "Null"),
1716            Variant::BooleanTrue => write!(f, "BooleanTrue"),
1717            Variant::BooleanFalse => write!(f, "BooleanFalse"),
1718            Variant::Int8(v) => f.debug_tuple("Int8").field(v).finish(),
1719            Variant::Int16(v) => f.debug_tuple("Int16").field(v).finish(),
1720            Variant::Int32(v) => f.debug_tuple("Int32").field(v).finish(),
1721            Variant::Int64(v) => f.debug_tuple("Int64").field(v).finish(),
1722            Variant::Float(v) => f.debug_tuple("Float").field(v).finish(),
1723            Variant::Double(v) => f.debug_tuple("Double").field(v).finish(),
1724            Variant::Decimal4(d) => f.debug_tuple("Decimal4").field(d).finish(),
1725            Variant::Decimal8(d) => f.debug_tuple("Decimal8").field(d).finish(),
1726            Variant::Decimal16(d) => f.debug_tuple("Decimal16").field(d).finish(),
1727            Variant::Date(d) => f.debug_tuple("Date").field(d).finish(),
1728            Variant::TimestampMicros(ts) => f.debug_tuple("TimestampMicros").field(ts).finish(),
1729            Variant::TimestampNtzMicros(ts) => {
1730                f.debug_tuple("TimestampNtzMicros").field(ts).finish()
1731            }
1732            Variant::TimestampNanos(ts) => f.debug_tuple("TimestampNanos").field(ts).finish(),
1733            Variant::TimestampNtzNanos(ts) => f.debug_tuple("TimestampNtzNanos").field(ts).finish(),
1734            Variant::Binary(bytes) => write!(f, "Binary({:?})", HexString(bytes)),
1735            Variant::String(s) => f.debug_tuple("String").field(s).finish(),
1736            Variant::Time(s) => f.debug_tuple("Time").field(s).finish(),
1737            Variant::ShortString(s) => f.debug_tuple("ShortString").field(s).finish(),
1738            Variant::Uuid(uuid) => f.debug_tuple("Uuid").field(&uuid).finish(),
1739            Variant::Object(obj) => {
1740                let mut map = f.debug_map();
1741                for res in obj.iter_try() {
1742                    match res {
1743                        Ok((k, v)) => map.entry(&k, &v),
1744                        Err(_) => map.entry(&InvalidVariant, &InvalidVariant),
1745                    };
1746                }
1747                map.finish()
1748            }
1749            Variant::List(arr) => {
1750                let mut list = f.debug_list();
1751                for res in arr.iter_try() {
1752                    match res {
1753                        Ok(v) => list.entry(&v),
1754                        Err(_) => list.entry(&InvalidVariant),
1755                    };
1756                }
1757                list.finish()
1758            }
1759        }
1760    }
1761}
1762
1763#[cfg(test)]
1764mod tests {
1765
1766    use super::*;
1767
1768    #[test]
1769    fn test_empty_variant_will_fail() {
1770        let metadata = VariantMetadata::try_new(&[1, 0, 0]).unwrap();
1771
1772        let err = Variant::try_new_with_metadata(metadata, &[]).unwrap_err();
1773
1774        assert!(matches!(
1775            err,
1776            ArrowError::InvalidArgumentError(ref msg) if msg == "Received empty bytes"));
1777    }
1778
1779    #[test]
1780    fn test_construct_short_string() {
1781        let short_string = ShortString::try_new("norm").expect("should fit in short string");
1782        assert_eq!(short_string.as_str(), "norm");
1783
1784        let long_string = "a".repeat(MAX_SHORT_STRING_BYTES + 1);
1785        let res = ShortString::try_new(&long_string);
1786        assert!(res.is_err());
1787    }
1788
1789    #[test]
1790    fn test_variant_decimal_conversion() {
1791        let decimal4 = VariantDecimal4::try_new(1234_i32, 2).unwrap();
1792        let variant = Variant::from(decimal4);
1793        assert_eq!(variant.as_decimal4(), Some(decimal4));
1794
1795        let decimal8 = VariantDecimal8::try_new(12345678901_i64, 2).unwrap();
1796        let variant = Variant::from(decimal8);
1797        assert_eq!(variant.as_decimal8(), Some(decimal8));
1798
1799        let decimal16 = VariantDecimal16::try_new(123456789012345678901234567890_i128, 2).unwrap();
1800        let variant = Variant::from(decimal16);
1801        assert_eq!(variant.as_decimal16(), Some(decimal16));
1802    }
1803
1804    #[test]
1805    fn test_variant_all_subtypes_debug() {
1806        use crate::VariantBuilder;
1807
1808        let mut builder = VariantBuilder::new();
1809
1810        // Create a root object that contains one of every variant subtype
1811        let mut root_obj = builder.new_object();
1812
1813        // Add primitive types
1814        root_obj.insert("null", ());
1815        root_obj.insert("boolean_true", true);
1816        root_obj.insert("boolean_false", false);
1817        root_obj.insert("int8", 42i8);
1818        root_obj.insert("int16", 1234i16);
1819        root_obj.insert("int32", 123456i32);
1820        root_obj.insert("int64", 1234567890123456789i64);
1821        root_obj.insert("float", 1.234f32);
1822        root_obj.insert("double", 1.23456789f64);
1823
1824        // Add date and timestamp types
1825        let date = chrono::NaiveDate::from_ymd_opt(2024, 12, 25).unwrap();
1826        root_obj.insert("date", date);
1827
1828        let timestamp_utc = chrono::NaiveDate::from_ymd_opt(2024, 12, 25)
1829            .unwrap()
1830            .and_hms_milli_opt(15, 30, 45, 123)
1831            .unwrap()
1832            .and_utc();
1833        root_obj.insert("timestamp_micros", Variant::TimestampMicros(timestamp_utc));
1834
1835        let timestamp_ntz = chrono::NaiveDate::from_ymd_opt(2024, 12, 25)
1836            .unwrap()
1837            .and_hms_milli_opt(15, 30, 45, 123)
1838            .unwrap();
1839        root_obj.insert(
1840            "timestamp_ntz_micros",
1841            Variant::TimestampNtzMicros(timestamp_ntz),
1842        );
1843
1844        let timestamp_nanos_utc = chrono::NaiveDate::from_ymd_opt(2025, 8, 15)
1845            .unwrap()
1846            .and_hms_nano_opt(12, 3, 4, 123456789)
1847            .unwrap()
1848            .and_utc();
1849        root_obj.insert(
1850            "timestamp_nanos",
1851            Variant::TimestampNanos(timestamp_nanos_utc),
1852        );
1853
1854        let timestamp_ntz_nanos = chrono::NaiveDate::from_ymd_opt(2025, 8, 15)
1855            .unwrap()
1856            .and_hms_nano_opt(12, 3, 4, 123456789)
1857            .unwrap();
1858        root_obj.insert(
1859            "timestamp_ntz_nanos",
1860            Variant::TimestampNtzNanos(timestamp_ntz_nanos),
1861        );
1862
1863        // Add decimal types
1864        let decimal4 = VariantDecimal4::try_new(1234i32, 2).unwrap();
1865        root_obj.insert("decimal4", decimal4);
1866
1867        let decimal8 = VariantDecimal8::try_new(123456789i64, 3).unwrap();
1868        root_obj.insert("decimal8", decimal8);
1869
1870        let decimal16 = VariantDecimal16::try_new(123456789012345678901234567890i128, 4).unwrap();
1871        root_obj.insert("decimal16", decimal16);
1872
1873        // Add binary and string types
1874        let binary_data = b"\x01\x02\x03\x04\xde\xad\xbe\xef";
1875        root_obj.insert("binary", binary_data.as_slice());
1876
1877        let long_string =
1878            "This is a long string that exceeds the short string limit and contains emoji 🦀";
1879        root_obj.insert("string", long_string);
1880        root_obj.insert("short_string", "Short string with emoji 🎉");
1881        let time = NaiveTime::from_hms_micro_opt(1, 2, 3, 4).unwrap();
1882        root_obj.insert("time", time);
1883
1884        // Add uuid
1885        let uuid = Uuid::parse_str("67e55044-10b1-426f-9247-bb680e5fe0c8").unwrap();
1886        root_obj.insert("uuid", Variant::Uuid(uuid));
1887
1888        // Add nested object
1889        let mut nested_obj = root_obj.new_object("nested_object");
1890        nested_obj.insert("inner_key1", "inner_value1");
1891        nested_obj.insert("inner_key2", 999i32);
1892        nested_obj.finish();
1893
1894        // Add list with mixed types
1895        let mut mixed_list = root_obj.new_list("mixed_list");
1896        mixed_list.append_value(1i32);
1897        mixed_list.append_value("two");
1898        mixed_list.append_value(true);
1899        mixed_list.append_value(4.0f32);
1900        mixed_list.append_value(());
1901
1902        // Add nested list inside the mixed list
1903        let mut nested_list = mixed_list.new_list();
1904        nested_list.append_value("nested");
1905        nested_list.append_value(10i8);
1906        nested_list.finish();
1907
1908        mixed_list.finish();
1909
1910        root_obj.finish();
1911
1912        let (metadata, value) = builder.finish();
1913        let variant = Variant::try_new(&metadata, &value).unwrap();
1914
1915        // Test Debug formatter (?)
1916        let debug_output = format!("{:?}", variant);
1917
1918        // Verify that the debug output contains all the expected types
1919        assert!(debug_output.contains("\"null\": Null"));
1920        assert!(debug_output.contains("\"boolean_true\": BooleanTrue"));
1921        assert!(debug_output.contains("\"boolean_false\": BooleanFalse"));
1922        assert!(debug_output.contains("\"int8\": Int8(42)"));
1923        assert!(debug_output.contains("\"int16\": Int16(1234)"));
1924        assert!(debug_output.contains("\"int32\": Int32(123456)"));
1925        assert!(debug_output.contains("\"int64\": Int64(1234567890123456789)"));
1926        assert!(debug_output.contains("\"float\": Float(1.234)"));
1927        assert!(debug_output.contains("\"double\": Double(1.23456789"));
1928        assert!(debug_output.contains("\"date\": Date(2024-12-25)"));
1929        assert!(debug_output.contains("\"timestamp_micros\": TimestampMicros("));
1930        assert!(debug_output.contains("\"timestamp_ntz_micros\": TimestampNtzMicros("));
1931        assert!(debug_output.contains("\"timestamp_nanos\": TimestampNanos("));
1932        assert!(debug_output.contains("\"timestamp_ntz_nanos\": TimestampNtzNanos("));
1933        assert!(debug_output.contains("\"decimal4\": Decimal4("));
1934        assert!(debug_output.contains("\"decimal8\": Decimal8("));
1935        assert!(debug_output.contains("\"decimal16\": Decimal16("));
1936        assert!(debug_output.contains("\"binary\": Binary(01 02 03 04 de ad be ef)"));
1937        assert!(debug_output.contains("\"string\": String("));
1938        assert!(debug_output.contains("\"short_string\": ShortString("));
1939        assert!(debug_output.contains("\"uuid\": Uuid(67e55044-10b1-426f-9247-bb680e5fe0c8)"));
1940        assert!(debug_output.contains("\"time\": Time(01:02:03.000004)"));
1941        assert!(debug_output.contains("\"nested_object\":"));
1942        assert!(debug_output.contains("\"mixed_list\":"));
1943
1944        let expected = r#"{"binary": Binary(01 02 03 04 de ad be ef), "boolean_false": BooleanFalse, "boolean_true": BooleanTrue, "date": Date(2024-12-25), "decimal16": Decimal16(VariantDecimal16 { integer: 123456789012345678901234567890, scale: 4 }), "decimal4": Decimal4(VariantDecimal4 { integer: 1234, scale: 2 }), "decimal8": Decimal8(VariantDecimal8 { integer: 123456789, scale: 3 }), "double": Double(1.23456789), "float": Float(1.234), "int16": Int16(1234), "int32": Int32(123456), "int64": Int64(1234567890123456789), "int8": Int8(42), "mixed_list": [Int32(1), ShortString(ShortString("two")), BooleanTrue, Float(4.0), Null, [ShortString(ShortString("nested")), Int8(10)]], "nested_object": {"inner_key1": ShortString(ShortString("inner_value1")), "inner_key2": Int32(999)}, "null": Null, "short_string": ShortString(ShortString("Short string with emoji 🎉")), "string": String("This is a long string that exceeds the short string limit and contains emoji 🦀"), "time": Time(01:02:03.000004), "timestamp_micros": TimestampMicros(2024-12-25T15:30:45.123Z), "timestamp_nanos": TimestampNanos(2025-08-15T12:03:04.123456789Z), "timestamp_ntz_micros": TimestampNtzMicros(2024-12-25T15:30:45.123), "timestamp_ntz_nanos": TimestampNtzNanos(2025-08-15T12:03:04.123456789), "uuid": Uuid(67e55044-10b1-426f-9247-bb680e5fe0c8)}"#;
1945        assert_eq!(debug_output, expected);
1946
1947        // Test alternate Debug formatter (#?)
1948        let alt_debug_output = format!("{:#?}", variant);
1949        let expected = r#"{
1950    "binary": Binary(01 02 03 04 de ad be ef),
1951    "boolean_false": BooleanFalse,
1952    "boolean_true": BooleanTrue,
1953    "date": Date(
1954        2024-12-25,
1955    ),
1956    "decimal16": Decimal16(
1957        VariantDecimal16 {
1958            integer: 123456789012345678901234567890,
1959            scale: 4,
1960        },
1961    ),
1962    "decimal4": Decimal4(
1963        VariantDecimal4 {
1964            integer: 1234,
1965            scale: 2,
1966        },
1967    ),
1968    "decimal8": Decimal8(
1969        VariantDecimal8 {
1970            integer: 123456789,
1971            scale: 3,
1972        },
1973    ),
1974    "double": Double(
1975        1.23456789,
1976    ),
1977    "float": Float(
1978        1.234,
1979    ),
1980    "int16": Int16(
1981        1234,
1982    ),
1983    "int32": Int32(
1984        123456,
1985    ),
1986    "int64": Int64(
1987        1234567890123456789,
1988    ),
1989    "int8": Int8(
1990        42,
1991    ),
1992    "mixed_list": [
1993        Int32(
1994            1,
1995        ),
1996        ShortString(
1997            ShortString(
1998                "two",
1999            ),
2000        ),
2001        BooleanTrue,
2002        Float(
2003            4.0,
2004        ),
2005        Null,
2006        [
2007            ShortString(
2008                ShortString(
2009                    "nested",
2010                ),
2011            ),
2012            Int8(
2013                10,
2014            ),
2015        ],
2016    ],
2017    "nested_object": {
2018        "inner_key1": ShortString(
2019            ShortString(
2020                "inner_value1",
2021            ),
2022        ),
2023        "inner_key2": Int32(
2024            999,
2025        ),
2026    },
2027    "null": Null,
2028    "short_string": ShortString(
2029        ShortString(
2030            "Short string with emoji 🎉",
2031        ),
2032    ),
2033    "string": String(
2034        "This is a long string that exceeds the short string limit and contains emoji 🦀",
2035    ),
2036    "time": Time(
2037        01:02:03.000004,
2038    ),
2039    "timestamp_micros": TimestampMicros(
2040        2024-12-25T15:30:45.123Z,
2041    ),
2042    "timestamp_nanos": TimestampNanos(
2043        2025-08-15T12:03:04.123456789Z,
2044    ),
2045    "timestamp_ntz_micros": TimestampNtzMicros(
2046        2024-12-25T15:30:45.123,
2047    ),
2048    "timestamp_ntz_nanos": TimestampNtzNanos(
2049        2025-08-15T12:03:04.123456789,
2050    ),
2051    "uuid": Uuid(
2052        67e55044-10b1-426f-9247-bb680e5fe0c8,
2053    ),
2054}"#;
2055        assert_eq!(alt_debug_output, expected);
2056    }
2057}