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#[derive(Clone, Debug, Default, PartialEq)]
20pub struct Struct(StructType, Vec<Value>);
21
22impl Struct {
23 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 pub fn struct_type(&self) -> &StructType {
41 &self.0
42 }
43
44 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#[derive(Debug, Clone, PartialEq)]
69pub enum Value {
70 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 }
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}