spanner_rs/
value.rs

1#[cfg(any(feature = "numeric", feature = "temporal"))]
2use std::str::FromStr;
3
4use crate::{Error, StructType, Type};
5
6#[cfg(feature = "numeric")]
7use bigdecimal::BigDecimal;
8use prost::bytes::Bytes;
9use prost_types::value::Kind;
10use prost_types::{ListValue, Value as SpannerValue};
11
12#[cfg(feature = "temporal")]
13use chrono::{DateTime, NaiveDate, SecondsFormat, Utc};
14
15#[cfg(feature = "json")]
16use serde_json::Value as JsValue;
17
18/// The Cloud Spanner value for the [`Struct`](https://cloud.google.com/spanner/docs/data-types#struct_type) type.
19#[derive(Clone, Debug, Default, PartialEq)]
20pub struct Struct(StructType, Vec<Value>);
21
22impl Struct {
23    /// Creates a new `Struct` with the provided type and values.
24    ///
25    /// # Panics
26    ///
27    /// If the provided `StructType` does not have the same number of fields as the number of provided values.
28    pub fn new(struct_type: StructType, values: Vec<Value>) -> Self {
29        if struct_type.fields().len() != values.len() {
30            panic!(
31                "invalid Struct: type has {} fields, but {} values were provided",
32                struct_type.fields().len(),
33                values.len()
34            )
35        }
36        Self(struct_type, values)
37    }
38
39    /// Returns a reference to this `Struct`'s type.
40    pub fn struct_type(&self) -> &StructType {
41        &self.0
42    }
43
44    /// Returns a reference to this `Struct`'s values.
45    pub fn values(&self) -> &Vec<Value> {
46        &self.1
47    }
48
49    pub(crate) fn try_from(tpe: &StructType, list_value: ListValue) -> Result<Self, crate::Error> {
50        if tpe.fields().len() != list_value.values.len() {
51            Err(crate::Error::Codec(format!(
52                "unmatched number of fields: expected {}, got {}",
53                tpe.fields().len(),
54                list_value.values.len()
55            )))
56        } else {
57            tpe.types()
58                .zip(list_value.values)
59                .map(|(tpe, value)| Value::try_from(tpe, value))
60                .collect::<Result<Vec<Value>, crate::Error>>()
61                .map(|values| Struct(tpe.clone(), values))
62        }
63    }
64}
65
66/// An enumeration of the Cloud Spanner values for each supported data type.
67// https://github.com/googleapis/googleapis/blob/master/google/spanner/v1/type.proto
68#[derive(Debug, Clone, PartialEq)]
69pub enum Value {
70    /// Represents the SQL `NULL` value, with type information.
71    Null(Type),
72    Bool(bool),
73    Int64(i64),
74    Float64(f64),
75    String(String),
76    Bytes(Bytes),
77    #[cfg(feature = "json")]
78    Json(JsValue),
79    #[cfg(feature = "numeric")]
80    Numeric(BigDecimal),
81    #[cfg(feature = "temporal")]
82    Timestamp(DateTime<Utc>),
83    #[cfg(feature = "temporal")]
84    Date(NaiveDate),
85    Array(Type, Vec<Value>),
86    Struct(Struct),
87}
88
89fn name_of(kind: Kind) -> &'static str {
90    match kind {
91        Kind::BoolValue(_) => "BoolValue",
92        Kind::ListValue(_) => "ListValue",
93        Kind::NullValue(_) => "NullValue",
94        Kind::NumberValue(_) => "NumberValue",
95        Kind::StringValue(_) => "StringValue",
96        Kind::StructValue(_) => "StructValue",
97    }
98}
99
100impl Value {
101    pub fn spanner_type(&self) -> Type {
102        match self {
103            Value::Bool(_) => Type::Bool,
104            Value::Null(inner) => inner.clone(),
105            Value::Int64(_) => Type::Int64,
106            Value::Float64(_) => Type::Float64,
107            Value::String(_) => Type::String,
108            Value::Bytes(_) => Type::Bytes,
109            Value::Json(_) => Type::Json,
110            #[cfg(feature = "numeric")]
111            Value::Numeric(_) => Type::Numeric,
112            #[cfg(feature = "temporal")]
113            Value::Timestamp(_) => Type::Timestamp,
114            #[cfg(feature = "temporal")]
115            Value::Date(_) => Type::Date,
116            Value::Array(inner, _) => inner.clone(),
117            Value::Struct(Struct(struct_type, _)) => Type::Struct(struct_type.clone()),
118        }
119    }
120
121    pub(crate) fn try_from(tpe: &Type, value: SpannerValue) -> Result<Self, crate::Error> {
122        let kind = value
123            .kind
124            .ok_or_else(|| Error::Codec("unexpected missing value format".to_string()))?;
125
126        if let Kind::NullValue(_) = kind {
127            return Ok(Value::Null(tpe.clone()));
128            // TODO: this doesn't seem to work. Null values seem to have 0 as their type code
129            // if let Some(type_code) = proto::TypeCode::from_i32(type_code) {
130            //     if tpe.code() == type_code {
131            //         return Ok(Value::Null(tpe.clone()));
132            //     }
133            // }
134            // return Err(Error::Codec(format!(
135            //     "null value had unexpected type code {}, expected {} ({:?})",
136            //     type_code,
137            //     tpe.code() as i32,
138            //     tpe.code(),
139            // )));
140        }
141
142        match tpe {
143            Type::Bool => {
144                if let Kind::BoolValue(b) = kind {
145                    return Ok(Value::Bool(b));
146                }
147            }
148            Type::Int64 => {
149                if let Kind::StringValue(s) = kind {
150                    return s
151                        .parse::<i64>()
152                        .map(Value::Int64)
153                        .map_err(|_| crate::Error::Codec(format!("{} is not a valid Int64", s)));
154                }
155            }
156            Type::Float64 => {
157                if let Kind::NumberValue(n) = kind {
158                    return Ok(Value::Float64(n));
159                }
160            }
161            #[cfg(feature = "numeric")]
162            Type::Numeric => {
163                if let Kind::StringValue(s) = kind {
164                    return BigDecimal::from_str(&s)
165                        .map(Value::Numeric)
166                        .map_err(|_| crate::Error::Codec(format!("{} is not a valid Numeric", s)));
167                }
168            }
169            Type::String => {
170                if let Kind::StringValue(s) = kind {
171                    return Ok(Value::String(s));
172                }
173            }
174            Type::Array(inner) => {
175                if let Kind::ListValue(list_value) = kind {
176                    return list_value
177                        .values
178                        .into_iter()
179                        .map(|v| Value::try_from(inner, v))
180                        .collect::<Result<Vec<Value>, crate::Error>>()
181                        .map(|values| Value::Array(inner.as_ref().clone(), values));
182                }
183            }
184            Type::Struct(struct_type) => {
185                if let Kind::ListValue(list_value) = kind {
186                    return Struct::try_from(struct_type, list_value).map(Value::Struct);
187                }
188            }
189            Type::Bytes => {
190                if let Kind::StringValue(base64) = kind {
191                    return base64::decode(base64)
192                        .map_err(|e| Error::Codec(format!("invalid bytes value: {}", e)))
193                        .map(|bytes| Value::Bytes(Bytes::from(bytes)));
194                }
195            }
196            #[cfg(feature = "json")]
197            Type::Json => {
198                if let Kind::StringValue(json) = kind {
199                    return Ok(Value::Json(serde_json::de::from_str(&json)?));
200                }
201            }
202            #[cfg(feature = "temporal")]
203            Type::Timestamp => {
204                if let Kind::StringValue(ts) = kind {
205                    return Ok(Value::Timestamp(
206                        DateTime::parse_from_rfc3339(&ts)?.with_timezone(&Utc),
207                    ));
208                }
209            }
210            #[cfg(feature = "temporal")]
211            Type::Date => {
212                if let Kind::StringValue(d) = kind {
213                    return Ok(Value::Date(NaiveDate::from_str(&d)?));
214                }
215            }
216        }
217
218        Err(Error::Codec(format!(
219            "unexpected value kind {} for type {:?}",
220            name_of(kind),
221            tpe.code(),
222        )))
223    }
224}
225
226impl TryFrom<Value> for SpannerValue {
227    type Error = crate::Error;
228
229    fn try_from(value: Value) -> Result<Self, Error> {
230        let kind = match value {
231            Value::Array(_, values) => {
232                let values = values
233                    .into_iter()
234                    .map(|v| v.try_into())
235                    .collect::<Result<Vec<SpannerValue>, Error>>()?;
236                Kind::ListValue(ListValue { values })
237            }
238            Value::Bool(b) => Kind::BoolValue(b),
239            Value::Bytes(b) => Kind::StringValue(base64::encode(b)),
240            Value::Float64(f) => Kind::NumberValue(f),
241            Value::Int64(i) => Kind::StringValue(i.to_string()),
242            #[cfg(feature = "json")]
243            Value::Json(json) => Kind::StringValue(serde_json::ser::to_string(&json)?),
244            Value::Null(tpe) => Kind::NullValue(tpe.code() as i32),
245            #[cfg(feature = "numeric")]
246            Value::Numeric(n) => Kind::StringValue(n.to_string()),
247            #[cfg(feature = "temporal")]
248            Value::Timestamp(dt) => {
249                Kind::StringValue(dt.to_rfc3339_opts(SecondsFormat::AutoSi, true))
250            }
251            #[cfg(feature = "temporal")]
252            Value::Date(d) => Kind::StringValue(d.to_string()),
253            Value::String(s) => Kind::StringValue(s),
254            Value::Struct(Struct(_, values)) => {
255                let values = values
256                    .into_iter()
257                    .map(|v| v.try_into())
258                    .collect::<Result<Vec<SpannerValue>, Error>>()?;
259
260                Kind::ListValue(ListValue { values })
261            }
262        };
263        Ok(Self { kind: Some(kind) })
264    }
265}
266
267#[cfg(test)]
268mod test {
269
270    use super::*;
271
272    fn spanner_value(kind: Kind) -> SpannerValue {
273        SpannerValue { kind: Some(kind) }
274    }
275
276    fn assert_try_from(tpe: Type, kind: Kind, expected: Value) {
277        let value = Value::try_from(&tpe, spanner_value(kind.clone())).unwrap();
278        assert_eq!(value, expected);
279    }
280
281    fn assert_try_into(from: Value, expected: Kind) {
282        let value: SpannerValue = from.try_into().unwrap();
283        assert_eq!(value, spanner_value(expected));
284    }
285
286    fn assert_try_from_into(tpe: Type, spanner: Kind, value: Value) {
287        assert_try_from(tpe, spanner.clone(), value.clone());
288        assert_try_into(value, spanner);
289    }
290
291    fn assert_nullable(tpe: Type) {
292        assert_try_from(
293            tpe.clone(),
294            Kind::NullValue(tpe.code() as i32),
295            Value::Null(tpe),
296        );
297    }
298
299    fn assert_invalid(tpe: Type, kind: Kind) {
300        let value = Value::try_from(&tpe, spanner_value(kind));
301        assert!(value.is_err(), "unexpected Ok");
302    }
303
304    #[test]
305    fn test_value_array() {
306        assert_try_from_into(
307            Type::Array(Box::new(Type::Bool)),
308            Kind::ListValue(ListValue {
309                values: vec![
310                    spanner_value(Kind::BoolValue(true)),
311                    spanner_value(Kind::BoolValue(false)),
312                ],
313            }),
314            Value::Array(Type::Bool, vec![Value::Bool(true), Value::Bool(false)]),
315        );
316        assert_nullable(Type::Array(Box::new(Type::Bool)));
317        assert_invalid(Type::Array(Box::new(Type::Bool)), Kind::BoolValue(true));
318    }
319
320    #[test]
321    fn test_value_bool() {
322        assert_try_from_into(Type::Bool, Kind::BoolValue(true), Value::Bool(true));
323        assert_try_from_into(Type::Bool, Kind::BoolValue(false), Value::Bool(false));
324        assert_nullable(Type::Bool);
325        assert_invalid(Type::Bool, Kind::NumberValue(6.0));
326    }
327
328    #[test]
329    fn test_value_bytes() {
330        assert_try_from_into(
331            Type::Bytes,
332            Kind::StringValue(base64::encode(vec![1, 2, 3, 4])),
333            Value::Bytes(Bytes::from(vec![1, 2, 3, 4])),
334        );
335        assert_try_from_into(
336            Type::Bytes,
337            Kind::StringValue(String::new()),
338            Value::Bytes(Bytes::new()),
339        );
340        assert_nullable(Type::Bytes);
341        assert_invalid(Type::Bytes, Kind::NumberValue(6.0));
342    }
343
344    #[cfg(feature = "temporal")]
345    #[test]
346    fn test_value_date() {
347        use chrono::NaiveDate;
348        assert_try_from_into(
349            Type::Date,
350            Kind::StringValue("2021-10-01".to_string()),
351            Value::Date(NaiveDate::from_ymd(2021, 10, 1)),
352        );
353        assert_nullable(Type::Date);
354        assert_invalid(Type::Date, Kind::BoolValue(true));
355    }
356
357    #[test]
358    fn test_value_float64() {
359        assert_try_from_into(Type::Float64, Kind::NumberValue(42.0), Value::Float64(42.0));
360
361        assert_try_from_into(
362            Type::Float64,
363            Kind::NumberValue(f64::MAX),
364            Value::Float64(f64::MAX),
365        );
366        assert_try_from_into(
367            Type::Float64,
368            Kind::NumberValue(f64::MIN),
369            Value::Float64(f64::MIN),
370        );
371        assert_try_from_into(
372            Type::Float64,
373            Kind::NumberValue(f64::NEG_INFINITY),
374            Value::Float64(f64::NEG_INFINITY),
375        );
376        assert_try_from_into(
377            Type::Float64,
378            Kind::NumberValue(f64::INFINITY),
379            Value::Float64(f64::INFINITY),
380        );
381        assert_nullable(Type::Float64);
382        assert_invalid(Type::Float64, Kind::BoolValue(true));
383        assert_invalid(
384            Type::Float64,
385            Kind::StringValue("this is not a number".to_string()),
386        );
387    }
388
389    #[test]
390    fn test_value_int64() {
391        assert_try_from_into(
392            Type::Int64,
393            Kind::StringValue("42".to_string()),
394            Value::Int64(42),
395        );
396        assert_try_from_into(
397            Type::Int64,
398            Kind::StringValue(i64::MAX.to_string()),
399            Value::Int64(i64::MAX),
400        );
401        assert_try_from_into(
402            Type::Int64,
403            Kind::StringValue(i64::MIN.to_string()),
404            Value::Int64(i64::MIN),
405        );
406        assert_nullable(Type::Int64);
407        assert_invalid(Type::Int64, Kind::NumberValue(6.0));
408        assert_invalid(Type::Int64, Kind::StringValue(f64::MAX.to_string()));
409        assert_invalid(Type::Int64, Kind::StringValue(u64::MAX.to_string()));
410        assert_invalid(
411            Type::Int64,
412            Kind::StringValue("this is not a number".to_string()),
413        );
414    }
415
416    #[cfg(feature = "json")]
417    #[test]
418    fn test_value_json() {
419        use serde_json::json;
420
421        assert_try_from(
422            Type::Json,
423            Kind::StringValue(r#"{"foo": "bar", "baz": [1, 2, 3], "qux": true}"#.to_string()),
424            Value::Json(json!({"foo": "bar", "baz": [1,2,3], "qux": true})),
425        );
426        assert_try_into(
427            Value::Json(json!({"foo": { "foobar": "baz" }, "bar": null, "qux": true})),
428            Kind::StringValue(r#"{"bar":null,"foo":{"foobar":"baz"},"qux":true}"#.to_string()),
429        );
430        assert_nullable(Type::Json);
431        assert_invalid(Type::Json, Kind::BoolValue(true));
432    }
433
434    #[cfg(feature = "numeric")]
435    #[test]
436    fn test_value_numeric() {
437        assert_try_from_into(
438            Type::Numeric,
439            Kind::StringValue(
440                "987654321098765432109876543210.987654321098765432109876543210".to_string(),
441            ),
442            Value::Numeric(
443                BigDecimal::parse_bytes(
444                    "987654321098765432109876543210.987654321098765432109876543210".as_bytes(),
445                    10,
446                )
447                .unwrap(),
448            ),
449        );
450        assert_try_from(
451            Type::Numeric,
452            Kind::StringValue("1e-24".to_string()),
453            Value::Numeric(BigDecimal::parse_bytes("1e-24".as_bytes(), 10).unwrap()),
454        );
455        assert_try_into(
456            Value::Numeric(BigDecimal::parse_bytes("1e-24".as_bytes(), 10).unwrap()),
457            Kind::StringValue("0.000000000000000000000001".to_string()),
458        );
459        assert_nullable(Type::Numeric);
460        assert_invalid(Type::Numeric, Kind::NumberValue(6.0));
461        assert_invalid(
462            Type::Numeric,
463            Kind::StringValue("this is not a number".to_string()),
464        );
465    }
466
467    #[test]
468    fn test_value_string() {
469        assert_try_from_into(
470            Type::String,
471            Kind::StringValue("this is a string".to_string()),
472            Value::String("this is a string".to_string()),
473        );
474        assert_nullable(Type::String);
475        assert_invalid(Type::String, Kind::BoolValue(true));
476    }
477
478    #[test]
479    fn test_value_struct() {
480        let test_tpe = Type::strct(vec![
481            ("bool", Type::Bool),
482            ("int64", Type::Int64),
483            ("string", Type::String),
484            ("null", Type::Float64),
485        ]);
486        assert_try_from_into(
487            test_tpe.clone(),
488            Kind::ListValue(ListValue {
489                values: vec![
490                    spanner_value(Kind::BoolValue(true)),
491                    spanner_value(Kind::StringValue("42".to_string())),
492                    spanner_value(Kind::StringValue("this is a string".to_string())),
493                    spanner_value(Kind::NullValue(Type::Float64.code() as i32)),
494                ],
495            }),
496            Value::Struct(Struct(
497                StructType::new(vec![
498                    ("bool", Type::Bool),
499                    ("int64", Type::Int64),
500                    ("string", Type::String),
501                    ("null", Type::Float64),
502                ]),
503                vec![
504                    Value::Bool(true),
505                    Value::Int64(42),
506                    Value::String("this is a string".to_string()),
507                    Value::Null(Type::Float64),
508                ],
509            )),
510        );
511        assert_nullable(test_tpe.clone());
512        assert_invalid(test_tpe, Kind::BoolValue(true));
513    }
514
515    #[cfg(feature = "temporal")]
516    #[test]
517    fn test_value_timestamp() {
518        assert_try_from_into(
519            Type::Timestamp,
520            Kind::StringValue("2021-10-01T20:56:34.756433987Z".to_string()),
521            Value::Timestamp(DateTime::<Utc>::from_utc(
522                NaiveDate::from_ymd(2021, 10, 1).and_hms_nano(20, 56, 34, 756_433_987),
523                Utc,
524            )),
525        );
526        assert_nullable(Type::Timestamp);
527        assert_invalid(Type::Timestamp, Kind::BoolValue(true));
528    }
529}