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_dtype::DType;
11use vortex_dtype::DecimalDType;
12use vortex_dtype::NativeDType;
13use vortex_dtype::Nullability;
14use vortex_error::VortexExpect;
15
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                Self::try_new(
39                    DType::$variant(Nullability::NonNullable),
40                    Some(ScalarValue::from(value)),
41                )
42                .vortex_expect("unable to construct a `Scalar`")
43            }
44        }
45
46        impl From<Option<$ty>> for Scalar {
47            fn from(value: Option<$ty>) -> Self {
48                Self::try_new(
49                    DType::$variant(Nullability::Nullable),
50                    value.map(ScalarValue::from),
51                )
52                .vortex_expect("unable to construct a `Scalar`")
53            }
54        }
55    };
56
57    // Identity conversion (no transformation needed).
58    ($ty:ty, $variant:ident) => {
59        impl_into_scalar!($ty, $variant, |value| value);
60    };
61}
62
63// Boolean scalar values can only be made from `bool`.
64impl_into_scalar!(bool, Bool);
65
66// Binary scalar values can be made from both `&[u8]` and `ByteBuffer`.
67impl_into_scalar!(&[u8], Binary, |value| ByteBuffer::from(value.to_vec()));
68impl_into_scalar!(ByteBuffer, Binary);
69
70// UTF-8 scalar values can be made from `&str`, `String`, and `BufferString`.
71impl_into_scalar!(&str, Utf8, |value| value.to_string().into());
72impl_into_scalar!(String, Utf8, |value| value.into());
73impl_into_scalar!(BufferString, Utf8);
74
75////////////////////////////////////////////////////////////////////////////////////////////////////
76// List (`Vec`) conversion into `Scalar`.
77////////////////////////////////////////////////////////////////////////////////////////////////////
78
79impl<T> From<Vec<T>> for ScalarValue
80where
81    T: NativeDType,
82    Scalar: From<T>,
83{
84    fn from(vec: Vec<T>) -> Self {
85        ScalarValue::List(
86            vec.into_iter()
87                .map(|elem| Scalar::from(elem).into_value())
88                .collect(),
89        )
90    }
91}
92
93impl<T> From<Vec<T>> for Scalar
94where
95    T: NativeDType,
96    Scalar: From<T>,
97{
98    fn from(vec: Vec<T>) -> Self {
99        Scalar::try_new(
100            DType::List(Arc::from(T::dtype()), Nullability::NonNullable),
101            Some(ScalarValue::from(vec)),
102        )
103        .vortex_expect("unable to construct a list `Scalar` from `Vec<T>`")
104    }
105}
106
107impl<T> From<Option<Vec<T>>> for Scalar
108where
109    T: NativeDType,
110    Scalar: From<T>,
111{
112    fn from(vec: Option<Vec<T>>) -> Self {
113        Scalar::try_new(
114            DType::List(Arc::from(T::dtype()), Nullability::Nullable),
115            vec.map(ScalarValue::from),
116        )
117        .vortex_expect("unable to construct a list `Scalar` from `Option<Vec<T>>`")
118    }
119}
120
121////////////////////////////////////////////////////////////////////////////////////////////////////
122// Decimal conversion into `Scalar`.
123////////////////////////////////////////////////////////////////////////////////////////////////////
124
125impl From<DecimalValue> for ScalarValue {
126    fn from(value: DecimalValue) -> Self {
127        Self::Decimal(value)
128    }
129}
130
131impl From<DecimalValue> for Scalar {
132    fn from(value: DecimalValue) -> Self {
133        let dtype = value.decimal_dtype();
134        Self::try_new(
135            DType::Decimal(dtype, Nullability::NonNullable),
136            Some(ScalarValue::Decimal(value)),
137        )
138        .vortex_expect("unable to construct a decimal `Scalar` from `DecimalValue`")
139    }
140}
141
142impl From<Option<DecimalValue>> for Scalar {
143    fn from(value: Option<DecimalValue>) -> Self {
144        let Some(value) = value else {
145            // TODO(connor): This is definitely a footgun!
146            // We have no way of knowing what the decimal value precision is (since we have no
147            // data), so just choose a small one.
148            return Self::null(DType::Decimal(
149                DecimalDType::new(3, 0),
150                Nullability::Nullable,
151            ));
152        };
153
154        let dtype = value.decimal_dtype();
155        Self::try_new(
156            DType::Decimal(dtype, Nullability::Nullable),
157            Some(ScalarValue::Decimal(value)),
158        )
159        .vortex_expect("unable to construct a decimal `Scalar` from `Option<DecimalValue>`")
160    }
161}