This is an automated email from the ASF dual-hosted git repository.

alamb pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-rs.git


The following commit(s) were added to refs/heads/main by this push:
     new 989ac0331a [Variant] Align cast logic for variant_get to cast kernel 
for numeric/bool types (#9563)
989ac0331a is described below

commit 989ac0331a4ce1bb4e782ad11e96b3120aaf4719
Author: Congxian Qiu <[email protected]>
AuthorDate: Tue Apr 7 03:36:58 2026 +0800

    [Variant] Align cast logic for variant_get to cast kernel for numeric/bool 
types (#9563)
    
    # Which issue does this PR close?
    
    <!--
    We generally require a GitHub issue to be filed for all bug fixes and
    enhancements and this helps us generate change logs for our releases.
    You can link an issue to this PR using the GitHub syntax.
    -->
    
    - Closes #9564 .
    
    
    # What changes are included in this PR?
    
    <!--
    There is no need to duplicate the description in the issue here but it
    is sometimes worth providing a summary of the individual changes in this
    PR.
    -->
    
    Align tests with cast kernel
    
    # Are these changes tested?
    
    Covered by the existing tests
    <!--
    We typically require tests for all PRs in order to:
    1. Prevent the code from being accidentally broken by subsequent changes
    2. Serve as another way to document the expected behavior of the code
    
    If tests are not included in your PR, please explain why (for example,
    are they covered by existing tests)?
    -->
    
    # Are there any user-facing changes?
    
    <!--
    If there are user-facing changes then we may require documentation to be
    updated before approving the PR.
    
    If there are any breaking changes to public APIs, please call them out.
    -->
    Yes, changed the logic for
    
`Variant::asboolean/as_int8/as_int16/as_int32/as_int64/as_u8/as_u16/as_u32/as_u64/as_f16/as_f32/as_f64
---
 arrow-cast/src/cast/mod.rs                   |  48 +++-
 arrow-cast/src/cast/string.rs                |  37 ++-
 parquet-variant-compute/src/shred_variant.rs |   2 +-
 parquet-variant-compute/src/variant_get.rs   |   8 +-
 parquet-variant/Cargo.toml                   |   2 +
 parquet-variant/src/utils.rs                 |  17 --
 parquet-variant/src/variant.rs               | 403 +++++++++++++++------------
 7 files changed, 289 insertions(+), 228 deletions(-)

diff --git a/arrow-cast/src/cast/mod.rs b/arrow-cast/src/cast/mod.rs
index f152f557e2..c96e7c8f6a 100644
--- a/arrow-cast/src/cast/mod.rs
+++ b/arrow-cast/src/cast/mod.rs
@@ -73,6 +73,7 @@ use arrow_select::take::take;
 use num_traits::{NumCast, ToPrimitive, cast::AsPrimitive};
 
 pub use decimal::{DecimalCast, rescale_decimal};
+pub use string::cast_single_string_to_boolean_default;
 
 /// CastOptions provides a way to override the default cast behaviors
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -2475,7 +2476,7 @@ where
     R::Native: NumCast,
 {
     from.try_unary(|value| {
-        num_traits::cast::cast::<T::Native, R::Native>(value).ok_or_else(|| {
+        num_cast::<T::Native, R::Native>(value).ok_or_else(|| {
             ArrowError::CastError(format!(
                 "Can't cast value {:?} to type {}",
                 value,
@@ -2485,6 +2486,17 @@ where
     })
 }
 
+/// Natural cast between numeric types
+/// Return None if the input `value` can't be casted to type `O`.
+#[inline]
+pub fn num_cast<I, O>(value: I) -> Option<O>
+where
+    I: NumCast,
+    O: NumCast,
+{
+    num_traits::cast::cast::<I, O>(value)
+}
+
 // Natural cast between numeric types
 // If the value of T can't be casted to R, it will be converted to null
 fn numeric_cast<T, R>(from: &PrimitiveArray<T>) -> PrimitiveArray<R>
@@ -2494,7 +2506,7 @@ where
     T::Native: NumCast,
     R::Native: NumCast,
 {
-    from.unary_opt::<_, R>(num_traits::cast::cast::<T::Native, R::Native>)
+    from.unary_opt::<_, R>(num_cast::<T::Native, R::Native>)
 }
 
 fn cast_numeric_to_binary<FROM: ArrowPrimitiveType, O: OffsetSizeTrait>(
@@ -2551,16 +2563,23 @@ where
     for i in 0..from.len() {
         if from.is_null(i) {
             b.append_null();
-        } else if from.value(i) != T::default_value() {
-            b.append_value(true);
         } else {
-            b.append_value(false);
+            b.append_value(cast_num_to_bool::<T::Native>(from.value(i)));
         }
     }
 
     Ok(b.finish())
 }
 
+/// Cast numeric types to boolean
+#[inline]
+pub fn cast_num_to_bool<I>(value: I) -> bool
+where
+    I: Default + PartialEq,
+{
+    value != I::default()
+}
+
 /// Cast Boolean types to numeric
 ///
 /// `false` returns 0 while `true` returns 1
@@ -2586,11 +2605,8 @@ where
     let iter = (0..from.len()).map(|i| {
         if from.is_null(i) {
             None
-        } else if from.value(i) {
-            // a workaround to cast a primitive to T::Native, infallible
-            num_traits::cast::cast(1)
         } else {
-            Some(T::default_value())
+            single_bool_to_numeric::<T::Native>(from.value(i))
         }
     });
     // Benefit:
@@ -2600,6 +2616,20 @@ where
     unsafe { PrimitiveArray::<T>::from_trusted_len_iter(iter) }
 }
 
+/// Cast single bool value to numeric value.
+#[inline]
+pub fn single_bool_to_numeric<O>(value: bool) -> Option<O>
+where
+    O: num_traits::NumCast + Default,
+{
+    if value {
+        // a workaround to cast a primitive to type O, infallible
+        num_traits::cast::cast(1)
+    } else {
+        Some(O::default())
+    }
+}
+
 /// Helper function to cast from one `BinaryArray` or 'LargeBinaryArray' to 
'FixedSizeBinaryArray'.
 fn cast_binary_to_fixed_size_binary<O: OffsetSizeTrait>(
     array: &dyn Array,
diff --git a/arrow-cast/src/cast/string.rs b/arrow-cast/src/cast/string.rs
index 68fce85cb4..86712e15f9 100644
--- a/arrow-cast/src/cast/string.rs
+++ b/arrow-cast/src/cast/string.rs
@@ -401,18 +401,7 @@ where
     let output_array = array
         .iter()
         .map(|value| match value {
-            Some(value) => match value.to_ascii_lowercase().trim() {
-                "t" | "tr" | "tru" | "true" | "y" | "ye" | "yes" | "on" | "1" 
=> Ok(Some(true)),
-                "f" | "fa" | "fal" | "fals" | "false" | "n" | "no" | "of" | 
"off" | "0" => {
-                    Ok(Some(false))
-                }
-                invalid_value => match cast_options.safe {
-                    true => Ok(None),
-                    false => Err(ArrowError::CastError(format!(
-                        "Cannot cast value '{invalid_value}' to value of 
Boolean type",
-                    ))),
-                },
-            },
+            Some(value) => cast_single_string_to_boolean(value, cast_options),
             None => Ok(None),
         })
         .collect::<Result<BooleanArray, _>>()?;
@@ -420,6 +409,30 @@ where
     Ok(Arc::new(output_array))
 }
 
+#[inline]
+fn cast_single_string_to_boolean(
+    value: &str,
+    cast_options: &CastOptions,
+) -> Result<Option<bool>, ArrowError> {
+    match value.to_ascii_lowercase().trim() {
+        "t" | "tr" | "tru" | "true" | "y" | "ye" | "yes" | "on" | "1" => 
Ok(Some(true)),
+        "f" | "fa" | "fal" | "fals" | "false" | "n" | "no" | "of" | "off" | 
"0" => Ok(Some(false)),
+        invalid_value => match cast_options.safe {
+            true => Ok(None),
+            false => Err(ArrowError::CastError(format!(
+                "Cannot cast value '{invalid_value}' to value of Boolean type",
+            ))),
+        },
+    }
+}
+
+/// Cast a single string to boolean with default cast option(safe=true).
+pub fn cast_single_string_to_boolean_default(value: &str) -> Option<bool> {
+    cast_single_string_to_boolean(value, &CastOptions::default())
+        .ok()
+        .flatten()
+}
+
 pub(crate) fn cast_utf8_to_boolean<OffsetSize>(
     from: &dyn Array,
     cast_options: &CastOptions,
diff --git a/parquet-variant-compute/src/shred_variant.rs 
b/parquet-variant-compute/src/shred_variant.rs
index d2021e12f6..7b181179d3 100644
--- a/parquet-variant-compute/src/shred_variant.rs
+++ b/parquet-variant-compute/src/shred_variant.rs
@@ -1288,7 +1288,7 @@ mod tests {
             .downcast_ref::<arrow::array::Int32Array>()
             .unwrap();
         assert_eq!(typed_value_int32.value(0), 42);
-        assert!(typed_value_int32.is_null(1)); // float doesn't convert to 
int32
+        assert_eq!(typed_value_int32.value(1), 3);
         assert!(typed_value_int32.is_null(2)); // string doesn't convert to 
int32
 
         // Test Float64 target
diff --git a/parquet-variant-compute/src/variant_get.rs 
b/parquet-variant-compute/src/variant_get.rs
index 896900cc5d..29e28c850b 100644
--- a/parquet-variant-compute/src/variant_get.rs
+++ b/parquet-variant-compute/src/variant_get.rs
@@ -2508,7 +2508,7 @@ mod test {
     #[test]
     fn test_error_message_boolean_type_display() {
         let mut builder = VariantArrayBuilder::new(1);
-        builder.append_variant(Variant::Int32(123));
+        builder.append_variant(Variant::from("abcd"));
         let variant_array: ArrayRef = ArrayRef::from(builder.build());
 
         // Request Boolean with strict casting to force an error
@@ -2529,10 +2529,10 @@ mod test {
     #[test]
     fn test_error_message_numeric_type_display() {
         let mut builder = VariantArrayBuilder::new(1);
-        builder.append_variant(Variant::BooleanTrue);
+        builder.append_variant(Variant::from("abcd"));
         let variant_array: ArrayRef = ArrayRef::from(builder.build());
 
-        // Request Boolean with strict casting to force an error
+        // Request Float32 with strict casting to force an error
         let options = GetOptions {
             path: VariantPath::default(),
             as_type: Some(Arc::new(Field::new("result", DataType::Float32, 
true))),
@@ -2553,7 +2553,7 @@ mod test {
         builder.append_variant(Variant::BooleanFalse);
         let variant_array: ArrayRef = ArrayRef::from(builder.build());
 
-        // Request Boolean with strict casting to force an error
+        // Request Timestamp with strict casting to force an error
         let options = GetOptions {
             path: VariantPath::default(),
             as_type: Some(Arc::new(Field::new(
diff --git a/parquet-variant/Cargo.toml b/parquet-variant/Cargo.toml
index 51671d5189..7d5064331e 100644
--- a/parquet-variant/Cargo.toml
+++ b/parquet-variant/Cargo.toml
@@ -29,10 +29,12 @@ edition = { workspace = true }
 rust-version = { workspace = true }
 
 [dependencies]
+arrow = { workspace = true , features = ["canonical_extension_types"] }
 arrow-schema = { workspace = true }
 chrono = { workspace = true }
 half = { version = "2.1", default-features = false }
 indexmap = "2.10.0"
+num-traits = { version = "0.2", default-features = false }
 uuid = { version = "1.18.0", features = ["v4"]}
 
 simdutf8 = { workspace = true , optional = true }
diff --git a/parquet-variant/src/utils.rs b/parquet-variant/src/utils.rs
index 85d79ed8ae..61ae443d3f 100644
--- a/parquet-variant/src/utils.rs
+++ b/parquet-variant/src/utils.rs
@@ -146,10 +146,6 @@ pub(crate) const fn expect_size_of<T>(expected: usize) {
     }
 }
 
-pub(crate) fn fits_precision<const N: u32>(n: impl Into<i64>) -> bool {
-    n.into().unsigned_abs().leading_zeros() >= (i64::BITS - N)
-}
-
 /// Parse a path string into a vector of [`VariantPathElement`].
 ///
 /// # Syntax
@@ -289,16 +285,3 @@ fn parse_in_bracket(s: &str, i: usize) -> 
Result<(VariantPathElement<'_>, usize)
 
     Ok((element, end + 1))
 }
-
-#[cfg(test)]
-mod test {
-    use super::*;
-
-    #[test]
-    fn test_fits_precision() {
-        assert!(fits_precision::<10>(1023));
-        assert!(!fits_precision::<10>(1024));
-        assert!(fits_precision::<10>(-1023));
-        assert!(!fits_precision::<10>(-1024));
-    }
-}
diff --git a/parquet-variant/src/variant.rs b/parquet-variant/src/variant.rs
index 53fb3c4b1e..accff00904 100644
--- a/parquet-variant/src/variant.rs
+++ b/parquet-variant/src/variant.rs
@@ -28,11 +28,14 @@ use crate::decoder::{
     self, VariantBasicType, VariantPrimitiveType, get_basic_type, 
get_primitive_type,
 };
 use crate::path::{VariantPath, VariantPathElement};
-use crate::utils::{first_byte_from_slice, fits_precision, slice_from_slice};
-use std::ops::Deref;
-
+use crate::utils::{first_byte_from_slice, slice_from_slice};
+use arrow::compute::{
+    cast_num_to_bool, cast_single_string_to_boolean_default, num_cast, 
single_bool_to_numeric,
+};
 use arrow_schema::ArrowError;
 use chrono::{DateTime, NaiveDate, NaiveDateTime, NaiveTime, Timelike, Utc};
+use num_traits::NumCast;
+use std::ops::Deref;
 
 mod decimal;
 mod list;
@@ -151,6 +154,24 @@ impl Deref for ShortString<'_> {
 /// [specification]: 
https://github.com/apache/parquet-format/blob/master/VariantEncoding.md
 /// [Variant Shredding specification]: 
https://github.com/apache/parquet-format/blob/master/VariantShredding.md
 ///
+/// # Casting Semantics
+///
+/// Scalar conversion semantics intentionally follow Arrow cast behavior where 
applicable.
+/// Conversions in this module delegate to Arrow compute cast helpers such as
+/// [`num_cast`], [`cast_num_to_bool`], [`single_bool_to_numeric`], and
+/// [`cast_single_string_to_boolean_default`].
+///
+/// - [`Self::as_boolean`] accepts boolean, numeric, and string variants.
+///   Numeric zero maps to `false`; non-zero maps to `true`. String parsing 
follows
+///   Arrow UTF8-to-boolean cast rules.
+/// - Numeric accessors such as [`Self::as_int8`], [`Self::as_int64`], 
[`Self::as_u8`],
+///   [`Self::as_u64`], [`Self::as_f16`], [`Self::as_f32`], and 
[`Self::as_f64`] accept
+///   boolean and numeric variants (integers, floating-point, and decimals 
with scale `0`).
+///   They return `None` when conversion is not possible.
+/// - Decimal accessors such as [`Self::as_decimal4`], [`Self::as_decimal8`], 
and
+///   [`Self::as_decimal16`] accept compatible decimal variants and integer 
variants.
+///   They return `None` when conversion is not possible.
+///
 /// # Examples:
 ///
 /// ## Creating `Variant` from Rust Types
@@ -475,7 +496,7 @@ impl<'m, 'v> Variant<'m, 'v> {
 
     /// Converts this variant to a `bool` if possible.
     ///
-    /// Returns `Some(bool)` for boolean variants,
+    /// Returns `Some(bool)` for boolean, numeric and string variants,
     /// `None` for non-boolean variants.
     ///
     /// # Examples
@@ -491,14 +512,30 @@ impl<'m, 'v> Variant<'m, 'v> {
     /// let v2 = Variant::from(false);
     /// assert_eq!(v2.as_boolean(), Some(false));
     ///
+    /// // and a numeric variant
+    /// let v3 = Variant::from(3);
+    /// assert_eq!(v3.as_boolean(), Some(true));
+    ///
+    /// // and a string variant
+    /// let v4 = Variant::from("true");
+    /// assert_eq!(v4.as_boolean(), Some(true));
+    ///
     /// // but not from other variants
-    /// let v3 = Variant::from("hello!");
-    /// assert_eq!(v3.as_boolean(), None);
+    /// let v5 = Variant::from("hello!");
+    /// assert_eq!(v5.as_boolean(), None);
     /// ```
     pub fn as_boolean(&self) -> Option<bool> {
         match self {
             Variant::BooleanTrue => Some(true),
             Variant::BooleanFalse => Some(false),
+            Variant::Int8(i) => Some(cast_num_to_bool(*i)),
+            Variant::Int16(i) => Some(cast_num_to_bool(*i)),
+            Variant::Int32(i) => Some(cast_num_to_bool(*i)),
+            Variant::Int64(i) => Some(cast_num_to_bool(*i)),
+            Variant::Float(f) => Some(cast_num_to_bool(*f)),
+            Variant::Double(d) => Some(cast_num_to_bool(*d)),
+            Variant::ShortString(s) => 
cast_single_string_to_boolean_default(s.as_str()),
+            Variant::String(s) => cast_single_string_to_boolean_default(s),
             _ => None,
         }
     }
@@ -760,10 +797,36 @@ impl<'m, 'v> Variant<'m, 'v> {
         }
     }
 
+    /// Converts a boolean or numeric variant(integers, floating-point, and 
decimals with scale 0)
+    /// to the specified numeric type `T`.
+    ///
+    /// Uses Arrow's casting logic to perform the conversion. Returns 
`Some(T)` if
+    /// the conversion succeeds, `None` if the variant can't be casted to type 
`T`.
+    fn as_num<T>(&self) -> Option<T>
+    where
+        T: NumCast + Default,
+    {
+        match *self {
+            Variant::BooleanFalse => single_bool_to_numeric(false),
+            Variant::BooleanTrue => single_bool_to_numeric(true),
+            Variant::Int8(i) => num_cast(i),
+            Variant::Int16(i) => num_cast(i),
+            Variant::Int32(i) => num_cast(i),
+            Variant::Int64(i) => num_cast(i),
+            Variant::Float(f) => num_cast(f),
+            Variant::Double(d) => num_cast(d),
+            Variant::Decimal4(d) if d.scale() == 0 => num_cast(d.integer()),
+            Variant::Decimal8(d) if d.scale() == 0 => num_cast(d.integer()),
+            Variant::Decimal16(d) if d.scale() == 0 => num_cast(d.integer()),
+            _ => None,
+        }
+    }
+
     /// Converts this variant to an `i8` if possible.
     ///
-    /// Returns `Some(i8)` for integer variants that fit in `i8` range,
-    /// `None` for non-integer variants or values that would overflow.
+    /// Returns `Some(i8)` for boolean and numeric variants(integers, 
floating-point,
+    /// and decimals with scale 0) that fit in `i8` range,
+    /// `None` for other variants or values that would overflow.
     ///
     /// # Examples
     ///
@@ -774,31 +837,27 @@ impl<'m, 'v> Variant<'m, 'v> {
     /// let v1 = Variant::from(123i64);
     /// assert_eq!(v1.as_int8(), Some(123i8));
     ///
+    /// // or from boolean variant
+    /// let v2 = Variant::BooleanFalse;
+    /// assert_eq!(v2.as_int8(), Some(0));
+    ///
     /// // but not if it would overflow
-    /// let v2 = Variant::from(1234i64);
-    /// assert_eq!(v2.as_int8(), None);
+    /// let v3 = Variant::from(1234i64);
+    /// assert_eq!(v3.as_int8(), None);
     ///
     /// // or if the variant cannot be cast into an integer
-    /// let v3 = Variant::from("hello!");
-    /// assert_eq!(v3.as_int8(), None);
+    /// let v4 = Variant::from("hello!");
+    /// assert_eq!(v4.as_int8(), None);
     /// ```
     pub fn as_int8(&self) -> Option<i8> {
-        match *self {
-            Variant::Int8(i) => Some(i),
-            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,
-        }
+        self.as_num()
     }
 
     /// Converts this variant to an `i16` if possible.
     ///
-    /// Returns `Some(i16)` for integer variants that fit in `i16` range,
-    /// `None` for non-integer variants or values that would overflow.
+    /// Returns `Some(i16)` for boolean and numeric variants(integers, 
floating-point,
+    /// and decimals with scale 0) that fit in `i16` range
+    /// `None` for other variants or values that would overflow.
     ///
     /// # Examples
     ///
@@ -809,31 +868,27 @@ impl<'m, 'v> Variant<'m, 'v> {
     /// let v1 = Variant::from(123i64);
     /// assert_eq!(v1.as_int16(), Some(123i16));
     ///
+    /// // or from boolean variant
+    /// let v2 = Variant::BooleanFalse;
+    /// assert_eq!(v2.as_int16(), Some(0));
+    ///
     /// // but not if it would overflow
-    /// let v2 = Variant::from(123456i64);
-    /// assert_eq!(v2.as_int16(), None);
+    /// let v3 = Variant::from(123456i64);
+    /// assert_eq!(v3.as_int16(), None);
     ///
     /// // or if the variant cannot be cast into an integer
-    /// let v3 = Variant::from("hello!");
-    /// assert_eq!(v3.as_int16(), None);
+    /// let v4 = Variant::from("hello!");
+    /// assert_eq!(v4.as_int16(), None);
     /// ```
     pub fn as_int16(&self) -> Option<i16> {
-        match *self {
-            Variant::Int8(i) => Some(i.into()),
-            Variant::Int16(i) => Some(i),
-            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,
-        }
+        self.as_num()
     }
 
     /// Converts this variant to an `i32` if possible.
     ///
-    /// Returns `Some(i32)` for integer variants that fit in `i32` range,
-    /// `None` for non-integer variants or values that would overflow.
+    /// Returns `Some(i32)` for boolean and numeric variants(integers, 
floating-point,
+    /// and decimals with scale 0) that fit in `i32` range
+    /// `None` for other variants or values that would overflow.
     ///
     /// # Examples
     ///
@@ -844,31 +899,27 @@ impl<'m, 'v> Variant<'m, 'v> {
     /// let v1 = Variant::from(123i64);
     /// assert_eq!(v1.as_int32(), Some(123i32));
     ///
+    /// // or from boolean variant
+    /// let v2 = Variant::BooleanFalse;
+    /// assert_eq!(v2.as_int32(), Some(0));
+    ///
     /// // but not if it would overflow
-    /// let v2 = Variant::from(12345678901i64);
-    /// assert_eq!(v2.as_int32(), None);
+    /// let v3 = Variant::from(12345678901i64);
+    /// assert_eq!(v3.as_int32(), None);
     ///
     /// // or if the variant cannot be cast into an integer
-    /// let v3 = Variant::from("hello!");
-    /// assert_eq!(v3.as_int32(), None);
+    /// let v4 = Variant::from("hello!");
+    /// assert_eq!(v4.as_int32(), None);
     /// ```
     pub fn as_int32(&self) -> Option<i32> {
-        match *self {
-            Variant::Int8(i) => Some(i.into()),
-            Variant::Int16(i) => Some(i.into()),
-            Variant::Int32(i) => Some(i),
-            Variant::Int64(i) => i.try_into().ok(),
-            Variant::Decimal4(d) if d.scale() == 0 => Some(d.integer()),
-            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,
-        }
+        self.as_num()
     }
 
     /// Converts this variant to an `i64` if possible.
     ///
-    /// Returns `Some(i64)` for integer variants that fit in `i64` range,
-    /// `None` for non-integer variants or values that would overflow.
+    /// Returns `Some(i64)` for boolean and numeric variants(integers, 
floating-point,
+    /// and decimals with scale 0) that fit in `i64` range
+    /// `None` for other variants or values that would overflow.
     ///
     /// # Examples
     ///
@@ -879,43 +930,23 @@ impl<'m, 'v> Variant<'m, 'v> {
     /// let v1 = Variant::from(123i64);
     /// assert_eq!(v1.as_int64(), Some(123i64));
     ///
+    /// // or from boolean variant
+    /// let v2 = Variant::BooleanFalse;
+    /// assert_eq!(v2.as_int64(), Some(0));
+    ///
     /// // but not a variant that cannot be cast into an integer
-    /// let v2 = Variant::from("hello!");
-    /// assert_eq!(v2.as_int64(), None);
+    /// let v3 = Variant::from("hello!");
+    /// assert_eq!(v3.as_int64(), None);
     /// ```
     pub fn as_int64(&self) -> Option<i64> {
-        match *self {
-            Variant::Int8(i) => Some(i.into()),
-            Variant::Int16(i) => Some(i.into()),
-            Variant::Int32(i) => Some(i.into()),
-            Variant::Int64(i) => Some(i),
-            Variant::Decimal4(d) if d.scale() == 0 => Some(d.integer().into()),
-            Variant::Decimal8(d) if d.scale() == 0 => Some(d.integer()),
-            Variant::Decimal16(d) if d.scale() == 0 => 
d.integer().try_into().ok(),
-            _ => None,
-        }
-    }
-
-    fn generic_convert_unsigned_primitive<T>(&self) -> Option<T>
-    where
-        T: TryFrom<i8> + TryFrom<i16> + TryFrom<i32> + TryFrom<i64> + 
TryFrom<i128>,
-    {
-        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,
-        }
+        self.as_num()
     }
 
     /// 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.
+    /// Returns `Some(u8)` for boolean and numeric variants(integers, 
floating-point,
+    /// and decimals with scale 0) that fit in `u8` range
+    /// `None` for other variants or values that would overflow.
     ///
     /// # Examples
     ///
@@ -931,27 +962,32 @@ impl<'m, 'v> Variant<'m, 'v> {
     ///  let v2 = Variant::from(d);
     ///  assert_eq!(v2.as_u8(), Some(26u8));
     ///
+    /// // or from boolean variant
+    /// let v3 = Variant::BooleanFalse;
+    /// assert_eq!(v3.as_u8(), Some(0));
+    ///
     ///  // but not a variant that can't fit into the range
-    ///  let v3 = Variant::from(-1);
-    ///  assert_eq!(v3.as_u8(), None);
+    ///  let v4 = Variant::from(-1);
+    ///  assert_eq!(v4.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);
+    ///  let v5 = Variant::from(d);
+    ///  assert_eq!(v5.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);
+    ///  let v6 = Variant::from("hello!");
+    ///  assert_eq!(v6.as_u8(), None);
     /// ```
     pub fn as_u8(&self) -> Option<u8> {
-        self.generic_convert_unsigned_primitive::<u8>()
+        self.as_num()
     }
 
     /// 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.
+    /// Returns `Some(u16)` for boolean and numeric variants(integers, 
floating-point,
+    /// and decimals with scale 0) that fit in `u16` range
+    /// `None` for other variants or values that would overflow.
     ///
     /// # Examples
     ///
@@ -967,27 +1003,32 @@ impl<'m, 'v> Variant<'m, 'v> {
     ///  let v2 = Variant::from(d);
     ///  assert_eq!(v2.as_u16(), Some(u16::MAX));
     ///
+    /// // or from boolean variant
+    /// let v3= Variant::BooleanFalse;
+    /// assert_eq!(v3.as_u16(), Some(0));
+    ///
     ///  // but not a variant that can't fit into the range
-    ///  let v3 = Variant::from(-1);
-    ///  assert_eq!(v3.as_u16(), None);
+    ///  let v4 = Variant::from(-1);
+    ///  assert_eq!(v4.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);
+    ///  let v5 = Variant::from(d);
+    ///  assert_eq!(v5.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);
+    ///  let v6 = Variant::from("hello!");
+    ///  assert_eq!(v6.as_u16(), None);
     /// ```
     pub fn as_u16(&self) -> Option<u16> {
-        self.generic_convert_unsigned_primitive::<u16>()
+        self.as_num()
     }
 
     /// 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.
+    /// Returns `Some(u32)` for boolean and numeric variants(integers, 
floating-point,
+    /// and decimals with scale 0) that fit in `u32` range
+    /// `None` for other variants or values that would overflow.
     ///
     /// # Examples
     ///
@@ -1003,27 +1044,32 @@ impl<'m, 'v> Variant<'m, 'v> {
     ///  let v2 = Variant::from(d);
     ///  assert_eq!(v2.as_u32(), Some(u32::MAX));
     ///
+    /// // or from boolean variant
+    /// let v3 = Variant::BooleanFalse;
+    /// assert_eq!(v3.as_u32(), Some(0));
+    ///
     ///  // but not a variant that can't fit into the range
-    ///  let v3 = Variant::from(-1);
-    ///  assert_eq!(v3.as_u32(), None);
+    ///  let v4 = Variant::from(-1);
+    ///  assert_eq!(v4.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);
+    ///  let v5 = Variant::from(d);
+    ///  assert_eq!(v5.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);
+    ///  let v6 = Variant::from("hello!");
+    ///  assert_eq!(v6.as_u32(), None);
     /// ```
     pub fn as_u32(&self) -> Option<u32> {
-        self.generic_convert_unsigned_primitive::<u32>()
+        self.as_num()
     }
 
     /// 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.
+    /// Returns `Some(u64)` for boolean and numeric variants(integers, 
floating-point,
+    /// and decimals with scale 0) that fit in `u64` range
+    /// `None` for other variants or values that would overflow.
     ///
     /// # Examples
     ///
@@ -1039,21 +1085,25 @@ impl<'m, 'v> Variant<'m, 'v> {
     ///  let v2 = Variant::from(d);
     ///  assert_eq!(v2.as_u64(), Some(u64::MAX));
     ///
+    /// // or from boolean variant
+    /// let v3 = Variant::BooleanFalse;
+    /// assert_eq!(v3.as_u64(), Some(0));
+    ///
     ///  // but not a variant that can't fit into the range
-    ///  let v3 = Variant::from(-1);
-    ///  assert_eq!(v3.as_u64(), None);
+    ///  let v4 = Variant::from(-1);
+    ///  assert_eq!(v4.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);
+    ///  let v5 = Variant::from(d);
+    ///  assert_eq!(v5.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);
+    ///  let v6 = Variant::from("hello!");
+    ///  assert_eq!(v6.as_u64(), None);
     /// ```
     pub fn as_u64(&self) -> Option<u64> {
-        self.generic_convert_unsigned_primitive::<u64>()
+        self.as_num()
     }
 
     /// Converts this variant to tuple with a 4-byte unscaled value if 
possible.
@@ -1085,10 +1135,9 @@ impl<'m, 'v> Variant<'m, 'v> {
     /// ```
     pub fn as_decimal4(&self) -> Option<VariantDecimal4> {
         match *self {
-            Variant::Int8(i) => i32::from(i).try_into().ok(),
-            Variant::Int16(i) => i32::from(i).try_into().ok(),
-            Variant::Int32(i) => i.try_into().ok(),
-            Variant::Int64(i) => i32::try_from(i).ok()?.try_into().ok(),
+            Variant::Int8(_) | Variant::Int16(_) | Variant::Int32(_) | 
Variant::Int64(_) => {
+                self.as_num::<i32>().and_then(|x| x.try_into().ok())
+            }
             Variant::Decimal4(decimal4) => Some(decimal4),
             Variant::Decimal8(decimal8) => decimal8.try_into().ok(),
             Variant::Decimal16(decimal16) => decimal16.try_into().ok(),
@@ -1125,10 +1174,9 @@ impl<'m, 'v> Variant<'m, 'v> {
     /// ```
     pub fn as_decimal8(&self) -> Option<VariantDecimal8> {
         match *self {
-            Variant::Int8(i) => i64::from(i).try_into().ok(),
-            Variant::Int16(i) => i64::from(i).try_into().ok(),
-            Variant::Int32(i) => i64::from(i).try_into().ok(),
-            Variant::Int64(i) => i.try_into().ok(),
+            Variant::Int8(_) | Variant::Int16(_) | Variant::Int32(_) | 
Variant::Int64(_) => {
+                self.as_num::<i64>().and_then(|x| x.try_into().ok())
+            }
             Variant::Decimal4(decimal4) => Some(decimal4.into()),
             Variant::Decimal8(decimal8) => Some(decimal8),
             Variant::Decimal16(decimal16) => decimal16.try_into().ok(),
@@ -1157,10 +1205,9 @@ impl<'m, 'v> Variant<'m, 'v> {
     /// ```
     pub fn as_decimal16(&self) -> Option<VariantDecimal16> {
         match *self {
-            Variant::Int8(i) => i128::from(i).try_into().ok(),
-            Variant::Int16(i) => i128::from(i).try_into().ok(),
-            Variant::Int32(i) => i128::from(i).try_into().ok(),
-            Variant::Int64(i) => i128::from(i).try_into().ok(),
+            Variant::Int8(_) | Variant::Int16(_) | Variant::Int32(_) | 
Variant::Int64(_) => {
+                self.as_num::<i128>().and_then(|x| x.try_into().ok())
+            }
             Variant::Decimal4(decimal4) => Some(decimal4.into()),
             Variant::Decimal8(decimal8) => Some(decimal8.into()),
             Variant::Decimal16(decimal16) => Some(decimal16),
@@ -1170,8 +1217,9 @@ impl<'m, 'v> Variant<'m, 'v> {
 
     /// Converts this variant to an `f16` if possible.
     ///
-    /// Returns `Some(f16)` for floating point values, and integers with up to 
11 bits of
-    /// precision. `None` otherwise.
+    /// Returns `Some(f16)` for boolean and numeric variants(integers, 
floating-point,
+    /// and decimals with scale 0) that fit in `f16` range
+    /// `None` otherwise.
     ///
     /// # Example
     ///
@@ -1187,29 +1235,26 @@ impl<'m, 'v> Variant<'m, 'v> {
     /// let v2 = Variant::from(std::f64::consts::PI);
     /// assert_eq!(v2.as_f16(), Some(f16::from_f64(std::f64::consts::PI)));
     ///
-    /// // and from integers with no more than 11 bits of precision
-    /// let v3 = Variant::from(2047);
-    /// assert_eq!(v3.as_f16(), Some(f16::from_f32(2047.0)));
+    /// // and from boolean
+    /// let v3 = Variant::BooleanTrue;
+    /// assert_eq!(v3.as_f16(), Some(f16::from_f32(1.0)));
+    ///
+    /// // return inf if overflow
+    /// let v4 = Variant::from(123456);
+    /// assert_eq!(v4.as_f16(), Some(f16::INFINITY));
     ///
     /// // but not from other variants
-    /// let v4 = Variant::from("hello!");
-    /// assert_eq!(v4.as_f16(), None);
+    /// let v5 = Variant::from("hello!");
+    /// assert_eq!(v5.as_f16(), None);
     pub fn as_f16(&self) -> Option<f16> {
-        match *self {
-            Variant::Float(i) => Some(f16::from_f32(i)),
-            Variant::Double(i) => Some(f16::from_f64(i)),
-            Variant::Int8(i) => Some(i.into()),
-            Variant::Int16(i) if fits_precision::<11>(i) => 
Some(f16::from_f32(i as _)),
-            Variant::Int32(i) if fits_precision::<11>(i) => 
Some(f16::from_f32(i as _)),
-            Variant::Int64(i) if fits_precision::<11>(i) => 
Some(f16::from_f32(i as _)),
-            _ => None,
-        }
+        self.as_num()
     }
 
     /// Converts this variant to an `f32` if possible.
     ///
-    /// Returns `Some(f32)` for floating point values, and integer values with 
up to 24 bits of
-    /// precision.  `None` otherwise.
+    /// Returns `Some(f32)` for boolean and numeric variants(integers, 
floating-point,
+    /// and decimals with scale 0) that fit in `f32` range
+    /// `None` otherwise.
     ///
     /// # Examples
     ///
@@ -1224,31 +1269,27 @@ impl<'m, 'v> Variant<'m, 'v> {
     /// let v2 = Variant::from(std::f64::consts::PI);
     /// assert_eq!(v2.as_f32(), Some(std::f32::consts::PI));
     ///
-    /// // and from integers with no more than 24 bits of precision
-    /// let v3 = Variant::from(16777215i64);
-    /// assert_eq!(v3.as_f32(), Some(16777215.0));
+    /// // and from boolean variant
+    /// let v3 = Variant::BooleanTrue;
+    /// assert_eq!(v3.as_f32(), Some(1.0));
+    ///
+    /// // and return inf if overflow
+    /// let v4 = Variant::from(f64::MAX);
+    /// assert_eq!(v4.as_f32(), Some(f32::INFINITY));
     ///
     /// // but not from other variants
-    /// let v4 = Variant::from("hello!");
-    /// assert_eq!(v4.as_f32(), None);
+    /// let v5 = Variant::from("hello!");
+    /// assert_eq!(v5.as_f32(), None);
     /// ```
-    #[allow(clippy::cast_possible_truncation)]
     pub fn as_f32(&self) -> Option<f32> {
-        match *self {
-            Variant::Float(i) => Some(i),
-            Variant::Double(i) => Some(i as f32),
-            Variant::Int8(i) => Some(i.into()),
-            Variant::Int16(i) => Some(i.into()),
-            Variant::Int32(i) if fits_precision::<24>(i) => Some(i as _),
-            Variant::Int64(i) if fits_precision::<24>(i) => Some(i as _),
-            _ => None,
-        }
+        self.as_num()
     }
 
     /// Converts this variant to an `f64` if possible.
     ///
-    /// Returns `Some(f64)` for floating point values, and integer values with 
up to 53 bits of
-    /// precision.  `None` otherwise.
+    /// Returns `Some(f64)` for boolean and numeric variants(integers, 
floating-point,
+    /// and decimals with scale 0) that fit in `f64` range
+    /// `None` for other variants or can't be represented by an f64.
     ///
     /// # Examples
     ///
@@ -1263,24 +1304,16 @@ impl<'m, 'v> Variant<'m, 'v> {
     /// let v2 = Variant::from(std::f64::consts::PI);
     /// assert_eq!(v2.as_f64(), Some(std::f64::consts::PI));
     ///
-    /// // and from integers with no more than 53 bits of precision
-    /// let v3 = Variant::from(9007199254740991i64);
-    /// assert_eq!(v3.as_f64(), Some(9007199254740991.0));
+    /// // and from boolean variant
+    /// let v3 = Variant::BooleanTrue;
+    /// assert_eq!(v3.as_f64(), Some(1.0f64));
     ///
     /// // but not from other variants
-    /// let v4 = Variant::from("hello!");
-    /// assert_eq!(v4.as_f64(), None);
+    /// let v5 = Variant::from("hello!");
+    /// assert_eq!(v5.as_f64(), None);
     /// ```
     pub fn as_f64(&self) -> Option<f64> {
-        match *self {
-            Variant::Float(i) => Some(i.into()),
-            Variant::Double(i) => Some(i),
-            Variant::Int8(i) => Some(i.into()),
-            Variant::Int16(i) => Some(i.into()),
-            Variant::Int32(i) => Some(i.into()),
-            Variant::Int64(i) if fits_precision::<53>(i) => Some(i as _),
-            _ => None,
-        }
+        self.as_num()
     }
 
     /// Converts this variant to an `Object` if it is an [`VariantObject`].
@@ -1527,7 +1560,7 @@ impl From<u8> for Variant<'_, '_> {
         if let Ok(value) = i8::try_from(value) {
             Variant::Int8(value)
         } else {
-            Variant::Int16(i16::from(value))
+            Variant::Int16(num_cast(value).unwrap()) // u8 -> i16 is infallible
         }
     }
 }
@@ -1538,7 +1571,7 @@ impl From<u16> for Variant<'_, '_> {
         if let Ok(value) = i16::try_from(value) {
             Variant::Int16(value)
         } else {
-            Variant::Int32(i32::from(value))
+            Variant::Int32(num_cast(value).unwrap()) // u16 -> i32 is 
infallible
         }
     }
 }
@@ -1548,7 +1581,7 @@ impl From<u32> for Variant<'_, '_> {
         if let Ok(value) = i32::try_from(value) {
             Variant::Int32(value)
         } else {
-            Variant::Int64(i64::from(value))
+            Variant::Int64(num_cast(value).unwrap()) // u32 -> i64 is 
infallible
         }
     }
 }
@@ -1560,7 +1593,7 @@ impl From<u64> for Variant<'_, '_> {
             Variant::Int64(value)
         } else {
             // u64 max is 18446744073709551615, which fits in i128
-            Variant::Decimal16(VariantDecimal16::try_new(i128::from(value), 
0).unwrap())
+            
Variant::Decimal16(VariantDecimal16::try_new(num_cast(value).unwrap(), 
0).unwrap())
         }
     }
 }


Reply via email to