Skip to main content

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