spanner_rs/
to_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 to 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/// `ToSpanner` is implemented for `Option<T>` when `T` implements `ToSpanner`.
33/// `Option<T>` represents a nullable Spanner value.
34///
35/// # Arrays
36///
37/// `ToSpanner` is implemented for `Vec<T>` when `T` implements `ToSpanner`.
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 ToSpanner {
41    /// Creates a new Cloud Spanner value from this value.
42    fn to_spanner(&self) -> Result<Value, Error>;
43
44    /// Returns the Cloud Spanner [Type] that this implementation produces.
45    fn spanner_type() -> Type
46    where
47        Self: Sized;
48}
49
50impl<T> ToSpanner for Option<T>
51where
52    T: ToSpanner,
53{
54    fn to_spanner(&self) -> Result<Value, Error> {
55        match self.as_ref() {
56            Some(v) => v.to_spanner(),
57            None => Ok(Value::Null(<T as ToSpanner>::spanner_type())),
58        }
59    }
60    fn spanner_type() -> Type {
61        <T as ToSpanner>::spanner_type()
62    }
63}
64
65impl<T> ToSpanner for Vec<T>
66where
67    T: ToSpanner,
68{
69    fn to_spanner(&self) -> Result<Value, Error> {
70        let values = self
71            .iter()
72            .map(|v| v.to_spanner())
73            .collect::<Result<Vec<Value>, Error>>()?;
74        Ok(Value::Array(<T as ToSpanner>::spanner_type(), values))
75    }
76    fn spanner_type() -> Type {
77        Type::Array(Box::new(<T as ToSpanner>::spanner_type()))
78    }
79}
80
81impl<T> ToSpanner for &[T]
82where
83    T: ToSpanner,
84{
85    fn to_spanner(&self) -> Result<Value, Error> {
86        let values = self
87            .iter()
88            .map(|v| v.to_spanner())
89            .collect::<Result<Vec<Value>, Error>>()?;
90        Ok(Value::Array(<T as ToSpanner>::spanner_type(), values))
91    }
92    fn spanner_type() -> Type {
93        Type::Array(Box::new(<T as ToSpanner>::spanner_type()))
94    }
95}
96
97macro_rules! simple {
98    ($t:ty, $v:ident, $into:path $(, $deref:tt)?) => {
99        impl ToSpanner for $t {
100            fn to_spanner(&self) -> Result<Value, Error> {
101                Ok(Value::$v($into($($deref)? self)))
102            }
103
104            fn spanner_type() -> Type {
105                Type::$v
106            }
107        }
108    };
109}
110
111simple!(i8, Int64, i64::from, *);
112simple!(u8, Int64, i64::from, *);
113simple!(i16, Int64, i64::from, *);
114simple!(u16, Int64, i64::from, *);
115simple!(i32, Int64, i64::from, *);
116simple!(u32, Int64, i64::from, *);
117simple!(i64, Int64, i64::from, *);
118simple!(String, String, Clone::clone);
119simple!(&str, String, ToString::to_string);
120#[cfg(feature = "numeric")]
121simple!(BigDecimal, Numeric, Clone::clone);
122simple!(Bytes, Bytes, Clone::clone);
123#[cfg(feature = "json")]
124simple!(serde_json::Value, Json, Clone::clone);
125#[cfg(feature = "temporal")]
126simple!(chrono::DateTime<chrono::Utc>, Timestamp, Clone::clone);
127#[cfg(feature = "temporal")]
128simple!(chrono::NaiveDate, Date, Clone::clone);
129
130#[cfg(test)]
131mod test {
132    use super::*;
133
134    macro_rules! simple_test_int64 {
135        ($t:ty) => {
136            assert_eq!((0 as $t).to_spanner().ok(), Some(Value::Int64(0)));
137        };
138        ($($t:ty),+) => {
139            $(
140                simple_test_int64!($t);
141            )+
142        };
143    }
144
145    #[test]
146    fn test_to_spanner_simple_int64() {
147        simple_test_int64!(i8, u8, i16, u16, i32, u32, i64);
148    }
149
150    #[test]
151    fn test_to_spanner_opt() {
152        let some = Some(0 as u32);
153        assert_eq!(some.to_spanner().ok(), Some(Value::Int64(0)));
154        let none: Option<u32> = None;
155        assert_eq!(none.to_spanner().ok(), Some(Value::Null(Type::Int64)));
156    }
157
158    #[test]
159    fn test_to_spanner_array() {
160        let array = vec![0, 1, 2, 3, 4];
161        assert_eq!(
162            array.to_spanner().ok(),
163            Some(Value::Array(
164                Type::Int64,
165                vec![
166                    Value::Int64(0),
167                    Value::Int64(1),
168                    Value::Int64(2),
169                    Value::Int64(3),
170                    Value::Int64(4)
171                ]
172            ))
173        );
174        let empty: Vec<u32> = vec![];
175        assert_eq!(
176            empty.to_spanner().ok(),
177            Some(Value::Array(Type::Int64, vec![]))
178        );
179    }
180}