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