spanner_rs/
from_spanner.rs

1#[cfg(feature = "numeric")]
2use bigdecimal::BigDecimal;
3use prost::bytes::Bytes;
4
5use crate::{Error, Type, Value};
6
7/// A trait for Rust types that can be converted from Cloud Spanner values.
8///
9/// # Types
10///
11/// The crate provides the following mapping between Cloud Spanner types and Rust types.
12///
13/// | Rust Type | Spanner Type |
14/// |---|---|
15/// | `bool` | [`BOOL`](https://cloud.google.com/spanner/docs/data-types#boolean_type) |
16/// | `u8`, `i8`, `u16`, `i16`, `u32`, `i32`, `i64` | [`INT64`](https://cloud.google.com/spanner/docs/data-types#integer_type) |
17/// | `f64` | [`FLOAT64`](https://cloud.google.com/spanner/docs/data-types#floating_point_types) |
18/// | `&str`, `String` | [`STRING`](https://cloud.google.com/spanner/docs/data-types#string_type) |
19/// | `&[u8]`, `Bytes` | [`BYTES`](https://cloud.google.com/spanner/docs/data-types#bytes_type) |
20///
21/// The following are provided when the corresponding feature is enabled:
22///
23/// | Feature | Rust Type | Spanner Type |
24/// |---|---|---|
25/// | `json` | `serde_json::Value` | [`JSON`](https://cloud.google.com/spanner/docs/data-types#json_type) |
26/// | `numeric` | `bigdecimal::BigDecimal` | [`NUMERIC`](https://cloud.google.com/spanner/docs/data-types#numeric_type) |
27/// | `temporal` | `chrono::DateTime<Utc>` | [`TIMESTAMP`](https://cloud.google.com/spanner/docs/data-types#timestamp_type) |
28/// | `temporal` | `chrono::NaiveDate` | [`DATE`](https://cloud.google.com/spanner/docs/data-types#date_type) |
29///
30/// # Nullability
31///
32/// `FromSpanner` is implemented for `Option<T>` when `T` implements `FromSpanner`.
33/// `Option<T>` represents a nullable Spanner value.
34///
35/// # Arrays
36///
37/// `FromSpanner` is implemented for `Vec<T>` when `T` implements `FromSpanner`.
38/// Such values map to Spanner's [`Array`](https://cloud.google.com/spanner/docs/data-types#array_type) type.
39/// Arrays may contain `null` values (i.e.: `Vec<Option<T>>`). Note that `Vec<Vec<T>>` is not allowed.
40pub trait FromSpanner<'a>: Sized {
41    /// Creates a new value of this type from the provided Cloud Spanner value.
42    /// Values passed to this method should not be `Value::Null`, if this is not known to be the case, use [FromSpanner::from_spanner_nullable] instead.
43    fn from_spanner(value: &'a Value) -> Result<Self, Error>;
44
45    /// Creates a new value of this type from the provided Cloud Spanner `NULL` value's type.
46    #[allow(unused_variables)]
47    fn from_spanner_null(tpe: &Type) -> Result<Self, Error> {
48        Err(crate::Error::Codec("value was null".to_string()))
49    }
50
51    /// Creates a new value of this type from the provided Cloud Spanner value which may or may not be null.
52    /// This method will dispatch to either [FromSpanner::from_spanner] or [FromSpanner::from_spanner_null] depending
53    /// on whether the provided value is `NULL`.
54    fn from_spanner_nullable(value: &'a Value) -> Result<Self, Error> {
55        match value {
56            Value::Null(tpe) => Self::from_spanner_null(tpe),
57            not_null => Self::from_spanner(not_null),
58        }
59    }
60}
61
62impl<'a, T> FromSpanner<'a> for Option<T>
63where
64    T: FromSpanner<'a>,
65{
66    fn from_spanner(value: &'a Value) -> Result<Self, Error> {
67        <T as FromSpanner>::from_spanner(value).map(Some)
68    }
69
70    fn from_spanner_null(_tpe: &Type) -> Result<Self, Error> {
71        Ok(None)
72    }
73}
74
75macro_rules! wrong_type {
76    ($expect:ident, $tpe:expr) => {
77        Err(Error::Codec(format!(
78            "type {:?} is unsupported by FromSpanner impl, expected {:?}",
79            $tpe,
80            Type::$expect,
81        )))
82    };
83}
84
85impl<'a, T> FromSpanner<'a> for Vec<T>
86where
87    T: FromSpanner<'a>,
88{
89    fn from_spanner(value: &'a Value) -> Result<Self, Error> {
90        match value {
91            Value::Array(_, values) => values
92                .iter()
93                .map(|value| <T as FromSpanner>::from_spanner_nullable(value))
94                .collect(),
95            _ => wrong_type!(String, value.spanner_type()),
96        }
97    }
98}
99
100impl<'a> FromSpanner<'a> for String {
101    fn from_spanner(value: &'a Value) -> Result<Self, Error> {
102        match value {
103            Value::String(v) => Ok(v.clone()),
104            _ => wrong_type!(String, value.spanner_type()),
105        }
106    }
107}
108
109impl<'a> FromSpanner<'a> for &'a str {
110    fn from_spanner(value: &'a Value) -> Result<Self, Error> {
111        match value {
112            Value::String(v) => Ok(v),
113            _ => wrong_type!(String, value.spanner_type()),
114        }
115    }
116}
117
118macro_rules! simple {
119    ($t:ty, $f:ident, TryFrom::try_from) => {
120        impl<'a> FromSpanner<'a> for $t {
121            fn from_spanner(value: &'a Value) -> Result<$t, Error> {
122                match value {
123                    Value::$f(v) => Ok(TryFrom::try_from(*v)?),
124                    _ => wrong_type!($f, value.spanner_type()),
125                }
126            }
127        }
128    };
129    ($t:ty, $f:ident, $from:path) => {
130        impl<'a> FromSpanner<'a> for $t {
131            fn from_spanner(value: &'a Value) -> Result<$t, Error> {
132                match value {
133                    Value::$f(v) => Ok($from(v)),
134                    _ => wrong_type!($f, value.spanner_type()),
135                }
136            }
137        }
138    };
139}
140
141#[inline]
142fn copy<T>(value: &T) -> T
143where
144    T: Copy,
145{
146    *value
147}
148
149simple!(i8, Int64, TryFrom::try_from);
150simple!(u8, Int64, TryFrom::try_from);
151simple!(i16, Int64, TryFrom::try_from);
152simple!(u16, Int64, TryFrom::try_from);
153simple!(i32, Int64, TryFrom::try_from);
154simple!(u32, Int64, TryFrom::try_from);
155simple!(i64, Int64, copy);
156simple!(f64, Float64, copy);
157simple!(bool, Bool, copy);
158#[cfg(feature = "numeric")]
159simple!(BigDecimal, Numeric, Clone::clone);
160#[cfg(feature = "numeric")]
161simple!(&'a BigDecimal, Numeric, std::convert::identity);
162simple!(Bytes, Bytes, Clone::clone);
163simple!(&'a Bytes, Bytes, std::convert::identity);
164simple!(&'a [u8], Bytes, std::convert::identity);
165#[cfg(feature = "json")]
166simple!(serde_json::Value, Json, Clone::clone);
167#[cfg(feature = "json")]
168simple!(&'a serde_json::Value, Json, std::convert::identity);
169#[cfg(feature = "temporal")]
170simple!(chrono::DateTime<chrono::Utc>, Timestamp, Clone::clone);
171#[cfg(feature = "temporal")]
172simple!(
173    &'a chrono::DateTime<chrono::Utc>,
174    Timestamp,
175    std::convert::identity
176);
177#[cfg(feature = "temporal")]
178simple!(chrono::NaiveDate, Date, Clone::clone);
179#[cfg(feature = "temporal")]
180simple!(&'a chrono::NaiveDate, Date, std::convert::identity);
181
182#[cfg(test)]
183mod test {
184    use super::*;
185    use crate::{Type, Value};
186    #[cfg(feature = "numeric")]
187    use bigdecimal::{BigDecimal, FromPrimitive};
188
189    macro_rules! from_spanner_ok {
190        ($t:ty, $ok_tpe:ident, $($ok_val:expr),+) => {
191            $(
192                let result = <$t as FromSpanner>::from_spanner_nullable(
193                    &Value::$ok_tpe($ok_val.into()),
194                );
195                assert_eq!(result.ok(), Some($ok_val));
196            )+
197        };
198    }
199    macro_rules! from_spanner_err {
200        ($t:ty, $err_tpe:ident, $($err_val:expr), +) => {
201            $(
202                let result = <$t as FromSpanner>::from_spanner_nullable(
203                    &Value::$err_tpe($err_val),
204                );
205                assert!(
206                    result.is_err(),
207                    "value {:?} expected to fail",
208                    &Value::$err_tpe($err_val)
209                );
210            )+
211        };
212    }
213    macro_rules! from_spanner_non_nullable {
214        ($t:ty, $tpe:ident) => {
215            let result = <$t as FromSpanner>::from_spanner_nullable(&Value::Null(Type::$tpe));
216            assert!(
217                result.is_err(),
218                "expected Err from null value, got {:?}",
219                result
220            );
221        };
222    }
223    macro_rules! from_spanner_nullable {
224        ($t:ty, $tpe:ident) => {
225            let result =
226                <Option<$t> as FromSpanner>::from_spanner_nullable(&Value::Null(Type::$tpe));
227            assert_eq!(result.ok(), Some(None));
228        };
229    }
230
231    macro_rules! from_spanner_int64 {
232        ($t:ty) => {
233            from_spanner_ok!($t, Int64, <$t>::MIN, <$t>::MAX, 0);
234            from_spanner_err!($t, Float64, 0.0, 42.5);
235            from_spanner_err!($t, Bool, true, false);
236            from_spanner_err!($t, String, "this is not an int64".to_string());
237            from_spanner_non_nullable!($t, Int64);
238            from_spanner_nullable!($t, Int64);
239        };
240        ($($t:ty),+) => {
241            $(
242                from_spanner_int64!($t);
243            )+
244        };
245    }
246
247    #[test]
248    fn test_from_spanner_int64() {
249        from_spanner_int64!(i8, u8, i16, u16, i32, u32, i64);
250    }
251
252    #[test]
253    fn test_from_spanner_bool() {
254        from_spanner_ok!(bool, Bool, true, false);
255        from_spanner_err!(bool, Float64, 0.0);
256        from_spanner_err!(bool, Int64, 0);
257        from_spanner_err!(bool, String, "this is not a bool".to_string());
258        from_spanner_non_nullable!(bool, Bool);
259        from_spanner_nullable!(bool, Bool);
260    }
261
262    #[test]
263    fn test_from_spanner_bytes() {
264        from_spanner_ok!(
265            Bytes,
266            Bytes,
267            Bytes::from_static(&[1, 2, 3, 4]),
268            Bytes::from_static(&[])
269        );
270        from_spanner_err!(Bytes, Float64, 0.0);
271        from_spanner_err!(Bytes, Int64, 0);
272        from_spanner_non_nullable!(Bytes, Bytes);
273        from_spanner_nullable!(Bytes, Bytes);
274
275        // assert FromSpanner for &[u8] from Bytes
276        let data: &'static [u8] = &[1, 2, 3, 4];
277        let bytes = Value::Bytes(Bytes::from_static(data));
278        let slice = <&[u8] as FromSpanner>::from_spanner_nullable(&bytes);
279        assert_eq!(slice.ok(), Some(data));
280    }
281
282    #[test]
283    fn test_from_spanner_float64() {
284        from_spanner_ok!(
285            f64,
286            Float64,
287            f64::MIN,
288            f64::MAX,
289            // f64::NAN, Works, but assert_eq fails
290            f64::NEG_INFINITY,
291            0.0
292        );
293        from_spanner_err!(f64, Bool, true);
294        from_spanner_err!(f64, Int64, 0);
295        from_spanner_err!(f64, String, "this is not a bool".to_string());
296        from_spanner_non_nullable!(f64, Float64);
297        from_spanner_nullable!(f64, Float64);
298    }
299
300    #[cfg(feature = "numeric")]
301    #[test]
302    fn test_from_spanner_numeric() {
303        from_spanner_ok!(
304            BigDecimal,
305            Numeric,
306            BigDecimal::from_i128(0).unwrap(),
307            BigDecimal::from_i128(42).unwrap()
308        );
309        from_spanner_err!(BigDecimal, Float64, 0.0);
310        from_spanner_err!(BigDecimal, Int64, 0);
311        from_spanner_err!(BigDecimal, String, "this is not a bool".to_string());
312        from_spanner_non_nullable!(BigDecimal, Numeric);
313        from_spanner_nullable!(BigDecimal, Numeric);
314    }
315
316    #[test]
317    fn test_from_spanner_array() {
318        let bool_array = Type::Array(Box::new(Type::Bool));
319        let value = Value::Array(Type::Bool, vec![Value::Bool(true), Value::Bool(false)]);
320
321        let result = <Vec<bool> as FromSpanner>::from_spanner_nullable(&value);
322        assert_eq!(result.ok(), Some(vec![true, false]));
323        let result =
324            <Vec<bool> as FromSpanner>::from_spanner_nullable(&Value::Array(Type::Bool, vec![]));
325        assert_eq!(result.ok(), Some(vec![]));
326
327        let result =
328            <Vec<bool> as FromSpanner>::from_spanner_nullable(&Value::Null(bool_array.clone()));
329        assert!(result.is_err());
330        let result = <Option<Vec<bool>> as FromSpanner>::from_spanner_nullable(&Value::Null(
331            bool_array.clone(),
332        ));
333        assert_eq!(result.ok(), Some(None));
334
335        let result = <Vec<Option<bool>> as FromSpanner>::from_spanner_nullable(&Value::Array(
336            Type::Bool,
337            vec![
338                Value::Bool(true),
339                Value::Null(bool_array),
340                Value::Bool(false),
341            ],
342        ))
343        .unwrap();
344        assert_eq!(result, vec![Some(true), None, Some(false)]);
345    }
346
347    #[test]
348    fn test_from_spanner_string() {
349        from_spanner_ok!(
350            String,
351            String,
352            "this is a string".to_string(),
353            "".to_string()
354        );
355        from_spanner_err!(String, Float64, 0.0);
356        from_spanner_err!(String, Int64, 0);
357        from_spanner_non_nullable!(String, String);
358        from_spanner_nullable!(String, String);
359    }
360}