polars_core/scalar/
serde.rs

1use arrow::array::IntoBoxedArray;
2use polars_error::{PolarsError, PolarsResult, polars_bail};
3use polars_utils::pl_str::PlSmallStr;
4#[cfg(feature = "serde")]
5use serde::{Deserialize, Deserializer, Serialize, Serializer};
6
7use super::Scalar;
8use crate::prelude::{AnyValue, DataType, Field};
9use crate::series::Series;
10
11#[cfg(feature = "dsl-schema")]
12impl schemars::JsonSchema for Scalar {
13    fn is_referenceable() -> bool {
14        <SerializableScalar as schemars::JsonSchema>::is_referenceable()
15    }
16
17    fn schema_id() -> std::borrow::Cow<'static, str> {
18        <SerializableScalar as schemars::JsonSchema>::schema_id()
19    }
20
21    fn schema_name() -> String {
22        <SerializableScalar as schemars::JsonSchema>::schema_name()
23    }
24
25    fn json_schema(generator: &mut schemars::r#gen::SchemaGenerator) -> schemars::schema::Schema {
26        <SerializableScalar as schemars::JsonSchema>::json_schema(generator)
27    }
28}
29
30#[cfg(feature = "serde")]
31impl Serialize for Scalar {
32    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
33    where
34        S: Serializer,
35    {
36        SerializableScalar::try_from(self.clone())
37            .map_err(serde::ser::Error::custom)?
38            .serialize(serializer)
39    }
40}
41
42#[cfg(feature = "serde")]
43impl<'a> Deserialize<'a> for Scalar {
44    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
45    where
46        D: Deserializer<'a>,
47    {
48        SerializableScalar::deserialize(deserializer)
49            .and_then(|v| Self::try_from(v).map_err(serde::de::Error::custom))
50    }
51}
52
53#[derive(Serialize, Deserialize)]
54#[serde(rename = "AnyValue")]
55#[cfg_attr(feature = "dsl-schema", derive(schemars::JsonSchema))]
56pub enum SerializableScalar {
57    Null(DataType),
58    /// An 8-bit integer number.
59    Int8(i8),
60    /// A 16-bit integer number.
61    Int16(i16),
62    /// A 32-bit integer number.
63    Int32(i32),
64    /// A 64-bit integer number.
65    Int64(i64),
66    /// A 128-bit integer number.
67    Int128(i128),
68    /// An unsigned 8-bit integer number.
69    UInt8(u8),
70    /// An unsigned 16-bit integer number.
71    UInt16(u16),
72    /// An unsigned 32-bit integer number.
73    UInt32(u32),
74    /// An unsigned 64-bit integer number.
75    UInt64(u64),
76    /// An unsigned 128-bit integer number.
77    UInt128(u128),
78    /// A 32-bit floating point number.
79    Float32(f32),
80    /// A 64-bit floating point number.
81    Float64(f64),
82    /// Nested type, contains arrays that are filled with one of the datatypes.
83    List(Series),
84    /// A binary true or false.
85    Boolean(bool),
86    /// A UTF8 encoded string type.
87    String(PlSmallStr),
88    Binary(Vec<u8>),
89
90    /// A 32-bit date representing the elapsed time since UNIX epoch (1970-01-01)
91    /// in days (32 bits).
92    #[cfg(feature = "dtype-date")]
93    Date(i32),
94
95    /// A 64-bit date representing the elapsed time since UNIX epoch (1970-01-01)
96    /// in nanoseconds (64 bits).
97    #[cfg(feature = "dtype-datetime")]
98    Datetime(
99        i64,
100        crate::prelude::TimeUnit,
101        Option<crate::prelude::TimeZone>,
102    ),
103
104    /// A 64-bit integer representing difference between date-times in [`TimeUnit`]
105    #[cfg(feature = "dtype-duration")]
106    Duration(i64, crate::prelude::TimeUnit),
107
108    /// A 64-bit time representing the elapsed time since midnight in nanoseconds
109    #[cfg(feature = "dtype-time")]
110    Time(i64),
111
112    #[cfg(feature = "dtype-array")]
113    Array(Series, usize),
114
115    /// A 128-bit fixed point decimal number with a scale.
116    #[cfg(feature = "dtype-decimal")]
117    Decimal(i128, usize, usize),
118
119    #[cfg(feature = "dtype-categorical")]
120    Categorical {
121        value: PlSmallStr,
122        name: PlSmallStr,
123        namespace: PlSmallStr,
124        physical: polars_dtype::categorical::CategoricalPhysical,
125    },
126    #[cfg(feature = "dtype-categorical")]
127    Enum {
128        value: polars_dtype::categorical::CatSize,
129        categories: Series,
130    },
131
132    #[cfg(feature = "dtype-struct")]
133    Struct(Vec<(PlSmallStr, SerializableScalar)>),
134}
135
136impl TryFrom<Scalar> for SerializableScalar {
137    type Error = PolarsError;
138
139    fn try_from(value: Scalar) -> Result<Self, Self::Error> {
140        let out = match value.value {
141            AnyValue::Null => Self::Null(value.dtype),
142            AnyValue::Int8(v) => Self::Int8(v),
143            AnyValue::Int16(v) => Self::Int16(v),
144            AnyValue::Int32(v) => Self::Int32(v),
145            AnyValue::Int64(v) => Self::Int64(v),
146            AnyValue::Int128(v) => Self::Int128(v),
147            AnyValue::UInt8(v) => Self::UInt8(v),
148            AnyValue::UInt16(v) => Self::UInt16(v),
149            AnyValue::UInt32(v) => Self::UInt32(v),
150            AnyValue::UInt64(v) => Self::UInt64(v),
151            AnyValue::UInt128(v) => Self::UInt128(v),
152            AnyValue::Float32(v) => Self::Float32(v),
153            AnyValue::Float64(v) => Self::Float64(v),
154            AnyValue::List(series) => Self::List(series),
155            AnyValue::Boolean(v) => Self::Boolean(v),
156            AnyValue::String(v) => Self::String(PlSmallStr::from(v)),
157            AnyValue::StringOwned(v) => Self::String(v),
158            AnyValue::Binary(v) => Self::Binary(v.to_vec()),
159            AnyValue::BinaryOwned(v) => Self::Binary(v),
160
161            #[cfg(feature = "dtype-date")]
162            AnyValue::Date(v) => Self::Date(v),
163
164            #[cfg(feature = "dtype-datetime")]
165            AnyValue::Datetime(v, tu, tz) => Self::Datetime(v, tu, tz.cloned()),
166            #[cfg(feature = "dtype-datetime")]
167            AnyValue::DatetimeOwned(v, time_unit, time_zone) => {
168                Self::Datetime(v, time_unit, time_zone.as_deref().cloned())
169            },
170
171            #[cfg(feature = "dtype-duration")]
172            AnyValue::Duration(v, time_unit) => Self::Duration(v, time_unit),
173
174            #[cfg(feature = "dtype-time")]
175            AnyValue::Time(v) => Self::Time(v),
176
177            #[cfg(feature = "dtype-categorical")]
178            AnyValue::Categorical(cat, _) | AnyValue::CategoricalOwned(cat, _) => {
179                let DataType::Categorical(categories, mapping) = value.dtype() else {
180                    unreachable!();
181                };
182
183                Self::Categorical {
184                    value: PlSmallStr::from(mapping.cat_to_str(cat).unwrap()),
185                    name: categories.name().clone(),
186                    namespace: categories.namespace().clone(),
187                    physical: categories.physical(),
188                }
189            },
190            #[cfg(feature = "dtype-categorical")]
191            AnyValue::Enum(idx, _) | AnyValue::EnumOwned(idx, _) => {
192                let DataType::Enum(categories, _) = value.dtype() else {
193                    unreachable!();
194                };
195
196                Self::Enum {
197                    value: idx,
198                    categories: Series::from_arrow(
199                        PlSmallStr::EMPTY,
200                        categories.categories().clone().into_boxed(),
201                    )
202                    .unwrap(),
203                }
204            },
205
206            #[cfg(feature = "dtype-array")]
207            AnyValue::Array(v, width) => Self::Array(v, width),
208
209            #[cfg(feature = "object")]
210            AnyValue::Object(..) | AnyValue::ObjectOwned(..) => {
211                polars_bail!(nyi = "Cannot serialize object value.")
212            },
213
214            #[cfg(feature = "dtype-struct")]
215            AnyValue::Struct(idx, arr, fields) => {
216                assert!(idx < arr.len());
217                assert_eq!(arr.values().len(), fields.len());
218
219                Self::Struct(
220                    arr.values()
221                        .iter()
222                        .zip(fields.iter())
223                        .map(|(arr, field)| {
224                            let series = unsafe {
225                                Series::from_chunks_and_dtype_unchecked(
226                                    PlSmallStr::EMPTY,
227                                    vec![arr.clone()],
228                                    field.dtype(),
229                                )
230                            };
231                            let av = unsafe { series.get_unchecked(idx) };
232                            PolarsResult::Ok((
233                                field.name().clone(),
234                                Self::try_from(Scalar::new(field.dtype.clone(), av.into_static()))?,
235                            ))
236                        })
237                        .collect::<Result<Vec<_>, _>>()?,
238                )
239            },
240
241            #[cfg(feature = "dtype-struct")]
242            AnyValue::StructOwned(v) => {
243                let (avs, fields) = *v;
244                assert_eq!(avs.len(), fields.len());
245
246                Self::Struct(
247                    avs.into_iter()
248                        .zip(fields.into_iter())
249                        .map(|(av, field)| {
250                            PolarsResult::Ok((
251                                field.name,
252                                Self::try_from(Scalar::new(field.dtype, av.into_static()))?,
253                            ))
254                        })
255                        .collect::<Result<Vec<_>, _>>()?,
256                )
257            },
258
259            #[cfg(feature = "dtype-decimal")]
260            AnyValue::Decimal(v, prec, scale) => Self::Decimal(v, prec, scale),
261        };
262        Ok(out)
263    }
264}
265
266impl TryFrom<SerializableScalar> for Scalar {
267    type Error = PolarsError;
268
269    fn try_from(value: SerializableScalar) -> Result<Self, Self::Error> {
270        type S = SerializableScalar;
271        Ok(match value {
272            S::Null(dtype) => Self::null(dtype),
273            S::Int8(v) => Self::from(v),
274            S::Int16(v) => Self::from(v),
275            S::Int32(v) => Self::from(v),
276            S::Int64(v) => Self::from(v),
277            S::Int128(v) => Self::from(v),
278            S::UInt8(v) => Self::from(v),
279            S::UInt16(v) => Self::from(v),
280            S::UInt32(v) => Self::from(v),
281            S::UInt64(v) => Self::from(v),
282            S::UInt128(v) => Self::from(v),
283            S::Float32(v) => Self::from(v),
284            S::Float64(v) => Self::from(v),
285            S::List(v) => Self::new_list(v),
286            S::Boolean(v) => Self::from(v),
287            S::String(v) => Self::from(v),
288            S::Binary(v) => Self::from(v),
289            #[cfg(feature = "dtype-date")]
290            S::Date(v) => Self::new_date(v),
291            #[cfg(feature = "dtype-datetime")]
292            S::Datetime(v, time_unit, time_zone) => Self::new_datetime(v, time_unit, time_zone),
293            #[cfg(feature = "dtype-duration")]
294            S::Duration(v, time_unit) => Self::new_duration(v, time_unit),
295            #[cfg(feature = "dtype-time")]
296            S::Time(v) => Self::new_time(v),
297            #[cfg(feature = "dtype-array")]
298            S::Array(v, width) => Self::new_array(v, width),
299            #[cfg(feature = "dtype-decimal")]
300            S::Decimal(v, prec, scale) => Self::new_decimal(v, prec, scale),
301
302            #[cfg(feature = "dtype-categorical")]
303            S::Categorical {
304                value,
305                name,
306                namespace,
307                physical,
308            } => Self::new_categorical(value.as_str(), name, namespace, physical)?,
309            #[cfg(feature = "dtype-categorical")]
310            S::Enum { value, categories } => {
311                Self::new_enum(value, categories.str()?.rechunk().downcast_as_array())?
312            },
313            #[cfg(feature = "dtype-struct")]
314            S::Struct(scs) => {
315                let (avs, fields) = scs
316                    .into_iter()
317                    .map(|(name, scalar)| {
318                        let Scalar { dtype, value } = Scalar::try_from(scalar)?;
319                        Ok((value, Field::new(name, dtype)))
320                    })
321                    .collect::<PolarsResult<(Vec<AnyValue<'static>>, Vec<Field>)>>()?;
322
323                let dtype = DataType::Struct(fields.clone());
324                Self::new(dtype, AnyValue::StructOwned(Box::new((avs, fields))))
325            },
326        })
327    }
328}