Skip to main content

vortex_array/scalar/convert/
into_scalar.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright the Vortex contributors
3
4//! Conversions from an assortment of types into scalars.
5
6use std::sync::Arc;
7
8use vortex_buffer::BufferString;
9use vortex_buffer::ByteBuffer;
10use vortex_error::VortexExpect;
11
12use crate::dtype::DType;
13use crate::dtype::DecimalDType;
14use crate::dtype::NativeDType;
15use crate::dtype::Nullability;
16use crate::scalar::DecimalValue;
17use crate::scalar::Scalar;
18use crate::scalar::ScalarValue;
19
20// NB: Unfortunately, we cannot do a blanket implementation of `From<Option<T>> for Scalar` because
21// we have no easy way of getting a `DType` from just a `T`. Maybe in the future it would be nice if
22// we had that as a private trait impl...
23
24/// Generates the three impl blocks for converting a type into [`ScalarValue`] and [`Scalar`]:
25/// - `From<$ty> for ScalarValue`
26/// - `From<$ty> for Scalar` (non-nullable)
27/// - `From<Option<$ty>> for Scalar` (nullable)
28macro_rules! impl_into_scalar {
29    ($ty:ty, $variant:ident, | $value:ident | $convert:expr) => {
30        impl From<$ty> for ScalarValue {
31            fn from($value: $ty) -> Self {
32                ScalarValue::$variant($convert)
33            }
34        }
35
36        impl From<$ty> for Scalar {
37            fn from(value: $ty) -> Self {
38                // SAFETY: The value variant matches the dtype variant.
39                unsafe {
40                    Self::new_unchecked(
41                        DType::$variant(Nullability::NonNullable),
42                        Some(ScalarValue::from(value)),
43                    )
44                }
45            }
46        }
47
48        impl From<Option<$ty>> for Scalar {
49            fn from(value: Option<$ty>) -> Self {
50                // SAFETY: The value variant matches the dtype variant, and the dtype is nullable.
51                unsafe {
52                    Self::new_unchecked(
53                        DType::$variant(Nullability::Nullable),
54                        value.map(ScalarValue::from),
55                    )
56                }
57            }
58        }
59    };
60
61    // Identity conversion (no transformation needed).
62    ($ty:ty, $variant:ident) => {
63        impl_into_scalar!($ty, $variant, |value| value);
64    };
65}
66
67// Boolean scalar values can only be made from `bool`.
68impl_into_scalar!(bool, Bool);
69
70// Binary scalar values can be made from both `&[u8]` and `ByteBuffer`.
71impl_into_scalar!(&[u8], Binary, |value| ByteBuffer::from(value.to_vec()));
72impl_into_scalar!(ByteBuffer, Binary);
73
74// UTF-8 scalar values can be made from `&str`, `String`, and `BufferString`.
75impl_into_scalar!(&str, Utf8, |value| value.to_string().into());
76impl_into_scalar!(String, Utf8, |value| value.into());
77impl_into_scalar!(BufferString, Utf8);
78
79////////////////////////////////////////////////////////////////////////////////////////////////////
80// List (`Vec`) conversion into `Scalar`.
81////////////////////////////////////////////////////////////////////////////////////////////////////
82
83impl<T> From<Vec<T>> for ScalarValue
84where
85    T: NativeDType,
86    Scalar: From<T>,
87{
88    fn from(vec: Vec<T>) -> Self {
89        ScalarValue::Tuple(
90            vec.into_iter()
91                .map(|elem| Scalar::from(elem).into_value())
92                .collect(),
93        )
94    }
95}
96
97impl<T> From<Vec<T>> for Scalar
98where
99    T: NativeDType,
100    Scalar: From<T>,
101{
102    fn from(vec: Vec<T>) -> Self {
103        Scalar::try_new(
104            DType::List(Arc::from(T::dtype()), Nullability::NonNullable),
105            Some(ScalarValue::from(vec)),
106        )
107        .vortex_expect("unable to construct a list `Scalar` from `Vec<T>`")
108    }
109}
110
111impl<T> From<Option<Vec<T>>> for Scalar
112where
113    T: NativeDType,
114    Scalar: From<T>,
115{
116    fn from(vec: Option<Vec<T>>) -> Self {
117        Scalar::try_new(
118            DType::List(Arc::from(T::dtype()), Nullability::Nullable),
119            vec.map(ScalarValue::from),
120        )
121        .vortex_expect("unable to construct a list `Scalar` from `Option<Vec<T>>`")
122    }
123}
124
125////////////////////////////////////////////////////////////////////////////////////////////////////
126// Decimal conversion into `Scalar`.
127////////////////////////////////////////////////////////////////////////////////////////////////////
128
129impl From<DecimalValue> for ScalarValue {
130    fn from(value: DecimalValue) -> Self {
131        Self::Decimal(value)
132    }
133}
134
135impl From<DecimalValue> for Scalar {
136    fn from(value: DecimalValue) -> Self {
137        let dtype = value.decimal_dtype();
138        Self::try_new(
139            DType::Decimal(dtype, Nullability::NonNullable),
140            Some(ScalarValue::Decimal(value)),
141        )
142        .vortex_expect("unable to construct a decimal `Scalar` from `DecimalValue`")
143    }
144}
145
146impl From<Option<DecimalValue>> for Scalar {
147    fn from(value: Option<DecimalValue>) -> Self {
148        let Some(value) = value else {
149            // TODO(connor): This is definitely a footgun!
150            // We have no way of knowing what the decimal value precision is (since we have no
151            // data), so just choose a small one.
152            return Self::null(DType::Decimal(
153                DecimalDType::new(3, 0),
154                Nullability::Nullable,
155            ));
156        };
157
158        let dtype = value.decimal_dtype();
159        Self::try_new(
160            DType::Decimal(dtype, Nullability::Nullable),
161            Some(ScalarValue::Decimal(value)),
162        )
163        .vortex_expect("unable to construct a decimal `Scalar` from `Option<DecimalValue>`")
164    }
165}