Skip to main content

google_cloud_spanner/
from_value.rs

1// Copyright 2026 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15pub use crate::types::{Type, TypeCode};
16use crate::value::Kind;
17use crate::value::Value;
18use base64::Engine;
19use base64::prelude::BASE64_STANDARD;
20use rust_decimal::Decimal;
21use std::time::SystemTime;
22use time::{Date, OffsetDateTime};
23
24/// Represent failures in converting a Spanner Value to a Rust type.
25#[derive(thiserror::Error, Debug)]
26#[non_exhaustive]
27pub enum ConvertError {
28    /// The value kind is not as expected.
29    #[error("expected {want:?}, got {got:?}")]
30    KindMismatch {
31        /// The expected Spanner value kind.
32        want: Kind,
33        /// The actual Spanner value kind.
34        got: Kind,
35    },
36
37    /// The value is null, but the target type does not support nulls.
38    #[error("expected non-null value, got null")]
39    NotNull,
40
41    /// There was a problem during conversion.
42    #[error("cannot convert value, source={0}")]
43    Convert(#[source] BoxedError),
44}
45
46type BoxedError = Box<dyn std::error::Error + Send + Sync>;
47
48/// Converts a Spanner [Value] into a Rust type.
49///
50/// Implementations are provided for all standard types like `String`, primitive integer
51/// and float types, decimals, timestamps, dates, vectors, and options for nullable fields.
52pub trait FromValue: Sized {
53    /// Converts a Spanner value into the target Rust type, using the provided
54    /// Spanner `Type` metadata for compatibility checks.
55    ///
56    /// # Errors
57    ///
58    /// Returns a [`ConvertError`] if the kind of the value does not match the expected kind,
59    /// if the value is null but the target type is not optional (e.g., `Option<T>`), or if
60    /// parsing or decoding the inner value format fails.
61    fn from_value(value: &Value, type_: &Type) -> Result<Self, ConvertError>;
62}
63
64impl<T> FromValue for Option<T>
65where
66    T: FromValue,
67{
68    fn from_value(value: &Value, type_: &Type) -> Result<Self, ConvertError> {
69        match &value.0.kind {
70            Some(prost_types::value::Kind::NullValue(_)) => Ok(None),
71            _ => T::from_value(value, type_).map(Some),
72        }
73    }
74}
75
76impl FromValue for Value {
77    fn from_value(value: &Value, _type: &Type) -> Result<Self, ConvertError> {
78        Ok(value.clone())
79    }
80}
81
82impl FromValue for String {
83    fn from_value(value: &Value, _type: &Type) -> Result<Self, ConvertError> {
84        match &value.0.kind {
85            Some(prost_types::value::Kind::StringValue(s)) => Ok(s.clone()),
86            Some(prost_types::value::Kind::NullValue(_)) => Err(ConvertError::NotNull),
87            _ => Err(ConvertError::KindMismatch {
88                want: crate::value::Kind::String,
89                got: value.kind(),
90            }),
91        }
92    }
93}
94
95impl FromValue for i64 {
96    fn from_value(value: &Value, _type: &Type) -> Result<Self, ConvertError> {
97        match &value.0.kind {
98            Some(prost_types::value::Kind::StringValue(s)) => {
99                s.parse().map_err(|e| ConvertError::Convert(Box::new(e)))
100            }
101            Some(prost_types::value::Kind::NullValue(_)) => Err(ConvertError::NotNull),
102            _ => Err(ConvertError::KindMismatch {
103                want: crate::value::Kind::String,
104                got: value.kind(),
105            }),
106        }
107    }
108}
109
110impl FromValue for i32 {
111    fn from_value(value: &Value, _type: &Type) -> Result<Self, ConvertError> {
112        match &value.0.kind {
113            Some(prost_types::value::Kind::StringValue(s)) => {
114                s.parse().map_err(|e| ConvertError::Convert(Box::new(e)))
115            }
116            Some(prost_types::value::Kind::NullValue(_)) => Err(ConvertError::NotNull),
117            _ => Err(ConvertError::KindMismatch {
118                want: crate::value::Kind::String,
119                got: value.kind(),
120            }),
121        }
122    }
123}
124
125impl FromValue for Decimal {
126    fn from_value(value: &Value, type_: &Type) -> Result<Self, ConvertError> {
127        if type_.code() != TypeCode::Numeric {
128            return Err(ConvertError::KindMismatch {
129                want: crate::value::Kind::String,
130                got: value.kind(),
131            });
132        }
133        match &value.0.kind {
134            Some(prost_types::value::Kind::StringValue(s)) => {
135                Decimal::from_str_exact(s).map_err(|e| ConvertError::Convert(Box::new(e)))
136            }
137            Some(prost_types::value::Kind::NullValue(_)) => Err(ConvertError::NotNull),
138            _ => Err(ConvertError::KindMismatch {
139                want: crate::value::Kind::String,
140                got: value.kind(),
141            }),
142        }
143    }
144}
145
146impl FromValue for SystemTime {
147    fn from_value(value: &Value, type_: &Type) -> Result<Self, ConvertError> {
148        if type_.code() != TypeCode::Timestamp {
149            return Err(ConvertError::KindMismatch {
150                want: crate::value::Kind::String,
151                got: value.kind(),
152            });
153        }
154        match &value.0.kind {
155            Some(prost_types::value::Kind::StringValue(s)) => {
156                let dt = OffsetDateTime::parse(s, &time::format_description::well_known::Rfc3339)
157                    .map_err(|e| ConvertError::Convert(Box::new(e)))?;
158                Ok(dt.into())
159            }
160            Some(prost_types::value::Kind::NullValue(_)) => Err(ConvertError::NotNull),
161            _ => Err(ConvertError::KindMismatch {
162                want: crate::value::Kind::String,
163                got: value.kind(),
164            }),
165        }
166    }
167}
168
169impl FromValue for OffsetDateTime {
170    fn from_value(value: &Value, type_: &Type) -> Result<Self, ConvertError> {
171        if type_.code() != TypeCode::Timestamp {
172            return Err(ConvertError::KindMismatch {
173                want: crate::value::Kind::String,
174                got: value.kind(),
175            });
176        }
177        match &value.0.kind {
178            Some(prost_types::value::Kind::StringValue(s)) => {
179                let dt = OffsetDateTime::parse(s, &time::format_description::well_known::Rfc3339)
180                    .map_err(|e| ConvertError::Convert(Box::new(e)))?;
181                Ok(dt)
182            }
183            Some(prost_types::value::Kind::NullValue(_)) => Err(ConvertError::NotNull),
184            _ => Err(ConvertError::KindMismatch {
185                want: crate::value::Kind::String,
186                got: value.kind(),
187            }),
188        }
189    }
190}
191
192impl FromValue for wkt::Timestamp {
193    fn from_value(value: &Value, type_: &Type) -> Result<Self, ConvertError> {
194        let dt = OffsetDateTime::from_value(value, type_)?;
195        wkt::Timestamp::try_from(dt).map_err(|e| ConvertError::Convert(Box::new(e)))
196    }
197}
198
199impl FromValue for Date {
200    fn from_value(value: &Value, type_: &Type) -> Result<Self, ConvertError> {
201        if type_.code() != TypeCode::Date {
202            return Err(ConvertError::KindMismatch {
203                want: crate::value::Kind::String,
204                got: value.kind(),
205            });
206        }
207        match &value.0.kind {
208            Some(prost_types::value::Kind::StringValue(s)) => {
209                let date = Date::parse(s, crate::value::SPANNER_DATE_FORMAT)
210                    .map_err(|e| ConvertError::Convert(Box::new(e)))?;
211                Ok(date)
212            }
213            Some(prost_types::value::Kind::NullValue(_)) => Err(ConvertError::NotNull),
214            _ => Err(ConvertError::KindMismatch {
215                want: crate::value::Kind::String,
216                got: value.kind(),
217            }),
218        }
219    }
220}
221
222impl FromValue for bool {
223    fn from_value(value: &Value, _type: &Type) -> Result<Self, ConvertError> {
224        match &value.0.kind {
225            Some(prost_types::value::Kind::BoolValue(b)) => Ok(*b),
226            Some(prost_types::value::Kind::NullValue(_)) => Err(ConvertError::NotNull),
227            _ => Err(ConvertError::KindMismatch {
228                want: crate::value::Kind::Bool,
229                got: value.kind(),
230            }),
231        }
232    }
233}
234
235impl FromValue for f64 {
236    fn from_value(value: &Value, _type: &Type) -> Result<Self, ConvertError> {
237        match &value.0.kind {
238            Some(prost_types::value::Kind::NumberValue(n)) => Ok(*n),
239            Some(prost_types::value::Kind::StringValue(s)) => {
240                s.parse().map_err(|e| ConvertError::Convert(Box::new(e)))
241            }
242            Some(prost_types::value::Kind::NullValue(_)) => Err(ConvertError::NotNull),
243            _ => Err(ConvertError::KindMismatch {
244                want: crate::value::Kind::Number,
245                got: value.kind(),
246            }),
247        }
248    }
249}
250
251impl FromValue for f32 {
252    fn from_value(value: &Value, _type: &Type) -> Result<Self, ConvertError> {
253        match &value.0.kind {
254            Some(prost_types::value::Kind::NumberValue(n)) => Ok(*n as f32),
255            Some(prost_types::value::Kind::StringValue(s)) => {
256                s.parse().map_err(|e| ConvertError::Convert(Box::new(e)))
257            }
258            Some(prost_types::value::Kind::NullValue(_)) => Err(ConvertError::NotNull),
259            _ => Err(ConvertError::KindMismatch {
260                want: crate::value::Kind::Number,
261                got: value.kind(),
262            }),
263        }
264    }
265}
266
267impl FromValue for Vec<u8> {
268    fn from_value(value: &Value, type_: &Type) -> Result<Self, ConvertError> {
269        if type_.code() != TypeCode::Bytes && type_.code() != TypeCode::Proto {
270            return Err(ConvertError::KindMismatch {
271                want: crate::value::Kind::String,
272                got: value.kind(),
273            });
274        }
275        match &value.0.kind {
276            Some(prost_types::value::Kind::StringValue(s)) => BASE64_STANDARD
277                .decode(s)
278                .map_err(|e| ConvertError::Convert(Box::new(e))),
279            Some(prost_types::value::Kind::NullValue(_)) => Err(ConvertError::NotNull),
280            _ => Err(ConvertError::KindMismatch {
281                want: crate::value::Kind::String,
282                got: value.kind(),
283            }),
284        }
285    }
286}
287
288impl<T> FromValue for Vec<T>
289where
290    T: FromValue,
291{
292    fn from_value(value: &Value, r#type: &Type) -> Result<Self, ConvertError> {
293        if r#type.code() != TypeCode::Array {
294            return Err(ConvertError::KindMismatch {
295                want: crate::value::Kind::List,
296                got: value.kind(),
297            });
298        }
299        let element_type = r#type
300            .array_element_type()
301            .ok_or_else(|| ConvertError::Convert("Array type missing element type".into()))?;
302
303        match &value.0.kind {
304            Some(prost_types::value::Kind::ListValue(list)) => {
305                let mut vec = Vec::with_capacity(list.values.len());
306                for v in &list.values {
307                    // `Value` is a `#[repr(transparent)]` wrapper around `ProtoValue`.
308                    // We use `from_ref` to safely cast the pointer and avoid cloning elements.
309                    let val = crate::value::Value::from_ref(v);
310                    vec.push(T::from_value(val, &element_type)?);
311                }
312                Ok(vec)
313            }
314            Some(prost_types::value::Kind::NullValue(_)) => Err(ConvertError::NotNull),
315            _ => Err(ConvertError::KindMismatch {
316                want: crate::value::Kind::List,
317                got: value.kind(),
318            }),
319        }
320    }
321}
322
323#[cfg(test)]
324mod tests {
325    use super::*;
326    use crate::to_value::ToValue;
327    use crate::types;
328
329    #[test]
330    fn test_from_value_string() {
331        let v = "hello".to_value();
332        let s = String::from_value(&v, &types::string()).unwrap();
333        assert_eq!(s, "hello");
334    }
335
336    #[test]
337    fn test_from_value_int() {
338        let v = 42i64.to_value();
339        let i = i64::from_value(&v, &types::int64()).unwrap();
340        assert_eq!(i, 42);
341
342        let v = 42i32.to_value();
343        let i = i32::from_value(&v, &types::int64()).unwrap();
344        assert_eq!(i, 42);
345
346        // Negative tests
347        let v = "not an int".to_value();
348        let err = i64::from_value(&v, &types::int64()).unwrap_err();
349        assert!(format!("{}", err).contains("cannot convert value"));
350
351        let v = "not an int".to_value();
352        let err = i32::from_value(&v, &types::int64()).unwrap_err();
353        assert!(format!("{}", err).contains("cannot convert value"));
354    }
355
356    #[test]
357    fn test_from_value_float() {
358        let v = 42.5f64.to_value();
359        let f = f64::from_value(&v, &types::float64()).unwrap();
360        assert_eq!(f, 42.5);
361
362        let v = "Infinity".to_string().to_value();
363        let f = f64::from_value(&v, &types::float64()).unwrap();
364        assert_eq!(f, f64::INFINITY);
365
366        let v = "invalid float".to_string().to_value();
367        let err = f64::from_value(&v, &types::float64()).unwrap_err();
368        assert!(format!("{}", err).contains("invalid float literal"));
369    }
370
371    #[test]
372    fn test_from_value_bool() {
373        let v = true.to_value();
374        let b = bool::from_value(&v, &types::bool()).unwrap();
375        assert!(b);
376    }
377
378    #[test]
379    fn test_from_value_array() {
380        // String array
381        let str_array = vec!["one".to_string(), "two".to_string()];
382        let v = str_array.to_value();
383        let res = Vec::<String>::from_value(&v, &types::array(types::string()))
384            .expect("parsed string array");
385        assert_eq!(res, str_array);
386
387        // Int array
388        let int_array = vec![42i64, 100i64];
389        let v = int_array.to_value();
390        let res =
391            Vec::<i64>::from_value(&v, &types::array(types::int64())).expect("parsed int array");
392        assert_eq!(res, int_array);
393
394        // Bool array
395        let bool_array = vec![true, false];
396        let v = bool_array.to_value();
397        let res =
398            Vec::<bool>::from_value(&v, &types::array(types::bool())).expect("parsed bool array");
399        assert_eq!(res, bool_array);
400
401        // Float array
402        let float_array = vec![9.9f64, -2.5f64];
403        let v = float_array.to_value();
404        let res = Vec::<f64>::from_value(&v, &types::array(types::float64()))
405            .expect("parsed float array");
406        assert_eq!(res, float_array);
407
408        // Empty array
409        let empty_array: Vec<f64> = vec![];
410        let v = empty_array.to_value();
411        let res = Vec::<f64>::from_value(&v, &types::array(types::float64()))
412            .expect("parsed empty array");
413        assert_eq!(res, empty_array);
414
415        // Array with nulls
416        let opt_array: Vec<Option<i64>> = vec![Some(42), None, Some(100)];
417        let v = opt_array.to_value();
418        let res = Vec::<Option<i64>>::from_value(&v, &types::array(types::int64()))
419            .expect("parsed optional array");
420        assert_eq!(res, opt_array);
421
422        // Null array entirely
423        let null_array: Option<Vec<i64>> = None;
424        let v = null_array.to_value();
425        let res = Option::<Vec<i64>>::from_value(&v, &types::array(types::int64()))
426            .expect("parsed null array");
427        assert_eq!(res, null_array);
428
429        // Wrong TypeCode test
430        let err = Vec::<i64>::from_value(&int_array.to_value(), &types::int64()).unwrap_err();
431        assert!(format!("{}", err).contains("expected List"));
432
433        // Invalid array element values
434        let err = Vec::<i64>::from_value(&str_array.to_value(), &types::array(types::int64()))
435            .unwrap_err();
436        assert!(format!("{}", err).contains("cannot convert value, source="));
437    }
438
439    #[test]
440    fn test_from_value_bytes() {
441        let bytes: Vec<u8> = vec![1, 2, 3];
442        let v = bytes.to_value();
443        let b = Vec::<u8>::from_value(&v, &types::bytes()).unwrap();
444        assert_eq!(b, bytes);
445
446        let v = "invalid base64".to_string().to_value();
447        let err = Vec::<u8>::from_value(&v, &types::bytes()).unwrap_err();
448        assert!(format!("{}", err).contains("cannot convert value"));
449    }
450
451    #[test]
452    fn test_from_value_decimal() {
453        let d = Decimal::from_str_exact("123.456").unwrap();
454        let v = d.to_value();
455        let res = Decimal::from_value(&v, &types::numeric()).unwrap();
456        assert_eq!(res, d);
457
458        let v = "invalid decimal".to_string().to_value();
459        let err = Decimal::from_value(&v, &types::numeric()).unwrap_err();
460        assert!(format!("{}", err).contains("cannot convert value"));
461    }
462
463    #[test]
464    fn test_from_value_date() {
465        let d = Date::from_calendar_date(2023, time::Month::October, 27).unwrap();
466        let v = d.to_value();
467        let res = Date::from_value(&v, &types::date()).unwrap();
468        assert_eq!(res, d);
469
470        let v = "invalid date".to_string().to_value();
471        let err = Date::from_value(&v, &types::date()).unwrap_err();
472        assert!(format!("{}", err).contains("cannot convert value"));
473    }
474
475    #[test]
476    fn test_from_value_timestamp() {
477        let dt = OffsetDateTime::parse(
478            "2023-10-27T10:00:00Z",
479            &time::format_description::well_known::Rfc3339,
480        )
481        .unwrap();
482        let v = dt.to_value();
483        let res = OffsetDateTime::from_value(&v, &types::timestamp()).unwrap();
484        assert_eq!(res, dt);
485
486        let v = "invalid timestamp".to_string().to_value();
487        let err = OffsetDateTime::from_value(&v, &types::timestamp()).unwrap_err();
488        assert!(format!("{}", err).contains("cannot convert value"));
489    }
490
491    #[test]
492    fn test_from_value_null() {
493        let v = Option::<i32>::None.to_value();
494        let res = Option::<i32>::from_value(&v, &types::int64()).unwrap();
495        assert_eq!(res, None);
496
497        let v = Option::<i32>::None.to_value();
498        let err = i32::from_value(&v, &types::int64()).unwrap_err();
499        assert!(format!("{}", err).contains("expected non-null value, got null"));
500    }
501    #[test]
502    fn test_from_value_system_time() {
503        let dt = OffsetDateTime::parse(
504            "2023-10-27T10:00:00Z",
505            &time::format_description::well_known::Rfc3339,
506        )
507        .unwrap();
508        let system_time: SystemTime = dt.into();
509        let v = system_time.to_value();
510        let res = SystemTime::from_value(&v, &types::timestamp()).unwrap();
511        let res_dt: OffsetDateTime = res.into();
512        assert_eq!(res_dt, dt);
513
514        let v = "invalid timestamp".to_string().to_value();
515        let err = SystemTime::from_value(&v, &types::timestamp()).unwrap_err();
516        assert!(format!("{}", err).contains("cannot convert value"));
517    }
518
519    #[test]
520    fn test_from_value_wkt_timestamp() {
521        let dt = OffsetDateTime::parse(
522            "2023-10-27T10:00:00Z",
523            &time::format_description::well_known::Rfc3339,
524        )
525        .expect("valid date time parsing");
526        let wkt_ts = wkt::Timestamp::try_from(dt).expect("valid wkt timestamp conversion");
527        let v = dt.to_value();
528        let res = wkt::Timestamp::from_value(&v, &types::timestamp())
529            .expect("valid wkt timestamp decoding");
530        assert_eq!(res, wkt_ts);
531
532        let v = "invalid timestamp".to_string().to_value();
533        let err = wkt::Timestamp::from_value(&v, &types::timestamp()).unwrap_err();
534        assert!(format!("{}", err).contains("cannot convert value"));
535    }
536
537    #[test]
538    fn test_from_value_type_mismatch() {
539        let v = Decimal::from(42).to_value();
540        let err = Decimal::from_value(&v, &types::int64()).unwrap_err();
541        assert!(format!("{}", err).contains("expected String, got String"));
542
543        let v = SystemTime::now().to_value();
544        let err = SystemTime::from_value(&v, &types::string()).unwrap_err();
545        assert!(format!("{}", err).contains("expected String, got String")); // This might require adjustment as logic changed. In `SystemTime::from_value`, we check TypeCode first.
546
547        let v = OffsetDateTime::now_utc().to_value();
548        let err = OffsetDateTime::from_value(&v, &types::string()).unwrap_err();
549        assert!(format!("{}", err).contains("expected String, got String"));
550
551        let v = Date::from_calendar_date(2023, time::Month::October, 27)
552            .unwrap()
553            .to_value();
554        let err = Date::from_value(&v, &types::string()).unwrap_err();
555        assert!(format!("{}", err).contains("expected String, got String"));
556
557        let v = vec![1u8].to_value();
558        let err = Vec::<u8>::from_value(&v, &types::string()).unwrap_err();
559        assert!(format!("{}", err).contains("expected String, got String"));
560    }
561
562    #[test]
563    fn test_from_value_wrong_kind() {
564        let v_bool = true.to_value();
565        let err = String::from_value(&v_bool, &types::string()).unwrap_err();
566        assert!(format!("{}", err).contains("expected String, got Bool"));
567
568        let v_string = "hello".to_value();
569        let err = i64::from_value(&v_string, &types::int64()).unwrap_err();
570        assert!(format!("{}", err).contains("cannot convert value"));
571
572        let v_struct = crate::value::Value(prost_types::Value {
573            kind: Some(prost_types::value::Kind::StructValue(
574                prost_types::Struct::default(),
575            )),
576        });
577        let err = i64::from_value(&v_struct, &types::int64()).unwrap_err();
578        assert!(format!("{}", err).contains("expected String, got Struct"));
579
580        let err = f64::from_value(&v_bool, &types::float64()).unwrap_err();
581        assert!(format!("{}", err).contains("expected Number, got Bool"));
582
583        let err = bool::from_value(&v_string, &types::bool()).unwrap_err();
584        assert!(format!("{}", err).contains("expected Bool, got String"));
585    }
586
587    #[test]
588    fn test_from_value_null_errors() {
589        let v_null = Option::<i32>::None.to_value();
590
591        let err = String::from_value(&v_null, &types::string()).unwrap_err();
592        assert!(format!("{}", err).contains("expected non-null value, got null"));
593
594        let err = i64::from_value(&v_null, &types::int64()).unwrap_err();
595        assert!(format!("{}", err).contains("expected non-null value, got null"));
596
597        let err = f64::from_value(&v_null, &types::float64()).unwrap_err();
598        assert!(format!("{}", err).contains("expected non-null value, got null"));
599
600        let err = f32::from_value(&v_null, &types::float32()).unwrap_err();
601        assert!(format!("{}", err).contains("expected non-null value, got null"));
602
603        let err = bool::from_value(&v_null, &types::bool()).unwrap_err();
604        assert!(format!("{}", err).contains("expected non-null value, got null"));
605
606        let err = Decimal::from_value(&v_null, &types::numeric()).unwrap_err();
607        assert!(format!("{}", err).contains("expected non-null value, got null"));
608
609        let err = SystemTime::from_value(&v_null, &types::timestamp()).unwrap_err();
610        assert!(format!("{}", err).contains("expected non-null value, got null"));
611
612        let err = OffsetDateTime::from_value(&v_null, &types::timestamp()).unwrap_err();
613        assert!(format!("{}", err).contains("expected non-null value, got null"));
614
615        let err = Date::from_value(&v_null, &types::date()).unwrap_err();
616        assert!(format!("{}", err).contains("expected non-null value, got null"));
617
618        let err = Vec::<u8>::from_value(&v_null, &types::bytes()).unwrap_err();
619        assert!(format!("{}", err).contains("expected non-null value, got null"));
620    }
621
622    #[test]
623    fn test_from_value_option_missing_kind() {
624        let v = crate::value::Value(prost_types::Value { kind: None });
625        let err = Option::<i32>::from_value(&v, &types::int64()).unwrap_err();
626        assert!(format!("{}", err).contains("expected String, got Null"));
627    }
628}