Skip to main content

polars_core/scalar/
serde.rs

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