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::{VariantDecimal16, VariantDecimal4, VariantDecimal8};
19pub use self::list::VariantList;
20pub use self::metadata::VariantMetadata;
21pub use self::object::VariantObject;
22use crate::decoder::{
23    self, get_basic_type, get_primitive_type, VariantBasicType, VariantPrimitiveType,
24};
25use crate::path::{VariantPath, VariantPathElement};
26use crate::utils::{first_byte_from_slice, slice_from_slice};
27use std::ops::Deref;
28
29use arrow_schema::ArrowError;
30use chrono::{DateTime, NaiveDate, NaiveDateTime, Utc};
31
32mod decimal;
33mod list;
34mod metadata;
35mod object;
36
37const MAX_SHORT_STRING_BYTES: usize = 0x3F;
38
39/// A Variant [`ShortString`]
40///
41/// This implementation is a zero cost wrapper over `&str` that ensures
42/// the length of the underlying string is a valid Variant short string (63 bytes or less)
43#[derive(Debug, Clone, Copy, PartialEq)]
44pub struct ShortString<'a>(pub(crate) &'a str);
45
46impl<'a> ShortString<'a> {
47    /// Attempts to interpret `value` as a variant short string value.
48    ///
49    /// # Errors
50    ///
51    /// Returns an error if  `value` is longer than the maximum allowed length
52    /// of a Variant short string (63 bytes).
53    pub fn try_new(value: &'a str) -> Result<Self, ArrowError> {
54        if value.len() > MAX_SHORT_STRING_BYTES {
55            return Err(ArrowError::InvalidArgumentError(format!(
56                "value is larger than {MAX_SHORT_STRING_BYTES} bytes"
57            )));
58        }
59
60        Ok(Self(value))
61    }
62
63    /// Returns the underlying Variant short string as a &str
64    pub fn as_str(&self) -> &'a str {
65        self.0
66    }
67}
68
69impl<'a> From<ShortString<'a>> for &'a str {
70    fn from(value: ShortString<'a>) -> Self {
71        value.0
72    }
73}
74
75impl<'a> TryFrom<&'a str> for ShortString<'a> {
76    type Error = ArrowError;
77
78    fn try_from(value: &'a str) -> Result<Self, Self::Error> {
79        Self::try_new(value)
80    }
81}
82
83impl AsRef<str> for ShortString<'_> {
84    fn as_ref(&self) -> &str {
85        self.0
86    }
87}
88
89impl Deref for ShortString<'_> {
90    type Target = str;
91
92    fn deref(&self) -> &Self::Target {
93        self.0
94    }
95}
96
97/// Represents a [Parquet Variant]
98///
99/// The lifetimes `'m` and `'v` are for metadata and value buffers, respectively.
100///
101/// # Background
102///
103/// The [specification] says:
104///
105/// The Variant Binary Encoding allows representation of semi-structured data
106/// (e.g. JSON) in a form that can be efficiently queried by path. The design is
107/// intended to allow efficient access to nested data even in the presence of
108/// very wide or deep structures.
109///
110/// Another motivation for the representation is that (aside from metadata) each
111/// nested Variant value is contiguous and self-contained. For example, in a
112/// Variant containing an Array of Variant values, the representation of an
113/// inner Variant value, when paired with the metadata of the full variant, is
114/// itself a valid Variant.
115///
116/// When stored in Parquet files, Variant fields can also be *shredded*. Shredding
117/// refers to extracting some elements of the variant into separate columns for
118/// more efficient extraction/filter pushdown. The [Variant Shredding
119/// specification] describes the details of shredding Variant values as typed
120/// Parquet columns.
121///
122/// A Variant represents a type that contains one of:
123///
124/// * Primitive: A type and corresponding value (e.g. INT, STRING)
125///
126/// * Array: An ordered list of Variant values
127///
128/// * Object: An unordered collection of string/Variant pairs (i.e. key/value
129///   pairs). An object may not contain duplicate keys.
130///
131/// # Encoding
132///
133/// A Variant is encoded with 2 binary values, the value and the metadata. The
134/// metadata stores a header and an optional dictionary of field names which are
135/// referred to by offset in the value. The value is a binary representation of
136/// the actual data, and varies depending on the type.
137///
138/// # Design Goals
139///
140/// The design goals of the Rust API are as follows:
141/// 1. Speed / Zero copy access (no `clone`ing is required)
142/// 2. Safety
143/// 3. Follow standard Rust conventions
144///
145/// [Parquet Variant]: https://github.com/apache/parquet-format/blob/master/VariantEncoding.md
146/// [specification]: https://github.com/apache/parquet-format/blob/master/VariantEncoding.md
147/// [Variant Shredding specification]: https://github.com/apache/parquet-format/blob/master/VariantShredding.md
148///
149/// # Examples:
150///
151/// ## Creating `Variant` from Rust Types
152/// ```
153/// use parquet_variant::Variant;
154/// // variants can be directly constructed
155/// let variant = Variant::Int32(123);
156/// // or constructed via `From` impls
157/// assert_eq!(variant, Variant::from(123i32));
158/// ```
159/// ## Creating `Variant` from metadata and value
160/// ```
161/// # use parquet_variant::{Variant, VariantMetadata};
162/// let metadata = [0x01, 0x00, 0x00];
163/// let value = [0x09, 0x48, 0x49];
164/// // parse the header metadata
165/// assert_eq!(
166///   Variant::from("HI"),
167///   Variant::new(&metadata, &value)
168/// );
169/// ```
170///
171/// ## Using `Variant` values
172/// ```
173/// # use parquet_variant::Variant;
174/// # let variant = Variant::Int32(123);
175/// // variants can be used in match statements like normal enums
176/// match variant {
177///   Variant::Int32(i) => println!("Integer: {}", i),
178///   Variant::String(s) => println!("String: {}", s),
179///   _ => println!("Other variant"),
180/// }
181/// ```
182///
183/// # Validation
184///
185/// Every instance of variant is either _valid_ or _invalid_. depending on whether the
186/// underlying bytes are a valid encoding of a variant value (see below).
187///
188/// Instances produced by [`Self::try_new`], [`Self::try_new_with_metadata`], or [`Self::with_full_validation`]
189/// are fully _validated_. They always contain _valid_ data, and infallible accesses such as
190/// iteration and indexing are panic-free. The validation cost is `O(m + v)` where `m` and
191/// `v` are the number of bytes in the metadata and value buffers, respectively.
192///
193/// Instances produced by [`Self::new`] and [`Self::new_with_metadata`] are _unvalidated_ and so
194/// they may contain either _valid_ or _invalid_ data. Infallible accesses to variant objects and
195/// arrays, such as iteration and indexing will panic if the underlying bytes are _invalid_, and
196/// fallible alternatives are provided as panic-free alternatives. [`Self::with_full_validation`] can also be
197/// used to _validate_ an _unvalidated_ instance, if desired.
198///
199/// _Unvalidated_ instances can be constructed in constant time. This can be useful if the caller
200/// knows the underlying bytes were already validated previously, or if the caller intends to
201/// perform a small number of (fallible) accesses to a large variant value.
202///
203/// A _validated_ variant value guarantees that the associated [metadata] and all nested [object]
204/// and [array] values are _valid_. Primitive variant subtypes are always _valid_ by construction.
205///
206/// # Safety
207///
208/// Even an _invalid_ variant value is still _safe_ to use in the Rust sense. Accessing it with
209/// infallible methods may cause panics but will never lead to undefined behavior.
210///
211/// [metadata]: VariantMetadata#Validation
212/// [object]: VariantObject#Validation
213/// [array]: VariantList#Validation
214#[derive(Debug, Clone, PartialEq)]
215pub enum Variant<'m, 'v> {
216    /// Primitive type: Null
217    Null,
218    /// Primitive (type_id=1): INT(8, SIGNED)
219    Int8(i8),
220    /// Primitive (type_id=1): INT(16, SIGNED)
221    Int16(i16),
222    /// Primitive (type_id=1): INT(32, SIGNED)
223    Int32(i32),
224    /// Primitive (type_id=1): INT(64, SIGNED)
225    Int64(i64),
226    /// Primitive (type_id=1): DATE
227    Date(NaiveDate),
228    /// Primitive (type_id=1): TIMESTAMP(isAdjustedToUTC=true, MICROS)
229    TimestampMicros(DateTime<Utc>),
230    /// Primitive (type_id=1): TIMESTAMP(isAdjustedToUTC=false, MICROS)
231    TimestampNtzMicros(NaiveDateTime),
232    /// Primitive (type_id=1): DECIMAL(precision, scale) 32-bits
233    Decimal4(VariantDecimal4),
234    /// Primitive (type_id=1): DECIMAL(precision, scale) 64-bits
235    Decimal8(VariantDecimal8),
236    /// Primitive (type_id=1): DECIMAL(precision, scale) 128-bits
237    Decimal16(VariantDecimal16),
238    /// Primitive (type_id=1): FLOAT
239    Float(f32),
240    /// Primitive (type_id=1): DOUBLE
241    Double(f64),
242    /// Primitive (type_id=1): BOOLEAN (true)
243    BooleanTrue,
244    /// Primitive (type_id=1): BOOLEAN (false)
245    BooleanFalse,
246    // Note: only need the *value* buffer for these types
247    /// Primitive (type_id=1): BINARY
248    Binary(&'v [u8]),
249    /// Primitive (type_id=1): STRING
250    String(&'v str),
251    /// Short String (type_id=2): STRING
252    ShortString(ShortString<'v>),
253    // need both metadata & value
254    /// Object (type_id=3): N/A
255    Object(VariantObject<'m, 'v>),
256    /// Array (type_id=4): N/A
257    List(VariantList<'m, 'v>),
258}
259
260// We don't want this to grow because it could hurt performance of a frequently-created type.
261const _: () = crate::utils::expect_size_of::<Variant>(80);
262
263impl<'m, 'v> Variant<'m, 'v> {
264    /// Attempts to interpret a metadata and value buffer pair as a new `Variant`.
265    ///
266    /// The instance is fully [validated].
267    ///
268    /// # Example
269    /// ```
270    /// use parquet_variant::{Variant, VariantMetadata};
271    /// let metadata = [0x01, 0x00, 0x00];
272    /// let value = [0x09, 0x48, 0x49];
273    /// // parse the header metadata
274    /// assert_eq!(
275    ///   Variant::from("HI"),
276    ///   Variant::try_new(&metadata, &value).unwrap()
277    /// );
278    /// ```
279    ///
280    /// [validated]: Self#Validation
281    pub fn try_new(metadata: &'m [u8], value: &'v [u8]) -> Result<Self, ArrowError> {
282        let metadata = VariantMetadata::try_new(metadata)?;
283        Self::try_new_with_metadata(metadata, value)
284    }
285
286    /// Attempts to interpret a metadata and value buffer pair as a new `Variant`.
287    ///
288    /// The instance is [unvalidated].
289    ///
290    /// # Example
291    /// ```
292    /// use parquet_variant::{Variant, VariantMetadata};
293    /// let metadata = [0x01, 0x00, 0x00];
294    /// let value = [0x09, 0x48, 0x49];
295    /// // parse the header metadata
296    /// assert_eq!(
297    ///   Variant::from("HI"),
298    ///   Variant::new(&metadata, &value)
299    /// );
300    /// ```
301    ///
302    /// [unvalidated]: Self#Validation
303    pub fn new(metadata: &'m [u8], value: &'v [u8]) -> Self {
304        let metadata = VariantMetadata::try_new_with_shallow_validation(metadata)
305            .expect("Invalid variant metadata");
306        Self::try_new_with_metadata_and_shallow_validation(metadata, value)
307            .expect("Invalid variant data")
308    }
309
310    /// Create a new variant with existing metadata.
311    ///
312    /// The instance is fully [validated].
313    ///
314    /// # Example
315    /// ```
316    /// # use parquet_variant::{Variant, VariantMetadata};
317    /// let metadata = [0x01, 0x00, 0x00];
318    /// let value = [0x09, 0x48, 0x49];
319    /// // parse the header metadata first
320    /// let metadata = VariantMetadata::new(&metadata);
321    /// assert_eq!(
322    ///   Variant::from("HI"),
323    ///   Variant::try_new_with_metadata(metadata, &value).unwrap()
324    /// );
325    /// ```
326    ///
327    /// [validated]: Self#Validation
328    pub fn try_new_with_metadata(
329        metadata: VariantMetadata<'m>,
330        value: &'v [u8],
331    ) -> Result<Self, ArrowError> {
332        Self::try_new_with_metadata_and_shallow_validation(metadata, value)?.with_full_validation()
333    }
334
335    /// Similar to [`Self::try_new_with_metadata`], but [unvalidated].
336    ///
337    /// [unvalidated]: Self#Validation
338    pub fn new_with_metadata(metadata: VariantMetadata<'m>, value: &'v [u8]) -> Self {
339        Self::try_new_with_metadata_and_shallow_validation(metadata, value)
340            .expect("Invalid variant")
341    }
342
343    // The actual constructor, which only performs shallow (constant-time) validation.
344    fn try_new_with_metadata_and_shallow_validation(
345        metadata: VariantMetadata<'m>,
346        value: &'v [u8],
347    ) -> Result<Self, ArrowError> {
348        let value_metadata = first_byte_from_slice(value)?;
349        let value_data = slice_from_slice(value, 1..)?;
350        let new_self = match get_basic_type(value_metadata) {
351            VariantBasicType::Primitive => match get_primitive_type(value_metadata)? {
352                VariantPrimitiveType::Null => Variant::Null,
353                VariantPrimitiveType::Int8 => Variant::Int8(decoder::decode_int8(value_data)?),
354                VariantPrimitiveType::Int16 => Variant::Int16(decoder::decode_int16(value_data)?),
355                VariantPrimitiveType::Int32 => Variant::Int32(decoder::decode_int32(value_data)?),
356                VariantPrimitiveType::Int64 => Variant::Int64(decoder::decode_int64(value_data)?),
357                VariantPrimitiveType::Decimal4 => {
358                    let (integer, scale) = decoder::decode_decimal4(value_data)?;
359                    Variant::Decimal4(VariantDecimal4::try_new(integer, scale)?)
360                }
361                VariantPrimitiveType::Decimal8 => {
362                    let (integer, scale) = decoder::decode_decimal8(value_data)?;
363                    Variant::Decimal8(VariantDecimal8::try_new(integer, scale)?)
364                }
365                VariantPrimitiveType::Decimal16 => {
366                    let (integer, scale) = decoder::decode_decimal16(value_data)?;
367                    Variant::Decimal16(VariantDecimal16::try_new(integer, scale)?)
368                }
369                VariantPrimitiveType::Float => Variant::Float(decoder::decode_float(value_data)?),
370                VariantPrimitiveType::Double => {
371                    Variant::Double(decoder::decode_double(value_data)?)
372                }
373                VariantPrimitiveType::BooleanTrue => Variant::BooleanTrue,
374                VariantPrimitiveType::BooleanFalse => Variant::BooleanFalse,
375                VariantPrimitiveType::Date => Variant::Date(decoder::decode_date(value_data)?),
376                VariantPrimitiveType::TimestampMicros => {
377                    Variant::TimestampMicros(decoder::decode_timestamp_micros(value_data)?)
378                }
379                VariantPrimitiveType::TimestampNtzMicros => {
380                    Variant::TimestampNtzMicros(decoder::decode_timestampntz_micros(value_data)?)
381                }
382                VariantPrimitiveType::Binary => {
383                    Variant::Binary(decoder::decode_binary(value_data)?)
384                }
385                VariantPrimitiveType::String => {
386                    Variant::String(decoder::decode_long_string(value_data)?)
387                }
388            },
389            VariantBasicType::ShortString => {
390                Variant::ShortString(decoder::decode_short_string(value_metadata, value_data)?)
391            }
392            VariantBasicType::Object => Variant::Object(
393                VariantObject::try_new_with_shallow_validation(metadata, value)?,
394            ),
395            VariantBasicType::Array => Variant::List(VariantList::try_new_with_shallow_validation(
396                metadata, value,
397            )?),
398        };
399        Ok(new_self)
400    }
401
402    /// True if this variant instance has already been [validated].
403    ///
404    /// [validated]: Self#Validation
405    pub fn is_fully_validated(&self) -> bool {
406        match self {
407            Variant::List(list) => list.is_fully_validated(),
408            Variant::Object(obj) => obj.is_fully_validated(),
409            _ => true,
410        }
411    }
412
413    /// Recursively validates this variant value, ensuring that infallible access will not panic due
414    /// to invalid bytes.
415    ///
416    /// Variant leaf values are always valid by construction, but [objects] and [arrays] can be
417    /// constructed in unvalidated (and potentially invalid) state.
418    ///
419    /// If [`Self::is_fully_validated`] is true, validation is a no-op. Otherwise, the cost is `O(m + v)`
420    /// where `m` and `v` are the sizes of metadata and value buffers, respectively.
421    ///
422    /// [objects]: VariantObject#Validation
423    /// [arrays]: VariantList#Validation
424    pub fn with_full_validation(self) -> Result<Self, ArrowError> {
425        use Variant::*;
426        match self {
427            List(list) => list.with_full_validation().map(List),
428            Object(obj) => obj.with_full_validation().map(Object),
429            _ => Ok(self),
430        }
431    }
432
433    /// Converts this variant to `()` if it is null.
434    ///
435    /// Returns `Some(())` for null variants,
436    /// `None` for non-null variants.
437    ///
438    /// # Examples
439    ///
440    /// ```
441    /// use parquet_variant::Variant;
442    ///
443    /// // you can extract `()` from a null variant
444    /// let v1 = Variant::from(());
445    /// assert_eq!(v1.as_null(), Some(()));
446    ///
447    /// // but not from other variants
448    /// let v2 = Variant::from("hello!");
449    /// assert_eq!(v2.as_null(), None);
450    /// ```
451    pub fn as_null(&self) -> Option<()> {
452        matches!(self, Variant::Null).then_some(())
453    }
454
455    /// Converts this variant to a `bool` if possible.
456    ///
457    /// Returns `Some(bool)` for boolean variants,
458    /// `None` for non-boolean variants.
459    ///
460    /// # Examples
461    ///
462    /// ```
463    /// use parquet_variant::Variant;
464    ///
465    /// // you can extract a bool from the true variant
466    /// let v1 = Variant::from(true);
467    /// assert_eq!(v1.as_boolean(), Some(true));
468    ///
469    /// // and the false variant
470    /// let v2 = Variant::from(false);
471    /// assert_eq!(v2.as_boolean(), Some(false));
472    ///
473    /// // but not from other variants
474    /// let v3 = Variant::from("hello!");
475    /// assert_eq!(v3.as_boolean(), None);
476    /// ```
477    pub fn as_boolean(&self) -> Option<bool> {
478        match self {
479            Variant::BooleanTrue => Some(true),
480            Variant::BooleanFalse => Some(false),
481            _ => None,
482        }
483    }
484
485    /// Converts this variant to a `NaiveDate` if possible.
486    ///
487    /// Returns `Some(NaiveDate)` for date variants,
488    /// `None` for non-date variants.
489    ///
490    /// # Examples
491    ///
492    /// ```
493    /// use parquet_variant::Variant;
494    /// use chrono::NaiveDate;
495    ///
496    /// // you can extract a NaiveDate from a date variant
497    /// let date = NaiveDate::from_ymd_opt(2025, 4, 12).unwrap();
498    /// let v1 = Variant::from(date);
499    /// assert_eq!(v1.as_naive_date(), Some(date));
500    ///
501    /// // but not from other variants
502    /// let v2 = Variant::from("hello!");
503    /// assert_eq!(v2.as_naive_date(), None);
504    /// ```
505    pub fn as_naive_date(&self) -> Option<NaiveDate> {
506        if let Variant::Date(d) = self {
507            Some(*d)
508        } else {
509            None
510        }
511    }
512
513    /// Converts this variant to a `DateTime<Utc>` if possible.
514    ///
515    /// Returns `Some(DateTime<Utc>)` for timestamp variants,
516    /// `None` for non-timestamp variants.
517    ///
518    /// # Examples
519    ///
520    /// ```
521    /// use parquet_variant::Variant;
522    /// use chrono::NaiveDate;
523    ///
524    /// // you can extract a DateTime<Utc> from a UTC-adjusted variant
525    /// let datetime = NaiveDate::from_ymd_opt(2025, 4, 16).unwrap().and_hms_milli_opt(12, 34, 56, 780).unwrap().and_utc();
526    /// let v1 = Variant::from(datetime);
527    /// assert_eq!(v1.as_datetime_utc(), Some(datetime));
528    ///
529    /// // or a non-UTC-adjusted variant
530    /// let datetime = NaiveDate::from_ymd_opt(2025, 4, 16).unwrap().and_hms_milli_opt(12, 34, 56, 780).unwrap();
531    /// let v2 = Variant::from(datetime);
532    /// assert_eq!(v2.as_datetime_utc(), Some(datetime.and_utc()));
533    ///
534    /// // but not from other variants
535    /// let v3 = Variant::from("hello!");
536    /// assert_eq!(v3.as_datetime_utc(), None);
537    /// ```
538    pub fn as_datetime_utc(&self) -> Option<DateTime<Utc>> {
539        match *self {
540            Variant::TimestampMicros(d) => Some(d),
541            Variant::TimestampNtzMicros(d) => Some(d.and_utc()),
542            _ => None,
543        }
544    }
545
546    /// Converts this variant to a `NaiveDateTime` if possible.
547    ///
548    /// Returns `Some(NaiveDateTime)` for timestamp variants,
549    /// `None` for non-timestamp variants.
550    ///
551    /// # Examples
552    ///
553    /// ```
554    /// use parquet_variant::Variant;
555    /// use chrono::NaiveDate;
556    ///
557    /// // you can extract a NaiveDateTime from a non-UTC-adjusted variant
558    /// let datetime = NaiveDate::from_ymd_opt(2025, 4, 16).unwrap().and_hms_milli_opt(12, 34, 56, 780).unwrap();
559    /// let v1 = Variant::from(datetime);
560    /// assert_eq!(v1.as_naive_datetime(), Some(datetime));
561    ///
562    /// // or a UTC-adjusted variant
563    /// let datetime = NaiveDate::from_ymd_opt(2025, 4, 16).unwrap().and_hms_milli_opt(12, 34, 56, 780).unwrap().and_utc();
564    /// let v2 = Variant::from(datetime);
565    /// assert_eq!(v2.as_naive_datetime(), Some(datetime.naive_utc()));
566    ///
567    /// // but not from other variants
568    /// let v3 = Variant::from("hello!");
569    /// assert_eq!(v3.as_naive_datetime(), None);
570    /// ```
571    pub fn as_naive_datetime(&self) -> Option<NaiveDateTime> {
572        match *self {
573            Variant::TimestampNtzMicros(d) => Some(d),
574            Variant::TimestampMicros(d) => Some(d.naive_utc()),
575            _ => None,
576        }
577    }
578
579    /// Converts this variant to a `&[u8]` if possible.
580    ///
581    /// Returns `Some(&[u8])` for binary variants,
582    /// `None` for non-binary variants.
583    ///
584    /// # Examples
585    ///
586    /// ```
587    /// use parquet_variant::Variant;
588    ///
589    /// // you can extract a byte slice from a binary variant
590    /// let data = b"hello!";
591    /// let v1 = Variant::Binary(data);
592    /// assert_eq!(v1.as_u8_slice(), Some(data.as_slice()));
593    ///
594    /// // but not from other variant types
595    /// let v2 = Variant::from(123i64);
596    /// assert_eq!(v2.as_u8_slice(), None);
597    /// ```
598    pub fn as_u8_slice(&'v self) -> Option<&'v [u8]> {
599        if let Variant::Binary(d) = self {
600            Some(d)
601        } else {
602            None
603        }
604    }
605
606    /// Converts this variant to a `&str` if possible.
607    ///
608    /// Returns `Some(&str)` for string variants (both regular and short strings),
609    /// `None` for non-string variants.
610    ///
611    /// # Examples
612    ///
613    /// ```
614    /// use parquet_variant::Variant;
615    ///
616    /// // you can extract a string from string variants
617    /// let s = "hello!";
618    /// let v1 = Variant::from(s);
619    /// assert_eq!(v1.as_string(), Some(s));
620    ///
621    /// // but not from other variants
622    /// let v2 = Variant::from(123i64);
623    /// assert_eq!(v2.as_string(), None);
624    /// ```
625    pub fn as_string(&'v self) -> Option<&'v str> {
626        match self {
627            Variant::String(s) | Variant::ShortString(ShortString(s)) => Some(s),
628            _ => None,
629        }
630    }
631
632    /// Converts this variant to an `i8` if possible.
633    ///
634    /// Returns `Some(i8)` for integer variants that fit in `i8` range,
635    /// `None` for non-integer variants or values that would overflow.
636    ///
637    /// # Examples
638    ///
639    /// ```
640    /// use parquet_variant::Variant;
641    ///
642    /// // you can read an int64 variant into an i8 if it fits
643    /// let v1 = Variant::from(123i64);
644    /// assert_eq!(v1.as_int8(), Some(123i8));
645    ///
646    /// // but not if it would overflow
647    /// let v2 = Variant::from(1234i64);
648    /// assert_eq!(v2.as_int8(), None);
649    ///
650    /// // or if the variant cannot be cast into an integer
651    /// let v3 = Variant::from("hello!");
652    /// assert_eq!(v3.as_int8(), None);
653    /// ```
654    pub fn as_int8(&self) -> Option<i8> {
655        match *self {
656            Variant::Int8(i) => Some(i),
657            Variant::Int16(i) => i.try_into().ok(),
658            Variant::Int32(i) => i.try_into().ok(),
659            Variant::Int64(i) => i.try_into().ok(),
660            Variant::Decimal4(d) if d.scale() == 0 => d.integer().try_into().ok(),
661            Variant::Decimal8(d) if d.scale() == 0 => d.integer().try_into().ok(),
662            Variant::Decimal16(d) if d.scale() == 0 => d.integer().try_into().ok(),
663            _ => None,
664        }
665    }
666
667    /// Converts this variant to an `i16` if possible.
668    ///
669    /// Returns `Some(i16)` for integer variants that fit in `i16` range,
670    /// `None` for non-integer variants or values that would overflow.
671    ///
672    /// # Examples
673    ///
674    /// ```
675    /// use parquet_variant::Variant;
676    ///
677    /// // you can read an int64 variant into an i16 if it fits
678    /// let v1 = Variant::from(123i64);
679    /// assert_eq!(v1.as_int16(), Some(123i16));
680    ///
681    /// // but not if it would overflow
682    /// let v2 = Variant::from(123456i64);
683    /// assert_eq!(v2.as_int16(), None);
684    ///
685    /// // or if the variant cannot be cast into an integer
686    /// let v3 = Variant::from("hello!");
687    /// assert_eq!(v3.as_int16(), None);
688    /// ```
689    pub fn as_int16(&self) -> Option<i16> {
690        match *self {
691            Variant::Int8(i) => Some(i.into()),
692            Variant::Int16(i) => Some(i),
693            Variant::Int32(i) => i.try_into().ok(),
694            Variant::Int64(i) => i.try_into().ok(),
695            Variant::Decimal4(d) if d.scale() == 0 => d.integer().try_into().ok(),
696            Variant::Decimal8(d) if d.scale() == 0 => d.integer().try_into().ok(),
697            Variant::Decimal16(d) if d.scale() == 0 => d.integer().try_into().ok(),
698            _ => None,
699        }
700    }
701
702    /// Converts this variant to an `i32` if possible.
703    ///
704    /// Returns `Some(i32)` for integer variants that fit in `i32` range,
705    /// `None` for non-integer variants or values that would overflow.
706    ///
707    /// # Examples
708    ///
709    /// ```
710    /// use parquet_variant::Variant;
711    ///
712    /// // you can read an int64 variant into an i32 if it fits
713    /// let v1 = Variant::from(123i64);
714    /// assert_eq!(v1.as_int32(), Some(123i32));
715    ///
716    /// // but not if it would overflow
717    /// let v2 = Variant::from(12345678901i64);
718    /// assert_eq!(v2.as_int32(), None);
719    ///
720    /// // or if the variant cannot be cast into an integer
721    /// let v3 = Variant::from("hello!");
722    /// assert_eq!(v3.as_int32(), None);
723    /// ```
724    pub fn as_int32(&self) -> Option<i32> {
725        match *self {
726            Variant::Int8(i) => Some(i.into()),
727            Variant::Int16(i) => Some(i.into()),
728            Variant::Int32(i) => Some(i),
729            Variant::Int64(i) => i.try_into().ok(),
730            Variant::Decimal4(d) if d.scale() == 0 => Some(d.integer()),
731            Variant::Decimal8(d) if d.scale() == 0 => d.integer().try_into().ok(),
732            Variant::Decimal16(d) if d.scale() == 0 => d.integer().try_into().ok(),
733            _ => None,
734        }
735    }
736
737    /// Converts this variant to an `i64` if possible.
738    ///
739    /// Returns `Some(i64)` for integer variants that fit in `i64` range,
740    /// `None` for non-integer variants or values that would overflow.
741    ///
742    /// # Examples
743    ///
744    /// ```
745    /// use parquet_variant::Variant;
746    ///
747    /// // you can read an int64 variant into an i64
748    /// let v1 = Variant::from(123i64);
749    /// assert_eq!(v1.as_int64(), Some(123i64));
750    ///
751    /// // but not a variant that cannot be cast into an integer
752    /// let v2 = Variant::from("hello!");
753    /// assert_eq!(v2.as_int64(), None);
754    /// ```
755    pub fn as_int64(&self) -> Option<i64> {
756        match *self {
757            Variant::Int8(i) => Some(i.into()),
758            Variant::Int16(i) => Some(i.into()),
759            Variant::Int32(i) => Some(i.into()),
760            Variant::Int64(i) => Some(i),
761            Variant::Decimal4(d) if d.scale() == 0 => Some(d.integer().into()),
762            Variant::Decimal8(d) if d.scale() == 0 => Some(d.integer()),
763            Variant::Decimal16(d) if d.scale() == 0 => d.integer().try_into().ok(),
764            _ => None,
765        }
766    }
767
768    /// Converts this variant to tuple with a 4-byte unscaled value if possible.
769    ///
770    /// Returns `Some((i32, u8))` for decimal variants where the unscaled value
771    /// fits in `i32` range,
772    /// `None` for non-decimal variants or decimal values that would overflow.
773    ///
774    /// # Examples
775    ///
776    /// ```
777    /// use parquet_variant::{Variant, VariantDecimal4, VariantDecimal8};
778    ///
779    /// // you can extract decimal parts from smaller or equally-sized decimal variants
780    /// let v1 = Variant::from(VariantDecimal4::try_new(1234_i32, 2).unwrap());
781    /// assert_eq!(v1.as_decimal4(), VariantDecimal4::try_new(1234_i32, 2).ok());
782    ///
783    /// // and from larger decimal variants if they fit
784    /// let v2 = Variant::from(VariantDecimal8::try_new(1234_i64, 2).unwrap());
785    /// assert_eq!(v2.as_decimal4(), VariantDecimal4::try_new(1234_i32, 2).ok());
786    ///
787    /// // but not if the value would overflow i32
788    /// let v3 = Variant::from(VariantDecimal8::try_new(12345678901i64, 2).unwrap());
789    /// assert_eq!(v3.as_decimal4(), None);
790    ///
791    /// // or if the variant is not a decimal
792    /// let v4 = Variant::from("hello!");
793    /// assert_eq!(v4.as_decimal4(), None);
794    /// ```
795    pub fn as_decimal4(&self) -> Option<VariantDecimal4> {
796        match *self {
797            Variant::Int8(i) => i32::from(i).try_into().ok(),
798            Variant::Int16(i) => i32::from(i).try_into().ok(),
799            Variant::Int32(i) => i.try_into().ok(),
800            Variant::Int64(i) => i32::try_from(i).ok()?.try_into().ok(),
801            Variant::Decimal4(decimal4) => Some(decimal4),
802            Variant::Decimal8(decimal8) => decimal8.try_into().ok(),
803            Variant::Decimal16(decimal16) => decimal16.try_into().ok(),
804            _ => None,
805        }
806    }
807
808    /// Converts this variant to tuple with an 8-byte unscaled value if possible.
809    ///
810    /// Returns `Some((i64, u8))` for decimal variants where the unscaled value
811    /// fits in `i64` range,
812    /// `None` for non-decimal variants or decimal values that would overflow.
813    ///
814    /// # Examples
815    ///
816    /// ```
817    /// use parquet_variant::{Variant, VariantDecimal4, VariantDecimal8, VariantDecimal16};
818    ///
819    /// // you can extract decimal parts from smaller or equally-sized decimal variants
820    /// let v1 = Variant::from(VariantDecimal4::try_new(1234_i32, 2).unwrap());
821    /// assert_eq!(v1.as_decimal8(), VariantDecimal8::try_new(1234_i64, 2).ok());
822    ///
823    /// // and from larger decimal variants if they fit
824    /// let v2 = Variant::from(VariantDecimal16::try_new(1234_i128, 2).unwrap());
825    /// assert_eq!(v2.as_decimal8(), VariantDecimal8::try_new(1234_i64, 2).ok());
826    ///
827    /// // but not if the value would overflow i64
828    /// let v3 = Variant::from(VariantDecimal16::try_new(2e19 as i128, 2).unwrap());
829    /// assert_eq!(v3.as_decimal8(), None);
830    ///
831    /// // or if the variant is not a decimal
832    /// let v4 = Variant::from("hello!");
833    /// assert_eq!(v4.as_decimal8(), None);
834    /// ```
835    pub fn as_decimal8(&self) -> Option<VariantDecimal8> {
836        match *self {
837            Variant::Int8(i) => i64::from(i).try_into().ok(),
838            Variant::Int16(i) => i64::from(i).try_into().ok(),
839            Variant::Int32(i) => i64::from(i).try_into().ok(),
840            Variant::Int64(i) => i.try_into().ok(),
841            Variant::Decimal4(decimal4) => Some(decimal4.into()),
842            Variant::Decimal8(decimal8) => Some(decimal8),
843            Variant::Decimal16(decimal16) => decimal16.try_into().ok(),
844            _ => None,
845        }
846    }
847
848    /// Converts this variant to tuple with a 16-byte unscaled value if possible.
849    ///
850    /// Returns `Some((i128, u8))` for decimal variants where the unscaled value
851    /// fits in `i128` range,
852    /// `None` for non-decimal variants or decimal values that would overflow.
853    ///
854    /// # Examples
855    ///
856    /// ```
857    /// use parquet_variant::{Variant, VariantDecimal16, VariantDecimal4};
858    ///
859    /// // you can extract decimal parts from smaller or equally-sized decimal variants
860    /// let v1 = Variant::from(VariantDecimal4::try_new(1234_i32, 2).unwrap());
861    /// assert_eq!(v1.as_decimal16(), VariantDecimal16::try_new(1234_i128, 2).ok());
862    ///
863    /// // but not if the variant is not a decimal
864    /// let v2 = Variant::from("hello!");
865    /// assert_eq!(v2.as_decimal16(), None);
866    /// ```
867    pub fn as_decimal16(&self) -> Option<VariantDecimal16> {
868        match *self {
869            Variant::Int8(i) => i128::from(i).try_into().ok(),
870            Variant::Int16(i) => i128::from(i).try_into().ok(),
871            Variant::Int32(i) => i128::from(i).try_into().ok(),
872            Variant::Int64(i) => i128::from(i).try_into().ok(),
873            Variant::Decimal4(decimal4) => Some(decimal4.into()),
874            Variant::Decimal8(decimal8) => Some(decimal8.into()),
875            Variant::Decimal16(decimal16) => Some(decimal16),
876            _ => None,
877        }
878    }
879    /// Converts this variant to an `f32` if possible.
880    ///
881    /// Returns `Some(f32)` for float and double variants,
882    /// `None` for non-floating-point variants.
883    ///
884    /// # Examples
885    ///
886    /// ```
887    /// use parquet_variant::Variant;
888    ///
889    /// // you can extract an f32 from a float variant
890    /// let v1 = Variant::from(std::f32::consts::PI);
891    /// assert_eq!(v1.as_f32(), Some(std::f32::consts::PI));
892    ///
893    /// // and from a double variant (with loss of precision to nearest f32)
894    /// let v2 = Variant::from(std::f64::consts::PI);
895    /// assert_eq!(v2.as_f32(), Some(std::f32::consts::PI));
896    ///
897    /// // but not from other variants
898    /// let v3 = Variant::from("hello!");
899    /// assert_eq!(v3.as_f32(), None);
900    /// ```
901    #[allow(clippy::cast_possible_truncation)]
902    pub fn as_f32(&self) -> Option<f32> {
903        match *self {
904            Variant::Float(i) => Some(i),
905            Variant::Double(i) => Some(i as f32),
906            _ => None,
907        }
908    }
909
910    /// Converts this variant to an `f64` if possible.
911    ///
912    /// Returns `Some(f64)` for float and double variants,
913    /// `None` for non-floating-point variants.
914    ///
915    /// # Examples
916    ///
917    /// ```
918    /// use parquet_variant::Variant;
919    ///
920    /// // you can extract an f64 from a float variant
921    /// let v1 = Variant::from(std::f32::consts::PI);
922    /// assert_eq!(v1.as_f64(), Some(std::f32::consts::PI as f64));
923    ///
924    /// // and from a double variant
925    /// let v2 = Variant::from(std::f64::consts::PI);
926    /// assert_eq!(v2.as_f64(), Some(std::f64::consts::PI));
927    ///
928    /// // but not from other variants
929    /// let v3 = Variant::from("hello!");
930    /// assert_eq!(v3.as_f64(), None);
931    /// ```
932    pub fn as_f64(&self) -> Option<f64> {
933        match *self {
934            Variant::Float(i) => Some(i.into()),
935            Variant::Double(i) => Some(i),
936            _ => None,
937        }
938    }
939
940    /// Converts this variant to an `Object` if it is an [`VariantObject`].
941    ///
942    /// Returns `Some(&VariantObject)` for object variants,
943    /// `None` for non-object variants.
944    ///
945    /// See [`Self::get_path`] to dynamically traverse objects
946    ///
947    /// # Examples
948    /// ```
949    /// # use parquet_variant::{Variant, VariantBuilder, VariantObject};
950    /// # let (metadata, value) = {
951    /// # let mut builder = VariantBuilder::new();
952    /// #   let mut obj = builder.new_object();
953    /// #   obj.insert("name", "John");
954    /// #   obj.finish();
955    /// #   builder.finish()
956    /// # };
957    /// // object that is {"name": "John"}
958    ///  let variant = Variant::new(&metadata, &value);
959    /// // use the `as_object` method to access the object
960    /// let obj = variant.as_object().expect("variant should be an object");
961    /// assert_eq!(obj.get("name"), Some(Variant::from("John")));
962    /// ```
963    pub fn as_object(&'m self) -> Option<&'m VariantObject<'m, 'v>> {
964        if let Variant::Object(obj) = self {
965            Some(obj)
966        } else {
967            None
968        }
969    }
970
971    /// If this is an object and the requested field name exists, retrieves the corresponding field
972    /// value. Otherwise, returns None.
973    ///
974    /// This is shorthand for [`Self::as_object`] followed by [`VariantObject::get`].
975    ///
976    /// # Examples
977    /// ```
978    /// # use parquet_variant::{Variant, VariantBuilder, VariantObject};
979    /// # let mut builder = VariantBuilder::new();
980    /// # let mut obj = builder.new_object();
981    /// # obj.insert("name", "John");
982    /// # obj.finish();
983    /// # let (metadata, value) = builder.finish();
984    /// // object that is {"name": "John"}
985    ///  let variant = Variant::new(&metadata, &value);
986    /// // use the `get_object_field` method to access the object
987    /// let obj = variant.get_object_field("name");
988    /// assert_eq!(obj, Some(Variant::from("John")));
989    /// let obj = variant.get_object_field("foo");
990    /// assert!(obj.is_none());
991    /// ```
992    pub fn get_object_field(&self, field_name: &str) -> Option<Self> {
993        match self {
994            Variant::Object(object) => object.get(field_name),
995            _ => None,
996        }
997    }
998
999    /// Converts this variant to a `List` if it is a [`VariantList`].
1000    ///
1001    /// Returns `Some(&VariantList)` for list variants,
1002    /// `None` for non-list variants.
1003    ///
1004    /// See [`Self::get_path`] to dynamically traverse lists
1005    ///
1006    /// # Examples
1007    /// ```
1008    /// # use parquet_variant::{Variant, VariantBuilder, VariantList};
1009    /// # let (metadata, value) = {
1010    /// # let mut builder = VariantBuilder::new();
1011    /// #   let mut list = builder.new_list();
1012    /// #   list.append_value("John");
1013    /// #   list.append_value("Doe");
1014    /// #   list.finish();
1015    /// #   builder.finish()
1016    /// # };
1017    /// // list that is ["John", "Doe"]
1018    /// let variant = Variant::new(&metadata, &value);
1019    /// // use the `as_list` method to access the list
1020    /// let list = variant.as_list().expect("variant should be a list");
1021    /// assert_eq!(list.len(), 2);
1022    /// assert_eq!(list.get(0).unwrap(), Variant::from("John"));
1023    /// assert_eq!(list.get(1).unwrap(), Variant::from("Doe"));
1024    /// ```
1025    pub fn as_list(&'m self) -> Option<&'m VariantList<'m, 'v>> {
1026        if let Variant::List(list) = self {
1027            Some(list)
1028        } else {
1029            None
1030        }
1031    }
1032
1033    /// If this is a list and the requested index is in bounds, retrieves the corresponding
1034    /// element. Otherwise, returns None.
1035    ///
1036    /// This is shorthand for [`Self::as_list`] followed by [`VariantList::get`].
1037    ///
1038    /// # Examples
1039    /// ```
1040    /// # use parquet_variant::{Variant, VariantBuilder, VariantList};
1041    /// # let mut builder = VariantBuilder::new();
1042    /// # let mut list = builder.new_list();
1043    /// # list.append_value("John");
1044    /// # list.append_value("Doe");
1045    /// # list.finish();
1046    /// # let (metadata, value) = builder.finish();
1047    /// // list that is ["John", "Doe"]
1048    /// let variant = Variant::new(&metadata, &value);
1049    /// // use the `get_list_element` method to access the list
1050    /// assert_eq!(variant.get_list_element(0), Some(Variant::from("John")));
1051    /// assert_eq!(variant.get_list_element(1), Some(Variant::from("Doe")));
1052    /// assert!(variant.get_list_element(2).is_none());
1053    /// ```
1054    pub fn get_list_element(&self, index: usize) -> Option<Self> {
1055        match self {
1056            Variant::List(list) => list.get(index),
1057            _ => None,
1058        }
1059    }
1060
1061    /// Return the metadata associated with this variant, if any.
1062    ///
1063    /// Returns `Some(&VariantMetadata)` for object and list variants,
1064    pub fn metadata(&self) -> Option<&'m VariantMetadata> {
1065        match self {
1066            Variant::Object(VariantObject { metadata, .. })
1067            | Variant::List(VariantList { metadata, .. }) => Some(metadata),
1068            _ => None,
1069        }
1070    }
1071
1072    /// Return a new Variant with the path followed.
1073    ///
1074    /// If the path is not found, `None` is returned.
1075    ///
1076    /// # Example
1077    /// ```
1078    /// # use parquet_variant::{Variant, VariantBuilder, VariantObject, VariantPath};
1079    /// # let mut builder = VariantBuilder::new();
1080    /// # let mut obj = builder.new_object();
1081    /// # let mut list = obj.new_list("foo");
1082    /// # list.append_value("bar");
1083    /// # list.append_value("baz");
1084    /// # list.finish();
1085    /// # obj.finish().unwrap();
1086    /// # let (metadata, value) = builder.finish();
1087    /// // given a variant like `{"foo": ["bar", "baz"]}`
1088    /// let variant = Variant::new(&metadata, &value);
1089    /// // Accessing a non existent path returns None
1090    /// assert_eq!(variant.get_path(&VariantPath::from("non_existent")), None);
1091    /// // Access obj["foo"]
1092    /// let path = VariantPath::from("foo");
1093    /// let foo = variant.get_path(&path).expect("field `foo` should exist");
1094    /// assert!(foo.as_list().is_some(), "field `foo` should be a list");
1095    /// // Access foo[0]
1096    /// let path = VariantPath::from(0);
1097    /// let bar = foo.get_path(&path).expect("element 0 should exist");
1098    /// // bar is a string
1099    /// assert_eq!(bar.as_string(), Some("bar"));
1100    /// // You can also access nested paths
1101    /// let path = VariantPath::from("foo").join(0);
1102    /// assert_eq!(variant.get_path(&path).unwrap(), bar);
1103    /// ```
1104    pub fn get_path(&self, path: &VariantPath) -> Option<Variant> {
1105        path.iter()
1106            .try_fold(self.clone(), |output, element| match element {
1107                VariantPathElement::Field { name } => output.get_object_field(name),
1108                VariantPathElement::Index { index } => output.get_list_element(*index),
1109            })
1110    }
1111}
1112
1113impl From<()> for Variant<'_, '_> {
1114    fn from((): ()) -> Self {
1115        Variant::Null
1116    }
1117}
1118
1119impl From<bool> for Variant<'_, '_> {
1120    fn from(value: bool) -> Self {
1121        match value {
1122            true => Variant::BooleanTrue,
1123            false => Variant::BooleanFalse,
1124        }
1125    }
1126}
1127
1128impl From<i8> for Variant<'_, '_> {
1129    fn from(value: i8) -> Self {
1130        Variant::Int8(value)
1131    }
1132}
1133
1134impl From<i16> for Variant<'_, '_> {
1135    fn from(value: i16) -> Self {
1136        Variant::Int16(value)
1137    }
1138}
1139
1140impl From<i32> for Variant<'_, '_> {
1141    fn from(value: i32) -> Self {
1142        Variant::Int32(value)
1143    }
1144}
1145
1146impl From<i64> for Variant<'_, '_> {
1147    fn from(value: i64) -> Self {
1148        Variant::Int64(value)
1149    }
1150}
1151
1152impl From<VariantDecimal4> for Variant<'_, '_> {
1153    fn from(value: VariantDecimal4) -> Self {
1154        Variant::Decimal4(value)
1155    }
1156}
1157
1158impl From<VariantDecimal8> for Variant<'_, '_> {
1159    fn from(value: VariantDecimal8) -> Self {
1160        Variant::Decimal8(value)
1161    }
1162}
1163
1164impl From<VariantDecimal16> for Variant<'_, '_> {
1165    fn from(value: VariantDecimal16) -> Self {
1166        Variant::Decimal16(value)
1167    }
1168}
1169
1170impl From<f32> for Variant<'_, '_> {
1171    fn from(value: f32) -> Self {
1172        Variant::Float(value)
1173    }
1174}
1175
1176impl From<f64> for Variant<'_, '_> {
1177    fn from(value: f64) -> Self {
1178        Variant::Double(value)
1179    }
1180}
1181
1182impl From<NaiveDate> for Variant<'_, '_> {
1183    fn from(value: NaiveDate) -> Self {
1184        Variant::Date(value)
1185    }
1186}
1187
1188impl From<DateTime<Utc>> for Variant<'_, '_> {
1189    fn from(value: DateTime<Utc>) -> Self {
1190        Variant::TimestampMicros(value)
1191    }
1192}
1193impl From<NaiveDateTime> for Variant<'_, '_> {
1194    fn from(value: NaiveDateTime) -> Self {
1195        Variant::TimestampNtzMicros(value)
1196    }
1197}
1198
1199impl<'v> From<&'v [u8]> for Variant<'_, 'v> {
1200    fn from(value: &'v [u8]) -> Self {
1201        Variant::Binary(value)
1202    }
1203}
1204
1205impl<'v> From<&'v str> for Variant<'_, 'v> {
1206    fn from(value: &'v str) -> Self {
1207        if value.len() > MAX_SHORT_STRING_BYTES {
1208            Variant::String(value)
1209        } else {
1210            Variant::ShortString(ShortString(value))
1211        }
1212    }
1213}
1214
1215impl TryFrom<(i32, u8)> for Variant<'_, '_> {
1216    type Error = ArrowError;
1217
1218    fn try_from(value: (i32, u8)) -> Result<Self, Self::Error> {
1219        Ok(Variant::Decimal4(VariantDecimal4::try_new(
1220            value.0, value.1,
1221        )?))
1222    }
1223}
1224
1225impl TryFrom<(i64, u8)> for Variant<'_, '_> {
1226    type Error = ArrowError;
1227
1228    fn try_from(value: (i64, u8)) -> Result<Self, Self::Error> {
1229        Ok(Variant::Decimal8(VariantDecimal8::try_new(
1230            value.0, value.1,
1231        )?))
1232    }
1233}
1234
1235impl TryFrom<(i128, u8)> for Variant<'_, '_> {
1236    type Error = ArrowError;
1237
1238    fn try_from(value: (i128, u8)) -> Result<Self, Self::Error> {
1239        Ok(Variant::Decimal16(VariantDecimal16::try_new(
1240            value.0, value.1,
1241        )?))
1242    }
1243}
1244
1245#[cfg(test)]
1246mod tests {
1247
1248    use super::*;
1249
1250    #[test]
1251    fn test_empty_variant_will_fail() {
1252        let metadata = VariantMetadata::try_new(&[1, 0, 0]).unwrap();
1253
1254        let err = Variant::try_new_with_metadata(metadata, &[]).unwrap_err();
1255
1256        assert!(matches!(
1257            err,
1258            ArrowError::InvalidArgumentError(ref msg) if msg == "Received empty bytes"));
1259    }
1260
1261    #[test]
1262    fn test_construct_short_string() {
1263        let short_string = ShortString::try_new("norm").expect("should fit in short string");
1264        assert_eq!(short_string.as_str(), "norm");
1265
1266        let long_string = "a".repeat(MAX_SHORT_STRING_BYTES + 1);
1267        let res = ShortString::try_new(&long_string);
1268        assert!(res.is_err());
1269    }
1270
1271    #[test]
1272    fn test_variant_decimal_conversion() {
1273        let decimal4 = VariantDecimal4::try_new(1234_i32, 2).unwrap();
1274        let variant = Variant::from(decimal4);
1275        assert_eq!(variant.as_decimal4(), Some(decimal4));
1276
1277        let decimal8 = VariantDecimal8::try_new(12345678901_i64, 2).unwrap();
1278        let variant = Variant::from(decimal8);
1279        assert_eq!(variant.as_decimal8(), Some(decimal8));
1280
1281        let decimal16 = VariantDecimal16::try_new(123456789012345678901234567890_i128, 2).unwrap();
1282        let variant = Variant::from(decimal16);
1283        assert_eq!(variant.as_decimal16(), Some(decimal16));
1284    }
1285}