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 Int8(i8),
60 Int16(i16),
62 Int32(i32),
64 Int64(i64),
66 Int128(i128),
68 UInt8(u8),
70 UInt16(u16),
72 UInt32(u32),
74 UInt64(u64),
76 UInt128(u128),
78 Float32(f32),
80 Float64(f64),
82 List(Series),
84 Boolean(bool),
86 String(PlSmallStr),
88 Binary(Vec<u8>),
89
90 #[cfg(feature = "dtype-date")]
93 Date(i32),
94
95 #[cfg(feature = "dtype-datetime")]
98 Datetime(
99 i64,
100 crate::prelude::TimeUnit,
101 Option<crate::prelude::TimeZone>,
102 ),
103
104 #[cfg(feature = "dtype-duration")]
106 Duration(i64, crate::prelude::TimeUnit),
107
108 #[cfg(feature = "dtype-time")]
110 Time(i64),
111
112 #[cfg(feature = "dtype-array")]
113 Array(Series, usize),
114
115 #[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}