tank_core/
as_value.rs

1use crate::{Error, FixedDecimal, Interval, Passive, Result, Value};
2use anyhow::Context;
3use quote::ToTokens;
4use rust_decimal::{Decimal, prelude::FromPrimitive};
5use std::{
6    any, array,
7    borrow::Cow,
8    cell::{Cell, RefCell},
9    collections::{BTreeMap, HashMap, LinkedList, VecDeque},
10    hash::Hash,
11    rc::Rc,
12    sync::{Arc, RwLock},
13    vec,
14};
15use time::macros::format_description;
16
17pub trait AsValue {
18    fn as_empty_value() -> Value;
19    fn as_value(self) -> Value;
20    fn try_from_value(value: Value) -> Result<Self>
21    where
22        Self: Sized;
23}
24
25impl<T: AsValue> From<T> for Value {
26    fn from(value: T) -> Self {
27        value.as_value()
28    }
29}
30
31impl From<&'static str> for Value {
32    fn from(value: &'static str) -> Self {
33        Value::Varchar(Some(value.into()))
34    }
35}
36
37macro_rules! impl_as_value {
38    ($source:ty, $value:path => $expr:expr $(, $pat_rest:path => $expr_rest:expr)* $(,)?) => {
39        impl AsValue for $source {
40            fn as_empty_value() -> Value {
41                $value(None)
42            }
43            fn as_value(self) -> Value {
44                $value(Some(self.into()))
45            }
46            fn try_from_value(value: Value) -> Result<Self> {
47                match value {
48                    $value(Some(v), ..) => $expr(v),
49                    #[allow(unreachable_patterns)]
50                    Value::Int64(Some(v), ..) => if v as $source as i64 == v {
51                        Ok(v as $source)
52                    } else {
53                        Err(Error::msg(format!(
54                            "Cannot convert `{:?}` into {}, the value is out of bounds",
55                            v,
56                            stringify!($source),
57                        )))
58                    }
59                    #[allow(unreachable_patterns)]
60                    $($pat_rest(Some(v), ..) => $expr_rest(v),)*
61                    _ => Err(Error::msg(format!(
62                        "Cannot convert `{:?}` into `{}`",
63                        value,
64                        stringify!($source),
65                    ))),
66                }
67            }
68        }
69    };
70}
71impl_as_value!(
72    i8,
73    Value::Int8 => |v| Ok(v),
74    Value::UInt8 => |v| Ok(v as i8),
75);
76impl_as_value!(
77    i16,
78    Value::Int16 => |v| Ok(v),
79    Value::Int8 => |v| Ok(v as i16),
80    Value::UInt16 => |v| Ok(v as i16),
81    Value::UInt8 => |v| Ok(v as i16),
82);
83impl_as_value!(
84    i32,
85    Value::Int32 => |v| Ok(v),
86    Value::Int16 => |v| Ok(v as i32),
87    Value::Int8 => |v| Ok(v as i32),
88    Value::UInt32 => |v| Ok(v as i32),
89    Value::UInt16 => |v| Ok(v as i32),
90    Value::UInt8 => |v| Ok(v as i32),
91);
92impl_as_value!(
93    i64,
94    Value::Int64 => |v| Ok(v),
95    Value::Int32 => |v| Ok(v as i64),
96    Value::Int16 => |v| Ok(v as i64),
97    Value::Int8 => |v| Ok(v as i64),
98    Value::UInt64 => |v| Ok(v as i64),
99    Value::UInt32 => |v| Ok(v as i64),
100    Value::UInt16 => |v| Ok(v as i64),
101    Value::UInt8 => |v| Ok(v as i64),
102);
103impl_as_value!(
104    i128,
105    Value::Int128 => |v| Ok(v),
106    Value::Int64 => |v| Ok(v as i128),
107    Value::Int32 => |v| Ok(v as i128),
108    Value::Int16 => |v| Ok(v as i128),
109    Value::Int8 => |v| Ok(v as i128),
110    Value::UInt128 => |v| Ok(v as i128),
111    Value::UInt64 => |v| Ok(v as i128),
112    Value::UInt32 => |v| Ok(v as i128),
113    Value::UInt16 => |v| Ok(v as i128),
114    Value::UInt8 => |v| Ok(v as i128),
115);
116impl_as_value!(u8, Value::UInt8 => |v| Ok(v));
117impl_as_value!(
118    u16,
119    Value::UInt16 => |v| Ok(v),
120    Value::UInt8 => |v| Ok(v as u16),
121);
122impl_as_value!(
123    u32,
124    Value::UInt32 => |v| Ok(v),
125    Value::UInt16 => |v| Ok(v as u32),
126    Value::UInt8 => |v| Ok(v as u32),
127);
128impl_as_value!(
129    u64,
130    Value::UInt64 => |v| Ok(v),
131    Value::UInt32 => |v| Ok(v as u64),
132    Value::UInt16 => |v| Ok(v as u64),
133    Value::UInt8 => |v| Ok(v as u64),
134);
135impl_as_value!(
136    u128,
137    Value::UInt128 => |v| Ok(v),
138    Value::UInt64 => |v| Ok(v as u128),
139    Value::UInt32 => |v| Ok(v as u128),
140    Value::UInt16 => |v| Ok(v as u128),
141    Value::UInt8 => |v| Ok(v as u128),
142);
143
144macro_rules! impl_as_value {
145    ($source:ty, $value:path => $expr:expr $(, $pat_rest:path => $expr_rest:expr)* $(,)?) => {
146        impl AsValue for $source {
147            fn as_empty_value() -> Value {
148                $value(None)
149            }
150            fn as_value(self) -> Value {
151                $value(Some(self.into()))
152            }
153            fn try_from_value(value: Value) -> Result<Self> {
154                match value {
155                    $value(Some(v), ..) => $expr(v),
156                    $($pat_rest(Some(v), ..) => $expr_rest(v),)*
157                    _ => Err(Error::msg(format!(
158                        "Cannot convert `{:?}` into `{}`",
159                        value,
160                        stringify!($source),
161                    ))),
162                }
163            }
164        }
165    };
166}
167impl_as_value!(
168    bool,
169    Value::Boolean => |v| Ok(v),
170    Value::Int8 => |v| Ok(v != 0),
171    Value::Int16 => |v| Ok(v != 0),
172    Value::Int32 => |v| Ok(v != 0),
173    Value::Int64 => |v| Ok(v != 0),
174    Value::Int128 => |v| Ok(v != 0),
175    Value::UInt8 => |v| Ok(v != 0),
176    Value::UInt16 => |v| Ok(v != 0),
177    Value::UInt32 => |v| Ok(v != 0),
178    Value::UInt64 => |v| Ok(v != 0),
179    Value::UInt128 => |v| Ok(v != 0),
180);
181impl_as_value!(
182    f32,
183    Value::Float32 => |v| Ok(v),
184    Value::Float64 => |v| Ok(v as f32),
185    Value::Decimal => |v: Decimal| Ok(v.try_into()?),
186);
187impl_as_value!(
188    f64,
189    Value::Float64 => |v| Ok(v),
190    Value::Float32 => |v| Ok(v as f64),
191    Value::Decimal => |v: Decimal| Ok(v.try_into()?),
192);
193impl_as_value!(char,
194    Value::Char => |v| Ok(v),
195    Value::Varchar => |v: String| {
196        if v.len() != 1 {
197            Err(Error::msg("Cannot convert Value::Varchar containing more then one character into a char."))
198        } else {
199            Ok(v.chars().next().unwrap())
200        }
201    }
202);
203impl_as_value!(
204    String,
205    Value::Varchar => |v| Ok(v),
206    Value::Char => |v: char| Ok(v.into()),
207);
208impl<'a> AsValue for Cow<'a, str> {
209    fn as_empty_value() -> Value {
210        Value::Varchar(None)
211    }
212    fn as_value(self) -> Value {
213        Value::Varchar(Some(self.into()))
214    }
215    fn try_from_value(value: Value) -> Result<Self>
216    where
217        Self: Sized,
218    {
219        let Value::Varchar(Some(value)) = value else {
220            return Err(Error::msg(format!(
221                "Cannot convert `{}` into `Cow<'a, str>`",
222                value.to_token_stream().to_string(),
223            )));
224        };
225        Ok(value.into())
226    }
227}
228impl_as_value!(Box<[u8]>, Value::Blob => |v| Ok(v));
229impl_as_value!(
230    time::Date,
231    Value::Date => |v| Ok(v),
232    Value::Varchar => |v: String| Ok(
233        time::Date::parse(
234            &v,
235            format_description!("[year]-[month]-[day]")
236        )
237        .with_context(|| format!("Cannot convert '{}' to time::Time", v))
238    ?),
239);
240impl_as_value!(
241    time::Time,
242    Value::Time => |v| Ok(v),
243    Value::Varchar => |v: String| {
244        time::Time::parse(
245            &v,
246            format_description!("[hour]:[minute]:[second].[subsecond]"),
247        )
248        .or(time::Time::parse(
249            &v,
250            format_description!("[hour]:[minute]:[second]"),
251        ))
252        .or(time::Time::parse(
253            &v,
254            format_description!("[hour]:[minute]"),
255        ))
256        .with_context(|| format!("Cannot convert '{}' to time::Time", v))
257        .map_err(Into::into)
258    },
259);
260impl_as_value!(
261    time::PrimitiveDateTime,
262    Value::Timestamp => |v| Ok(v),
263    Value::Varchar => |v: String| {
264        time::PrimitiveDateTime::parse(
265            &v,
266            format_description!("[year]-[month]-[day]T[hour]:[minute]:[second].[subsecond]")
267        )
268        .or(time::PrimitiveDateTime::parse(
269            &v,
270            format_description!("[year]-[month]-[day]T[hour]:[minute]:[second]")
271        ))
272        .or(time::PrimitiveDateTime::parse(
273            &v,
274            format_description!("[year]-[month]-[day]T[hour]:[minute]")
275        ))
276        .or(time::PrimitiveDateTime::parse(
277            &v,
278            format_description!("[year]-[month]-[day] [hour]:[minute]:[second].[subsecond]")
279        ))
280        .or(time::PrimitiveDateTime::parse(
281            &v,
282            format_description!("[year]-[month]-[day] [hour]:[minute]:[second]")
283        ))
284        .or(time::PrimitiveDateTime::parse(
285            &v,
286            format_description!("[year]-[month]-[day] [hour]:[minute]")
287        ))
288        .with_context(|| format!("Cannot convert '{}' to time::PrimitiveDateTime", v))
289        .map_err(Into::into)
290    },
291);
292impl_as_value!(
293    time::OffsetDateTime,
294    Value::TimestampWithTimezone => |v| Ok(v),
295    Value::Varchar => |v: String| {
296        time::OffsetDateTime::parse(
297            &v,
298            format_description!("[year]-[month]-[day]T[hour]:[minute]:[second].[subsecond][offset_hour sign:mandatory]:[offset_minute]")
299        )
300        .or(time::OffsetDateTime::parse(
301            &v,
302            format_description!("[year]-[month]-[day]T[hour]:[minute]:[second].[subsecond][offset_hour sign:mandatory]")
303        ))
304        .or(time::OffsetDateTime::parse(
305            &v,
306            format_description!("[year]-[month]-[day]T[hour]:[minute]:[second][offset_hour sign:mandatory]:[offset_minute]")
307        ))
308        .or(time::OffsetDateTime::parse(
309            &v,
310            format_description!("[year]-[month]-[day]T[hour]:[minute]:[second][offset_hour sign:mandatory]")
311        ))
312        .or(time::OffsetDateTime::parse(
313            &v,
314            format_description!("[year]-[month]-[day]T[hour]:[minute][offset_hour sign:mandatory]:[offset_minute]")
315        ))
316        .or(time::OffsetDateTime::parse(
317            &v,
318            format_description!("[year]-[month]-[day]T[hour]:[minute][offset_hour sign:mandatory]")
319        ))
320        .with_context(|| format!("Cannot convert '{}' to time::OffsetDateTime", v))
321        .map_err(Into::into)
322    }
323);
324impl_as_value!(std::time::Duration, Value::Interval => |v: Interval| Ok(v.into()));
325impl_as_value!(Interval, Value::Interval => |v| Ok(v));
326impl_as_value!(time::Duration, Value::Interval => |v: Interval| Ok(v.into()));
327impl_as_value!(
328    uuid::Uuid,
329    Value::Uuid => |v| Ok(v),
330    Value::Varchar => |v: String| Ok(uuid::Uuid::parse_str(&v)?),
331);
332
333impl AsValue for Decimal {
334    fn as_empty_value() -> Value {
335        Value::Decimal(None, 0, 0)
336    }
337    fn as_value(self) -> Value {
338        Value::Decimal(Some(self), 0, self.scale() as _)
339    }
340    fn try_from_value(value: Value) -> Result<Self> {
341        match value {
342            Value::Decimal(Some(v), ..) => Ok(v),
343            Value::Int8(Some(v), ..) => Ok(Decimal::new(v as i64, 0)),
344            Value::Int16(Some(v), ..) => Ok(Decimal::new(v as i64, 0)),
345            Value::Int32(Some(v), ..) => Ok(Decimal::new(v as i64, 0)),
346            Value::Int64(Some(v), ..) => Ok(Decimal::new(v, 0)),
347            Value::UInt8(Some(v), ..) => Ok(Decimal::new(v as i64, 0)),
348            Value::UInt16(Some(v), ..) => Ok(Decimal::new(v as i64, 0)),
349            Value::UInt32(Some(v), ..) => Ok(Decimal::new(v as i64, 0)),
350            Value::UInt64(Some(v), ..) => Ok(Decimal::new(v as i64, 0)),
351            Value::Float32(Some(v), ..) => Ok(Decimal::from_f32(v)
352                .ok_or(Error::msg("Could not convert the Float32 into Decimal"))?),
353            Value::Float64(Some(v), ..) => Ok(Decimal::from_f64(v)
354                .ok_or(Error::msg("Could not convert the Float64 into Decimal"))?),
355            _ => Err(Error::msg(format!(
356                "Cannot convert `{:?}` into `rust_decimal::Decimal`",
357                value,
358            ))),
359        }
360    }
361}
362
363impl<const W: u8, const S: u8> AsValue for FixedDecimal<W, S> {
364    fn as_empty_value() -> Value {
365        Decimal::as_empty_value()
366    }
367    fn as_value(self) -> Value {
368        Value::Decimal(Some(self.0), W, S)
369    }
370    fn try_from_value(value: Value) -> Result<Self>
371    where
372        Self: Sized,
373    {
374        Ok(Self(Decimal::try_from_value(value)?))
375    }
376}
377
378impl<T: AsValue, const N: usize> AsValue for [T; N] {
379    fn as_empty_value() -> Value {
380        Value::Array(None, Box::new(T::as_empty_value()), N as u32)
381    }
382    fn as_value(self) -> Value {
383        Value::Array(
384            Some(self.into_iter().map(AsValue::as_value).collect()),
385            Box::new(T::as_empty_value()),
386            N as u32,
387        )
388    }
389    fn try_from_value(value: Value) -> Result<Self> {
390        let from_iter = |mut it: vec::IntoIter<Value>| {
391            let len = it.len();
392            array::try_from_fn(|_| {
393                T::try_from_value(it.next().ok_or_else(|| {
394                    Error::msg(format!(
395                        "Cannot convert a container of {} elements to array {}",
396                        len,
397                        any::type_name::<Self>()
398                    ))
399                })?)
400            })
401        };
402        match value {
403            Value::List(Some(v), ..) if v.len() == N => from_iter(v.into_iter()),
404            Value::Array(Some(v), ..) => from_iter(v.into_iter()),
405            _ => Err(Error::msg(format!(
406                "Cannot convert `{:?}` into array `{}`",
407                value,
408                any::type_name::<Self>()
409            ))),
410        }
411    }
412}
413
414macro_rules! impl_as_value {
415    ($list:ident) => {
416        impl<T: AsValue> AsValue for $list<T> {
417            fn as_empty_value() -> Value {
418                Value::List(None, Box::new(T::as_empty_value()))
419            }
420            fn as_value(self) -> Value {
421                Value::List(
422                    Some(self.into_iter().map(AsValue::as_value).collect()),
423                    Box::new(T::as_empty_value()),
424                )
425            }
426            fn try_from_value(value: Value) -> Result<Self> {
427                match value {
428                    Value::List(Some(v), ..) => Ok(v
429                        .into_iter()
430                        .map(|v| Ok::<_, Error>(<T as AsValue>::try_from_value(v)?))
431                        .collect::<Result<_>>()?),
432                    Value::List(None, ..) => Ok($list::<T>::new()),
433                    Value::Array(Some(v), ..) => Ok(v
434                        .into_iter()
435                        .map(|v| Ok::<_, Error>(<T as AsValue>::try_from_value(v)?))
436                        .collect::<Result<_>>()?),
437                    _ => Err(Error::msg(format!(
438                        "Cannot convert `{:?}` into list-like `{}`",
439                        value,
440                        stringify!($list<T>),
441                    ))),
442                }
443            }
444        }
445    };
446}
447impl_as_value!(Vec);
448impl_as_value!(VecDeque);
449impl_as_value!(LinkedList);
450
451macro_rules! impl_as_value {
452    ($map:ident, $($key_trait:ident),+) => {
453        impl<K: AsValue $(+ $key_trait)+, V: AsValue> AsValue for $map<K, V> {
454            fn as_empty_value() -> Value {
455                Value::Map(None, K::as_empty_value().into(), V::as_empty_value().into())
456            }
457            fn as_value(self) -> Value {
458                Value::Map(
459                    Some(
460                        self.into_iter()
461                            .map(|(k, v)| (k.as_value(), v.as_value()))
462                            .collect(),
463                    ),
464                    K::as_empty_value().into(),
465                    V::as_empty_value().into(),
466                )
467            }
468            fn try_from_value(value: Value) -> Result<Self> {
469                if let Value::Map(Some(v), ..) = value {
470                    Ok(v.into_iter()
471                        .map(|(k, v)| {
472                            Ok((
473                                <K as AsValue>::try_from_value(k)?,
474                                <V as AsValue>::try_from_value(v)?,
475                            ))
476                        })
477                        .collect::<Result<_>>()?)
478                } else {
479                    Err(Error::msg(format!(
480                        "Cannot convert `{:?}` into map `{}`",
481                        value,
482                        stringify!($map<K, V>),
483                    )))
484                }
485            }
486        }
487    }
488}
489impl_as_value!(BTreeMap, Ord);
490impl_as_value!(HashMap, Eq, Hash);
491
492impl<T: AsValue> AsValue for Passive<T> {
493    fn as_empty_value() -> Value {
494        T::as_empty_value()
495    }
496    fn as_value(self) -> Value {
497        match self {
498            Passive::Set(v) => v.as_value(),
499            Passive::NotSet => T::as_empty_value(),
500        }
501    }
502    fn try_from_value(value: Value) -> Result<Self> {
503        Ok(Passive::Set(<T as AsValue>::try_from_value(value)?))
504    }
505}
506
507impl<T: AsValue> AsValue for Option<T> {
508    fn as_empty_value() -> Value {
509        T::as_empty_value()
510    }
511    fn as_value(self) -> Value {
512        match self {
513            Some(v) => v.as_value(),
514            None => T::as_empty_value(),
515        }
516    }
517    fn try_from_value(value: Value) -> Result<Self> {
518        Ok(if value.is_null() {
519            None
520        } else {
521            Some(<T as AsValue>::try_from_value(value)?)
522        })
523    }
524}
525
526// TODO: USe the macro below once box_into_inner is stabilized
527impl<T: AsValue> AsValue for Box<T> {
528    fn as_empty_value() -> Value {
529        T::as_empty_value()
530    }
531    fn as_value(self) -> Value {
532        (*self).as_value()
533    }
534    fn try_from_value(value: Value) -> Result<Self> {
535        Ok(Box::new(<T as AsValue>::try_from_value(value)?))
536    }
537}
538
539macro_rules! impl_as_value {
540    ($wrapper:ident) => {
541        impl<T: AsValue + ToOwned<Owned = impl AsValue>> AsValue for $wrapper<T> {
542            fn as_empty_value() -> Value {
543                T::as_empty_value()
544            }
545            fn as_value(self) -> Value {
546                $wrapper::<T>::into_inner(self).as_value()
547            }
548            fn try_from_value(value: Value) -> Result<Self> {
549                Ok($wrapper::new(<T as AsValue>::try_from_value(value)?))
550            }
551        }
552    };
553}
554impl_as_value!(Cell);
555impl_as_value!(RefCell);
556
557impl<T: AsValue> AsValue for RwLock<T> {
558    fn as_empty_value() -> Value {
559        T::as_empty_value()
560    }
561    fn as_value(self) -> Value {
562        self.into_inner()
563            .expect("Error occurred while trying to take the content of the RwLock")
564            .as_value()
565    }
566    fn try_from_value(value: Value) -> Result<Self> {
567        Ok(RwLock::new(<T as AsValue>::try_from_value(value)?))
568    }
569}
570
571macro_rules! impl_as_value {
572    ($wrapper:ident) => {
573        impl<T: AsValue + ToOwned<Owned = impl AsValue>> AsValue for $wrapper<T> {
574            fn as_empty_value() -> Value {
575                T::as_empty_value()
576            }
577            fn as_value(self) -> Value {
578                $wrapper::try_unwrap(self)
579                    .map(|v| v.as_value())
580                    .unwrap_or_else(|v| v.as_ref().to_owned().as_value())
581            }
582            fn try_from_value(value: Value) -> Result<Self> {
583                Ok($wrapper::new(<T as AsValue>::try_from_value(value)?))
584            }
585        }
586    };
587}
588impl_as_value!(Arc);
589impl_as_value!(Rc);