clia_rustorm_dao/
value.rs

1#![allow(clippy::cast_lossless)]
2use crate::{
3    interval::Interval,
4    ConvertError,
5};
6use bigdecimal::{
7    BigDecimal,
8    ToPrimitive,
9};
10use chrono::{
11    DateTime,
12    NaiveDate,
13    NaiveDateTime,
14    NaiveTime,
15    Utc,
16};
17use geo_types::Point;
18use serde_derive::{
19    Deserialize,
20    Serialize,
21};
22use std::fmt;
23use uuid::Uuid;
24
25/// Generic value storage 32 byte in size
26/// Some contains the same value container, but the variant is more
27/// important for type hinting and view presentation hinting purposes
28#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
29pub enum Value {
30    Nil, // no value
31    Bool(bool),
32
33    Tinyint(i8),
34    Smallint(i16),
35    Int(i32),
36    Bigint(i64),
37
38    Float(f32),
39    Double(f64),
40    BigDecimal(BigDecimal),
41
42    Blob(Vec<u8>),
43    Char(char),
44    Text(String),
45    Json(String),
46
47    Uuid(Uuid),
48    Date(NaiveDate),
49    Time(NaiveTime),
50    DateTime(NaiveDateTime),
51    Timestamp(DateTime<Utc>),
52    Interval(Interval),
53
54    Point(Point<f64>),
55
56    Array(Array),
57}
58
59impl Value {
60    pub fn is_nil(&self) -> bool { *self == Value::Nil }
61}
62
63impl fmt::Display for Value {
64    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
65        match self {
66            Value::Nil => write!(f, ""),
67            Value::Bool(v) => write!(f, "{}", v),
68            Value::Tinyint(v) => write!(f, "{}", v),
69            Value::Smallint(v) => write!(f, "{}", v),
70            Value::Int(v) => write!(f, "{}", v),
71            Value::Bigint(v) => write!(f, "{}", v),
72            Value::Float(v) => write!(f, "{}", v),
73            Value::Double(v) => write!(f, "{}", v),
74            Value::BigDecimal(v) => write!(f, "{}", v),
75            Value::Char(v) => write!(f, "{}", v),
76            Value::Text(v) => write!(f, "{}", v),
77            Value::Json(v) => write!(f, "{}", v),
78            Value::Uuid(v) => write!(f, "{}", v),
79            Value::Date(v) => write!(f, "{}", v),
80            Value::Time(v) => write!(f, "{}", v),
81            Value::DateTime(v) => write!(f, "{}", v.format("%Y-%m-%d %H:%M:%S").to_string()),
82            Value::Timestamp(v) => write!(f, "{}", v.to_rfc3339()),
83            Value::Array(array) => array.fmt(f),
84            Value::Blob(v) => {
85                let encoded = base64::encode_config(&v, base64::STANDARD);
86                write!(f, "{}", encoded)
87            }
88            _ => panic!("not yet implemented: {:?}", self),
89        }
90    }
91}
92
93#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
94pub enum Array {
95    /*
96    Bool(Vec<bool>),
97
98    Tinyint(Vec<i8>),
99    Smallint(Vec<i16>),
100    */
101    Int(Vec<i32>),
102    Float(Vec<f32>),
103    /*
104    Bigint(Vec<i64>),
105
106    Double(Vec<f64>),
107    BigDecimal(Vec<BigDecimal>),
108    */
109    Text(Vec<String>),
110    /*
111    Char(Vec<char>),
112    Uuid(Vec<Uuid>),
113    Date(Vec<NaiveDate>),
114    Timestamp(Vec<DateTime<Utc>>),
115    */
116}
117
118impl fmt::Display for Array {
119    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
120        match self {
121            Array::Text(texts) => {
122                let json_arr = serde_json::to_string(texts).expect("must serialize");
123                write!(f, "{}", json_arr)
124            }
125            Array::Float(floats) => {
126                let json_arr = serde_json::to_string(floats).expect("must serialize");
127                write!(f, "{}", json_arr)
128            }
129            _ => panic!("not yet implemented: {:?}", self),
130        }
131    }
132}
133
134/// A trait to allow passing of parameters ergonomically
135/// in em.execute_sql_with_return
136pub trait ToValue {
137    fn to_value(&self) -> Value;
138}
139
140macro_rules! impl_to_value {
141    ($ty:ty, $variant:ident) => {
142        impl ToValue for $ty {
143            fn to_value(&self) -> Value { Value::$variant(self.to_owned()) }
144        }
145    };
146}
147
148impl_to_value!(bool, Bool);
149impl_to_value!(i8, Tinyint);
150impl_to_value!(i16, Smallint);
151impl_to_value!(i32, Int);
152impl_to_value!(i64, Bigint);
153impl_to_value!(f32, Float);
154impl_to_value!(f64, Double);
155impl_to_value!(Vec<u8>, Blob);
156impl_to_value!(char, Char);
157impl_to_value!(String, Text);
158impl_to_value!(Uuid, Uuid);
159impl_to_value!(NaiveDate, Date);
160impl_to_value!(NaiveTime, Time);
161impl_to_value!(DateTime<Utc>, Timestamp);
162impl_to_value!(NaiveDateTime, DateTime);
163
164impl ToValue for &str {
165    fn to_value(&self) -> Value { Value::Text(self.to_string()) }
166}
167
168impl ToValue for Vec<String> {
169    fn to_value(&self) -> Value { Value::Array(Array::Text(self.to_owned())) }
170}
171
172impl<T> ToValue for Option<T>
173where
174    T: ToValue,
175{
176    fn to_value(&self) -> Value {
177        match self {
178            Some(v) => v.to_value(),
179            None => Value::Nil,
180        }
181    }
182}
183
184impl<T> ToValue for &T
185where
186    T: ToValue,
187{
188    fn to_value(&self) -> Value { (*self).to_value() }
189}
190
191impl<T> From<T> for Value
192where
193    T: ToValue,
194{
195    fn from(v: T) -> Value { v.to_value() }
196}
197
198pub trait FromValue: Sized {
199    fn from_value(v: &Value) -> Result<Self, ConvertError>;
200}
201
202macro_rules! impl_from_value {
203    ($ty: ty, $ty_name: tt, $($variant: ident),*) => {
204        /// try from to owned
205        impl FromValue for $ty {
206            fn from_value(v: &Value) -> Result<Self, ConvertError> {
207                match *v {
208                    $(Value::$variant(ref v) => Ok(v.to_owned() as $ty),
209                    )*
210                    _ => Err(ConvertError::NotSupported(format!("{:?}",v), $ty_name.into())),
211                }
212            }
213        }
214    }
215}
216
217macro_rules! impl_from_value_numeric {
218    ($ty: ty, $method:ident, $ty_name: tt, $($variant: ident),*) => {
219        impl FromValue for $ty {
220            fn from_value(v: &Value) -> Result<Self, ConvertError> {
221                match *v {
222                    $(Value::$variant(ref v) => Ok(v.to_owned() as $ty),
223                    )*
224                    Value::BigDecimal(ref v) => Ok(v.$method().unwrap()),
225                    _ => Err(ConvertError::NotSupported(format!("{:?}", v), $ty_name.into())),
226                }
227            }
228        }
229    }
230}
231
232impl_from_value!(Vec<u8>, "Vec<u8>", Blob);
233impl_from_value!(char, "char", Char);
234impl_from_value!(Uuid, "Uuid", Uuid);
235impl_from_value!(NaiveDate, "NaiveDate", Date);
236impl_from_value_numeric!(i8, to_i8, "i8", Tinyint);
237impl_from_value_numeric!(i16, to_i16, "i16", Tinyint, Smallint);
238impl_from_value_numeric!(i32, to_i32, "i32", Tinyint, Smallint, Int, Bigint);
239impl_from_value_numeric!(i64, to_i64, "i64", Tinyint, Smallint, Int, Bigint);
240impl_from_value_numeric!(f32, to_f32, "f32", Float);
241impl_from_value_numeric!(f64, to_f64, "f64", Float, Double);
242
243/// Char can be casted into String
244/// and they havea separate implementation for extracting data
245impl FromValue for String {
246    fn from_value(v: &Value) -> Result<Self, ConvertError> {
247        match *v {
248            Value::Text(ref v) => Ok(v.to_owned()),
249            Value::Char(ref v) => {
250                let mut s = String::new();
251                s.push(*v);
252                Ok(s)
253            }
254            Value::Blob(ref v) => {
255                String::from_utf8(v.to_owned()).map_err(|e| {
256                    ConvertError::NotSupported(format!("{:?}", v), format!("String: {}", e))
257                })
258            }
259            _ => {
260                Err(ConvertError::NotSupported(
261                    format!("{:?}", v),
262                    "String".to_string(),
263                ))
264            }
265        }
266    }
267}
268
269impl FromValue for Vec<String> {
270    fn from_value(v: &Value) -> Result<Self, ConvertError> {
271        match *v {
272            Value::Array(Array::Text(ref t)) => Ok(t.to_owned()),
273            _ => {
274                Err(ConvertError::NotSupported(
275                    format!("{:?}", v),
276                    "Vec<String>".to_string(),
277                ))
278            }
279        }
280    }
281}
282
283impl FromValue for bool {
284    fn from_value(v: &Value) -> Result<Self, ConvertError> {
285        match *v {
286            Value::Bool(v) => Ok(v),
287            Value::Tinyint(v) => Ok(v == 1),
288            Value::Smallint(v) => Ok(v == 1),
289            Value::Int(v) => Ok(v == 1),
290            Value::Bigint(v) => Ok(v == 1),
291            _ => {
292                Err(ConvertError::NotSupported(
293                    format!("{:?}", v),
294                    "bool".to_string(),
295                ))
296            }
297        }
298    }
299}
300
301impl FromValue for DateTime<Utc> {
302    fn from_value(v: &Value) -> Result<Self, ConvertError> {
303        match *v {
304            Value::Text(ref v) => Ok(DateTime::<Utc>::from_utc(parse_naive_date_time(v), Utc)),
305            Value::DateTime(v) => Ok(DateTime::<Utc>::from_utc(v, Utc)),
306            Value::Timestamp(v) => Ok(v),
307            _ => {
308                Err(ConvertError::NotSupported(
309                    format!("{:?}", v),
310                    "DateTime".to_string(),
311                ))
312            }
313        }
314    }
315}
316
317impl FromValue for NaiveDateTime {
318    fn from_value(v: &Value) -> Result<Self, ConvertError> {
319        match *v {
320            Value::Text(ref v) => Ok(parse_naive_date_time(v)),
321            Value::DateTime(v) => Ok(v),
322            _ => {
323                Err(ConvertError::NotSupported(
324                    format!("{:?}", v),
325                    "NaiveDateTime".to_string(),
326                ))
327            }
328        }
329    }
330}
331
332impl<T> FromValue for Option<T>
333where
334    T: FromValue,
335{
336    fn from_value(v: &Value) -> Result<Self, ConvertError> {
337        match *v {
338            Value::Nil => Ok(None),
339            _ => FromValue::from_value(v).map(Some),
340        }
341    }
342}
343
344fn parse_naive_date_time(v: &str) -> NaiveDateTime {
345    let ts = NaiveDateTime::parse_from_str(&v, "%Y-%m-%d %H:%M:%S");
346    if let Ok(ts) = ts {
347        ts
348    } else {
349        let ts = NaiveDateTime::parse_from_str(&v, "%Y-%m-%d %H:%M:%S%.3f");
350        if let Ok(ts) = ts {
351            ts
352        } else {
353            panic!("unable to parse timestamp: {}", v);
354        }
355    }
356}
357
358#[cfg(test)]
359mod tests {
360    use super::*;
361    use chrono::offset::Utc;
362    use std::mem::size_of;
363
364    #[test]
365    fn data_sizes() {
366        assert_eq!(48, size_of::<Value>()); // use to be 32, now 48 due to the addition of BigDecimal type
367        assert_eq!(24, size_of::<Vec<u8>>());
368        assert_eq!(24, size_of::<String>());
369        assert_eq!(12, size_of::<DateTime<Utc>>());
370        assert_eq!(4, size_of::<NaiveDate>());
371        assert_eq!(16, size_of::<Uuid>());
372    }
373
374    #[test]
375    fn test_types() {
376        let _: Value = 127i8.to_value();
377        let _: Value = 2222i16.to_value();
378        let _: Value = 4444i32.to_value();
379        let _: Value = 10000i64.to_value();
380        let _v1: Value = 1.0f32.to_value();
381        let _v2: Value = 100.0f64.to_value();
382        let _v3: Value = Utc::now().to_value();
383        let _v7: Value = Utc::today().naive_utc().to_value();
384        let _v4: Value = "hello world!".to_value();
385        let _v5: Value = "hello world!".to_string().to_value();
386        let _v6: Value = vec![1u8, 2, 255, 3].to_value();
387    }
388
389    #[test]
390    fn naive_date_parse() {
391        let v = "2018-01-29";
392        let ts = NaiveDate::parse_from_str(v, "%Y-%m-%d");
393        println!("{:?}", ts);
394        assert!(ts.is_ok());
395    }
396
397    #[test]
398    fn naive_date_time_parse() {
399        let v = "2018-01-29 09:58:20";
400        let ts = NaiveDateTime::parse_from_str(v, "%Y-%m-%d %H:%M:%S");
401        println!("{:?}", ts);
402        assert!(ts.is_ok());
403    }
404
405    #[test]
406    fn date_time_conversion() {
407        let v = "2018-01-29 09:58:20";
408        let ts = NaiveDateTime::parse_from_str(v, "%Y-%m-%d %H:%M:%S");
409        println!("{:?}", ts);
410        assert!(ts.is_ok());
411        DateTime::<Utc>::from_utc(ts.unwrap(), Utc);
412    }
413}