tank_core/
as_value.rs

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