diff --git a/parquet-variant/src/variant.rs b/parquet-variant/src/variant.rs index 8ae74653e948..3dae4daa0ff8 100644 --- a/parquet-variant/src/variant.rs +++ b/parquet-variant/src/variant.rs @@ -805,6 +805,166 @@ impl<'m, 'v> Variant<'m, 'v> { } } + fn generic_convert_unsigned_primitive(&self) -> Option + where + T: TryFrom + TryFrom + TryFrom + TryFrom + TryFrom, + { + match *self { + Variant::Int8(i) => i.try_into().ok(), + Variant::Int16(i) => i.try_into().ok(), + Variant::Int32(i) => i.try_into().ok(), + Variant::Int64(i) => i.try_into().ok(), + Variant::Decimal4(d) if d.scale() == 0 => d.integer().try_into().ok(), + Variant::Decimal8(d) if d.scale() == 0 => d.integer().try_into().ok(), + Variant::Decimal16(d) if d.scale() == 0 => d.integer().try_into().ok(), + _ => None, + } + } + + /// Converts this variant to a `u8` if possible. + /// + /// Returns `Some(u8)` for integer variants that fit in `u8` + /// `None` for non-integer variants or values that would overflow. + /// + /// # Examples + /// + /// ``` + /// use parquet_variant::{Variant, VariantDecimal4}; + /// + /// // you can read an int64 variant into an u8 + /// let v1 = Variant::from(123i64); + /// assert_eq!(v1.as_u8(), Some(123u8)); + /// + /// // or a Decimal4 with scale 0 into u8 + /// let d = VariantDecimal4::try_new(26, 0).unwrap(); + /// let v2 = Variant::from(d); + /// assert_eq!(v2.as_u8(), Some(26u8)); + /// + /// // but not a variant that can't fit into the range + /// let v3 = Variant::from(-1); + /// assert_eq!(v3.as_u8(), None); + /// + /// // not a variant that decimal with scale not equal to zero + /// let d = VariantDecimal4::try_new(1, 2).unwrap(); + /// let v4 = Variant::from(d); + /// assert_eq!(v4.as_u8(), None); + /// + /// // or not a variant that cannot be cast into an integer + /// let v5 = Variant::from("hello!"); + /// assert_eq!(v5.as_u8(), None); + /// ``` + pub fn as_u8(&self) -> Option { + self.generic_convert_unsigned_primitive::() + } + + /// Converts this variant to an `u16` if possible. + /// + /// Returns `Some(u16)` for integer variants that fit in `u16` + /// `None` for non-integer variants or values that would overflow. + /// + /// # Examples + /// + /// ``` + /// use parquet_variant::{Variant, VariantDecimal4}; + /// + /// // you can read an int64 variant into an u16 + /// let v1 = Variant::from(123i64); + /// assert_eq!(v1.as_u16(), Some(123u16)); + /// + /// // or a Decimal4 with scale 0 into u8 + /// let d = VariantDecimal4::try_new(u16::MAX as i32, 0).unwrap(); + /// let v2 = Variant::from(d); + /// assert_eq!(v2.as_u16(), Some(u16::MAX)); + /// + /// // but not a variant that can't fit into the range + /// let v3 = Variant::from(-1); + /// assert_eq!(v3.as_u16(), None); + /// + /// // not a variant that decimal with scale not equal to zero + /// let d = VariantDecimal4::try_new(1, 2).unwrap(); + /// let v4 = Variant::from(d); + /// assert_eq!(v4.as_u16(), None); + /// + /// // or not a variant that cannot be cast into an integer + /// let v5 = Variant::from("hello!"); + /// assert_eq!(v5.as_u16(), None); + /// ``` + pub fn as_u16(&self) -> Option { + self.generic_convert_unsigned_primitive::() + } + + /// Converts this variant to an `u32` if possible. + /// + /// Returns `Some(u32)` for integer variants that fit in `u32` + /// `None` for non-integer variants or values that would overflow. + /// + /// # Examples + /// + /// ``` + /// use parquet_variant::{Variant, VariantDecimal8}; + /// + /// // you can read an int64 variant into an u32 + /// let v1 = Variant::from(123i64); + /// assert_eq!(v1.as_u32(), Some(123u32)); + /// + /// // or a Decimal4 with scale 0 into u8 + /// let d = VariantDecimal8::try_new(u32::MAX as i64, 0).unwrap(); + /// let v2 = Variant::from(d); + /// assert_eq!(v2.as_u32(), Some(u32::MAX)); + /// + /// // but not a variant that can't fit into the range + /// let v3 = Variant::from(-1); + /// assert_eq!(v3.as_u32(), None); + /// + /// // not a variant that decimal with scale not equal to zero + /// let d = VariantDecimal8::try_new(1, 2).unwrap(); + /// let v4 = Variant::from(d); + /// assert_eq!(v4.as_u32(), None); + /// + /// // or not a variant that cannot be cast into an integer + /// let v5 = Variant::from("hello!"); + /// assert_eq!(v5.as_u32(), None); + /// ``` + pub fn as_u32(&self) -> Option { + self.generic_convert_unsigned_primitive::() + } + + /// Converts this variant to an `u64` if possible. + /// + /// Returns `Some(u64)` for integer variants that fit in `u64` + /// `None` for non-integer variants or values that would overflow. + /// + /// # Examples + /// + /// ``` + /// use parquet_variant::{Variant, VariantDecimal16}; + /// + /// // you can read an int64 variant into an u64 + /// let v1 = Variant::from(123i64); + /// assert_eq!(v1.as_u64(), Some(123u64)); + /// + /// // or a Decimal16 with scale 0 into u8 + /// let d = VariantDecimal16::try_new(u64::MAX as i128, 0).unwrap(); + /// let v2 = Variant::from(d); + /// assert_eq!(v2.as_u64(), Some(u64::MAX)); + /// + /// // but not a variant that can't fit into the range + /// let v3 = Variant::from(-1); + /// assert_eq!(v3.as_u64(), None); + /// + /// // not a variant that decimal with scale not equal to zero + /// let d = VariantDecimal16::try_new(1, 2).unwrap(); + /// let v4 = Variant::from(d); + /// assert_eq!(v4.as_u64(), None); + /// + /// // or not a variant that cannot be cast into an integer + /// let v5 = Variant::from("hello!"); + /// assert_eq!(v5.as_u64(), None); + /// ``` + pub fn as_u64(&self) -> Option { + self.generic_convert_unsigned_primitive::() + } + /// Converts this variant to tuple with a 4-byte unscaled value if possible. /// /// Returns `Some((i32, u8))` for decimal variants where the unscaled value