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 Int8(i8),
61 Int16(i16),
63 Int32(i32),
65 Int64(i64),
67 Int128(i128),
69 UInt8(u8),
71 UInt16(u16),
73 UInt32(u32),
75 UInt64(u64),
77 UInt128(u128),
79 Float16(pf16),
81 Float32(f32),
83 Float64(f64),
85 List(Series),
87 #[cfg(feature = "dtype-map")]
88 Map(Series),
89 Boolean(bool),
91 String(PlSmallStr),
93 Binary(Vec<u8>),
94
95 #[cfg(feature = "dtype-date")]
98 Date(i32),
99
100 #[cfg(feature = "dtype-datetime")]
103 Datetime(
104 i64,
105 crate::prelude::TimeUnit,
106 Option<crate::prelude::TimeZone>,
107 ),
108
109 #[cfg(feature = "dtype-duration")]
111 Duration(i64, crate::prelude::TimeUnit),
112
113 #[cfg(feature = "dtype-time")]
115 Time(i64),
116
117 #[cfg(feature = "dtype-array")]
118 Array(Series, usize),
119
120 #[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}