pub use crate::types::{Type, TypeCode};
use crate::value::Kind;
use crate::value::Value;
use base64::Engine;
use base64::prelude::BASE64_STANDARD;
use rust_decimal::Decimal;
use serde_json::Value as JsonValue;
use std::time::SystemTime;
use time::{Date, OffsetDateTime};
#[derive(thiserror::Error, Debug)]
#[non_exhaustive]
pub enum ConvertError {
#[error("expected {want:?}, got {got:?}")]
KindMismatch {
want: Kind,
got: Kind,
},
#[error("expected non-null value, got null")]
NotNull,
#[error("cannot convert value, source={0}")]
Convert(#[source] BoxedError),
}
type BoxedError = Box<dyn std::error::Error + Send + Sync>;
pub trait FromValue: Sized {
fn from_value(value: &Value, type_: &Type) -> Result<Self, ConvertError>;
}
impl<T> FromValue for Option<T>
where
T: FromValue,
{
fn from_value(value: &Value, type_: &Type) -> Result<Self, ConvertError> {
match &value.0.kind {
Some(prost_types::value::Kind::NullValue(_)) => Ok(None),
_ => T::from_value(value, type_).map(Some),
}
}
}
impl FromValue for Value {
fn from_value(value: &Value, _type: &Type) -> Result<Self, ConvertError> {
Ok(value.clone())
}
}
impl FromValue for JsonValue {
fn from_value(value: &Value, type_: &Type) -> Result<Self, ConvertError> {
from_value_recursive(value, Some(type_), 0)
}
}
fn from_value_recursive(
value: &Value,
type_: Option<&Type>,
depth: usize,
) -> Result<JsonValue, ConvertError> {
const MAX_RECURSION_DEPTH: usize = 64;
if depth > MAX_RECURSION_DEPTH {
return Err(ConvertError::Convert(
"maximum nesting depth exceeded".into(),
));
}
match &value.0.kind {
Some(prost_types::value::Kind::NullValue(_)) => Ok(JsonValue::Null),
Some(prost_types::value::Kind::NumberValue(n)) => Ok(serde_json::Number::from_f64(*n)
.map(JsonValue::Number)
.unwrap_or(JsonValue::Null)),
Some(prost_types::value::Kind::StringValue(s)) => {
let Some(t) = type_ else {
return Ok(JsonValue::String(s.clone()));
};
match t.code() {
TypeCode::Json => {
serde_json::from_str(s).map_err(|e| ConvertError::Convert(Box::new(e)))
}
TypeCode::Float64 | TypeCode::Float32 => {
if s == "NaN" || s == "Infinity" || s == "-Infinity" {
return Ok(JsonValue::Null);
}
let f = s
.parse::<f64>()
.map_err(|e| ConvertError::Convert(Box::new(e)))?;
Ok(serde_json::Number::from_f64(f)
.map(JsonValue::Number)
.unwrap_or(JsonValue::Null))
}
_ => Ok(JsonValue::String(s.clone())),
}
}
Some(prost_types::value::Kind::BoolValue(b)) => Ok(JsonValue::Bool(*b)),
Some(prost_types::value::Kind::StructValue(s)) => struct_value_to_json(s, type_, depth + 1),
Some(prost_types::value::Kind::ListValue(list)) => {
list_value_to_json(list, type_, depth + 1)
}
None => Ok(JsonValue::Null),
}
}
fn struct_value_to_json(
s: &prost_types::Struct,
type_: Option<&Type>,
depth: usize,
) -> Result<JsonValue, ConvertError> {
let s = crate::value::Struct::from_ref(s);
let mut map = serde_json::Map::new();
let Some(struct_type) = type_.and_then(|t| t.struct_type()) else {
for (k, v) in s.fields() {
map.insert(k.clone(), from_value_recursive(v, None, depth)?);
}
return Ok(JsonValue::Object(map));
};
for field in &struct_type.fields {
let field_type = field.r#type.as_deref().map(Type::from_ref);
let value = if let Some(v) = s.get(&field.name) {
from_value_recursive(v, field_type, depth)?
} else {
JsonValue::Null
};
map.insert(field.name.clone(), value);
}
Ok(JsonValue::Object(map))
}
fn list_value_to_json(
list: &prost_types::ListValue,
type_: Option<&Type>,
depth: usize,
) -> Result<JsonValue, ConvertError> {
let code = type_.map_or(TypeCode::Unspecified, |t| t.code());
match code {
TypeCode::Struct => {
let Some(struct_type) = type_.and_then(|t| t.struct_type()) else {
let mut arr = Vec::with_capacity(list.values.len());
for v in &list.values {
let val = Value::from_ref(v);
arr.push(from_value_recursive(val, None, depth)?);
}
return Ok(JsonValue::Array(arr));
};
let mut map = serde_json::Map::new();
for (i, field) in struct_type.fields.iter().enumerate() {
let field_type = field.r#type.as_deref().map(Type::from_ref);
let value = if let Some(v) = list.values.get(i) {
let val = Value::from_ref(v);
from_value_recursive(val, field_type, depth)?
} else {
JsonValue::Null
};
map.insert(field.name.clone(), value);
}
Ok(JsonValue::Object(map))
}
TypeCode::Array => {
let element_type = type_.and_then(|t| t.array_element_type());
let mut arr = Vec::with_capacity(list.values.len());
for v in &list.values {
let val = Value::from_ref(v);
arr.push(from_value_recursive(val, element_type.as_ref(), depth)?);
}
Ok(JsonValue::Array(arr))
}
_ => {
let mut arr = Vec::with_capacity(list.values.len());
for v in &list.values {
let val = Value::from_ref(v);
arr.push(from_value_recursive(val, None, depth)?);
}
Ok(JsonValue::Array(arr))
}
}
}
impl FromValue for String {
fn from_value(value: &Value, _type: &Type) -> Result<Self, ConvertError> {
match &value.0.kind {
Some(prost_types::value::Kind::StringValue(s)) => Ok(s.clone()),
Some(prost_types::value::Kind::NullValue(_)) => Err(ConvertError::NotNull),
_ => Err(ConvertError::KindMismatch {
want: crate::value::Kind::String,
got: value.kind(),
}),
}
}
}
impl FromValue for i64 {
fn from_value(value: &Value, _type: &Type) -> Result<Self, ConvertError> {
match &value.0.kind {
Some(prost_types::value::Kind::StringValue(s)) => {
s.parse().map_err(|e| ConvertError::Convert(Box::new(e)))
}
Some(prost_types::value::Kind::NullValue(_)) => Err(ConvertError::NotNull),
_ => Err(ConvertError::KindMismatch {
want: crate::value::Kind::String,
got: value.kind(),
}),
}
}
}
impl FromValue for i32 {
fn from_value(value: &Value, _type: &Type) -> Result<Self, ConvertError> {
match &value.0.kind {
Some(prost_types::value::Kind::StringValue(s)) => {
s.parse().map_err(|e| ConvertError::Convert(Box::new(e)))
}
Some(prost_types::value::Kind::NullValue(_)) => Err(ConvertError::NotNull),
_ => Err(ConvertError::KindMismatch {
want: crate::value::Kind::String,
got: value.kind(),
}),
}
}
}
impl FromValue for Decimal {
fn from_value(value: &Value, type_: &Type) -> Result<Self, ConvertError> {
if type_.code() != TypeCode::Numeric {
return Err(ConvertError::KindMismatch {
want: crate::value::Kind::String,
got: value.kind(),
});
}
match &value.0.kind {
Some(prost_types::value::Kind::StringValue(s)) => {
Decimal::from_str_exact(s).map_err(|e| ConvertError::Convert(Box::new(e)))
}
Some(prost_types::value::Kind::NullValue(_)) => Err(ConvertError::NotNull),
_ => Err(ConvertError::KindMismatch {
want: crate::value::Kind::String,
got: value.kind(),
}),
}
}
}
impl FromValue for SystemTime {
fn from_value(value: &Value, type_: &Type) -> Result<Self, ConvertError> {
if type_.code() != TypeCode::Timestamp {
return Err(ConvertError::KindMismatch {
want: crate::value::Kind::String,
got: value.kind(),
});
}
match &value.0.kind {
Some(prost_types::value::Kind::StringValue(s)) => {
let dt = OffsetDateTime::parse(s, &time::format_description::well_known::Rfc3339)
.map_err(|e| ConvertError::Convert(Box::new(e)))?;
Ok(dt.into())
}
Some(prost_types::value::Kind::NullValue(_)) => Err(ConvertError::NotNull),
_ => Err(ConvertError::KindMismatch {
want: crate::value::Kind::String,
got: value.kind(),
}),
}
}
}
impl FromValue for OffsetDateTime {
fn from_value(value: &Value, type_: &Type) -> Result<Self, ConvertError> {
if type_.code() != TypeCode::Timestamp {
return Err(ConvertError::KindMismatch {
want: crate::value::Kind::String,
got: value.kind(),
});
}
match &value.0.kind {
Some(prost_types::value::Kind::StringValue(s)) => {
let dt = OffsetDateTime::parse(s, &time::format_description::well_known::Rfc3339)
.map_err(|e| ConvertError::Convert(Box::new(e)))?;
Ok(dt)
}
Some(prost_types::value::Kind::NullValue(_)) => Err(ConvertError::NotNull),
_ => Err(ConvertError::KindMismatch {
want: crate::value::Kind::String,
got: value.kind(),
}),
}
}
}
impl FromValue for wkt::Timestamp {
fn from_value(value: &Value, type_: &Type) -> Result<Self, ConvertError> {
let dt = OffsetDateTime::from_value(value, type_)?;
wkt::Timestamp::try_from(dt).map_err(|e| ConvertError::Convert(Box::new(e)))
}
}
impl FromValue for Date {
fn from_value(value: &Value, type_: &Type) -> Result<Self, ConvertError> {
if type_.code() != TypeCode::Date {
return Err(ConvertError::KindMismatch {
want: crate::value::Kind::String,
got: value.kind(),
});
}
match &value.0.kind {
Some(prost_types::value::Kind::StringValue(s)) => {
let date = Date::parse(s, crate::value::SPANNER_DATE_FORMAT)
.map_err(|e| ConvertError::Convert(Box::new(e)))?;
Ok(date)
}
Some(prost_types::value::Kind::NullValue(_)) => Err(ConvertError::NotNull),
_ => Err(ConvertError::KindMismatch {
want: crate::value::Kind::String,
got: value.kind(),
}),
}
}
}
impl FromValue for bool {
fn from_value(value: &Value, _type: &Type) -> Result<Self, ConvertError> {
match &value.0.kind {
Some(prost_types::value::Kind::BoolValue(b)) => Ok(*b),
Some(prost_types::value::Kind::NullValue(_)) => Err(ConvertError::NotNull),
_ => Err(ConvertError::KindMismatch {
want: crate::value::Kind::Bool,
got: value.kind(),
}),
}
}
}
impl FromValue for f64 {
fn from_value(value: &Value, _type: &Type) -> Result<Self, ConvertError> {
match &value.0.kind {
Some(prost_types::value::Kind::NumberValue(n)) => Ok(*n),
Some(prost_types::value::Kind::StringValue(s)) => {
s.parse().map_err(|e| ConvertError::Convert(Box::new(e)))
}
Some(prost_types::value::Kind::NullValue(_)) => Err(ConvertError::NotNull),
_ => Err(ConvertError::KindMismatch {
want: crate::value::Kind::Number,
got: value.kind(),
}),
}
}
}
impl FromValue for f32 {
fn from_value(value: &Value, _type: &Type) -> Result<Self, ConvertError> {
match &value.0.kind {
Some(prost_types::value::Kind::NumberValue(n)) => Ok(*n as f32),
Some(prost_types::value::Kind::StringValue(s)) => {
s.parse().map_err(|e| ConvertError::Convert(Box::new(e)))
}
Some(prost_types::value::Kind::NullValue(_)) => Err(ConvertError::NotNull),
_ => Err(ConvertError::KindMismatch {
want: crate::value::Kind::Number,
got: value.kind(),
}),
}
}
}
impl FromValue for Vec<u8> {
fn from_value(value: &Value, type_: &Type) -> Result<Self, ConvertError> {
if type_.code() != TypeCode::Bytes && type_.code() != TypeCode::Proto {
return Err(ConvertError::KindMismatch {
want: crate::value::Kind::String,
got: value.kind(),
});
}
match &value.0.kind {
Some(prost_types::value::Kind::StringValue(s)) => BASE64_STANDARD
.decode(s)
.map_err(|e| ConvertError::Convert(Box::new(e))),
Some(prost_types::value::Kind::NullValue(_)) => Err(ConvertError::NotNull),
_ => Err(ConvertError::KindMismatch {
want: crate::value::Kind::String,
got: value.kind(),
}),
}
}
}
impl<T> FromValue for Vec<T>
where
T: FromValue,
{
fn from_value(value: &Value, r#type: &Type) -> Result<Self, ConvertError> {
if r#type.code() != TypeCode::Array {
return Err(ConvertError::KindMismatch {
want: crate::value::Kind::List,
got: value.kind(),
});
}
let element_type = r#type
.array_element_type()
.ok_or_else(|| ConvertError::Convert("Array type missing element type".into()))?;
match &value.0.kind {
Some(prost_types::value::Kind::ListValue(list)) => {
let mut vec = Vec::with_capacity(list.values.len());
for v in &list.values {
let val = crate::value::Value::from_ref(v);
vec.push(T::from_value(val, &element_type)?);
}
Ok(vec)
}
Some(prost_types::value::Kind::NullValue(_)) => Err(ConvertError::NotNull),
_ => Err(ConvertError::KindMismatch {
want: crate::value::Kind::List,
got: value.kind(),
}),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::generated::gapic_dataplane::model as mdl;
use crate::to_value::ToValue;
use crate::types;
use serde_json::Value as JsonValue;
#[test]
fn test_from_value_string() {
let v = "hello".to_value();
let s = String::from_value(&v, &types::string()).unwrap();
assert_eq!(s, "hello");
}
#[test]
fn test_from_value_int() {
let v = 42i64.to_value();
let i = i64::from_value(&v, &types::int64()).unwrap();
assert_eq!(i, 42);
let v = 42i32.to_value();
let i = i32::from_value(&v, &types::int64()).unwrap();
assert_eq!(i, 42);
let v = "not an int".to_value();
let err = i64::from_value(&v, &types::int64()).unwrap_err();
assert!(format!("{}", err).contains("cannot convert value"));
let v = "not an int".to_value();
let err = i32::from_value(&v, &types::int64()).unwrap_err();
assert!(format!("{}", err).contains("cannot convert value"));
}
#[test]
fn test_from_value_float() {
let v = 42.5f64.to_value();
let f = f64::from_value(&v, &types::float64()).unwrap();
assert_eq!(f, 42.5);
let v = "Infinity".to_string().to_value();
let f = f64::from_value(&v, &types::float64()).unwrap();
assert_eq!(f, f64::INFINITY);
let v = "invalid float".to_string().to_value();
let err = f64::from_value(&v, &types::float64()).unwrap_err();
assert!(format!("{}", err).contains("invalid float literal"));
}
#[test]
fn test_from_value_bool() {
let v = true.to_value();
let b = bool::from_value(&v, &types::bool()).unwrap();
assert!(b);
}
#[test]
fn test_from_value_array() {
let str_array = vec!["one".to_string(), "two".to_string()];
let v = str_array.to_value();
let res = Vec::<String>::from_value(&v, &types::array(types::string()))
.expect("parsed string array");
assert_eq!(res, str_array);
let int_array = vec![42i64, 100i64];
let v = int_array.to_value();
let res =
Vec::<i64>::from_value(&v, &types::array(types::int64())).expect("parsed int array");
assert_eq!(res, int_array);
let bool_array = vec![true, false];
let v = bool_array.to_value();
let res =
Vec::<bool>::from_value(&v, &types::array(types::bool())).expect("parsed bool array");
assert_eq!(res, bool_array);
let float_array = vec![9.9f64, -2.5f64];
let v = float_array.to_value();
let res = Vec::<f64>::from_value(&v, &types::array(types::float64()))
.expect("parsed float array");
assert_eq!(res, float_array);
let empty_array: Vec<f64> = vec![];
let v = empty_array.to_value();
let res = Vec::<f64>::from_value(&v, &types::array(types::float64()))
.expect("parsed empty array");
assert_eq!(res, empty_array);
let opt_array: Vec<Option<i64>> = vec![Some(42), None, Some(100)];
let v = opt_array.to_value();
let res = Vec::<Option<i64>>::from_value(&v, &types::array(types::int64()))
.expect("parsed optional array");
assert_eq!(res, opt_array);
let null_array: Option<Vec<i64>> = None;
let v = null_array.to_value();
let res = Option::<Vec<i64>>::from_value(&v, &types::array(types::int64()))
.expect("parsed null array");
assert_eq!(res, null_array);
let err = Vec::<i64>::from_value(&int_array.to_value(), &types::int64()).unwrap_err();
assert!(format!("{}", err).contains("expected List"));
let err = Vec::<i64>::from_value(&str_array.to_value(), &types::array(types::int64()))
.unwrap_err();
assert!(format!("{}", err).contains("cannot convert value, source="));
}
#[test]
fn test_from_value_bytes() {
let bytes: Vec<u8> = vec![1, 2, 3];
let v = bytes.to_value();
let b = Vec::<u8>::from_value(&v, &types::bytes()).unwrap();
assert_eq!(b, bytes);
let v = "invalid base64".to_string().to_value();
let err = Vec::<u8>::from_value(&v, &types::bytes()).unwrap_err();
assert!(format!("{}", err).contains("cannot convert value"));
}
#[test]
fn test_from_value_decimal() {
let d = Decimal::from_str_exact("123.456").unwrap();
let v = d.to_value();
let res = Decimal::from_value(&v, &types::numeric()).unwrap();
assert_eq!(res, d);
let v = "invalid decimal".to_string().to_value();
let err = Decimal::from_value(&v, &types::numeric()).unwrap_err();
assert!(format!("{}", err).contains("cannot convert value"));
}
#[test]
fn test_from_value_date() {
let d = Date::from_calendar_date(2023, time::Month::October, 27).unwrap();
let v = d.to_value();
let res = Date::from_value(&v, &types::date()).unwrap();
assert_eq!(res, d);
let v = "invalid date".to_string().to_value();
let err = Date::from_value(&v, &types::date()).unwrap_err();
assert!(format!("{}", err).contains("cannot convert value"));
}
#[test]
fn test_from_value_timestamp() {
let dt = OffsetDateTime::parse(
"2023-10-27T10:00:00Z",
&time::format_description::well_known::Rfc3339,
)
.unwrap();
let v = dt.to_value();
let res = OffsetDateTime::from_value(&v, &types::timestamp()).unwrap();
assert_eq!(res, dt);
let v = "invalid timestamp".to_string().to_value();
let err = OffsetDateTime::from_value(&v, &types::timestamp()).unwrap_err();
assert!(format!("{}", err).contains("cannot convert value"));
}
#[test]
fn test_from_value_null() {
let v = Option::<i32>::None.to_value();
let res = Option::<i32>::from_value(&v, &types::int64()).unwrap();
assert_eq!(res, None);
let v = Option::<i32>::None.to_value();
let err = i32::from_value(&v, &types::int64()).unwrap_err();
assert!(format!("{}", err).contains("expected non-null value, got null"));
}
#[test]
fn test_from_value_system_time() {
let dt = OffsetDateTime::parse(
"2023-10-27T10:00:00Z",
&time::format_description::well_known::Rfc3339,
)
.unwrap();
let system_time: SystemTime = dt.into();
let v = system_time.to_value();
let res = SystemTime::from_value(&v, &types::timestamp()).unwrap();
let res_dt: OffsetDateTime = res.into();
assert_eq!(res_dt, dt);
let v = "invalid timestamp".to_string().to_value();
let err = SystemTime::from_value(&v, &types::timestamp()).unwrap_err();
assert!(format!("{}", err).contains("cannot convert value"));
}
#[test]
fn test_from_value_wkt_timestamp() {
let dt = OffsetDateTime::parse(
"2023-10-27T10:00:00Z",
&time::format_description::well_known::Rfc3339,
)
.expect("valid date time parsing");
let wkt_ts = wkt::Timestamp::try_from(dt).expect("valid wkt timestamp conversion");
let v = dt.to_value();
let res = wkt::Timestamp::from_value(&v, &types::timestamp())
.expect("valid wkt timestamp decoding");
assert_eq!(res, wkt_ts);
let v = "invalid timestamp".to_string().to_value();
let err = wkt::Timestamp::from_value(&v, &types::timestamp()).unwrap_err();
assert!(format!("{}", err).contains("cannot convert value"));
}
#[test]
fn test_from_value_type_mismatch() {
let v = Decimal::from(42).to_value();
let err = Decimal::from_value(&v, &types::int64()).unwrap_err();
assert!(format!("{}", err).contains("expected String, got String"));
let v = SystemTime::now().to_value();
let err = SystemTime::from_value(&v, &types::string()).unwrap_err();
assert!(format!("{}", err).contains("expected String, got String"));
let v = OffsetDateTime::now_utc().to_value();
let err = OffsetDateTime::from_value(&v, &types::string()).unwrap_err();
assert!(format!("{}", err).contains("expected String, got String"));
let v = Date::from_calendar_date(2023, time::Month::October, 27)
.unwrap()
.to_value();
let err = Date::from_value(&v, &types::string()).unwrap_err();
assert!(format!("{}", err).contains("expected String, got String"));
let v = vec![1u8].to_value();
let err = Vec::<u8>::from_value(&v, &types::string()).unwrap_err();
assert!(format!("{}", err).contains("expected String, got String"));
}
#[test]
fn test_from_value_wrong_kind() {
let v_bool = true.to_value();
let err = String::from_value(&v_bool, &types::string()).unwrap_err();
assert!(format!("{}", err).contains("expected String, got Bool"));
let v_string = "hello".to_value();
let err = i64::from_value(&v_string, &types::int64()).unwrap_err();
assert!(format!("{}", err).contains("cannot convert value"));
let v_struct = crate::value::Value(prost_types::Value {
kind: Some(prost_types::value::Kind::StructValue(
prost_types::Struct::default(),
)),
});
let err = i64::from_value(&v_struct, &types::int64()).unwrap_err();
assert!(format!("{}", err).contains("expected String, got Struct"));
let err = f64::from_value(&v_bool, &types::float64()).unwrap_err();
assert!(format!("{}", err).contains("expected Number, got Bool"));
let err = bool::from_value(&v_string, &types::bool()).unwrap_err();
assert!(format!("{}", err).contains("expected Bool, got String"));
}
#[test]
fn test_from_value_null_errors() {
let v_null = Option::<i32>::None.to_value();
let err = String::from_value(&v_null, &types::string()).unwrap_err();
assert!(format!("{}", err).contains("expected non-null value, got null"));
let err = i64::from_value(&v_null, &types::int64()).unwrap_err();
assert!(format!("{}", err).contains("expected non-null value, got null"));
let err = f64::from_value(&v_null, &types::float64()).unwrap_err();
assert!(format!("{}", err).contains("expected non-null value, got null"));
let err = f32::from_value(&v_null, &types::float32()).unwrap_err();
assert!(format!("{}", err).contains("expected non-null value, got null"));
let err = bool::from_value(&v_null, &types::bool()).unwrap_err();
assert!(format!("{}", err).contains("expected non-null value, got null"));
let err = Decimal::from_value(&v_null, &types::numeric()).unwrap_err();
assert!(format!("{}", err).contains("expected non-null value, got null"));
let err = SystemTime::from_value(&v_null, &types::timestamp()).unwrap_err();
assert!(format!("{}", err).contains("expected non-null value, got null"));
let err = OffsetDateTime::from_value(&v_null, &types::timestamp()).unwrap_err();
assert!(format!("{}", err).contains("expected non-null value, got null"));
let err = Date::from_value(&v_null, &types::date()).unwrap_err();
assert!(format!("{}", err).contains("expected non-null value, got null"));
let err = Vec::<u8>::from_value(&v_null, &types::bytes()).unwrap_err();
assert!(format!("{}", err).contains("expected non-null value, got null"));
}
#[test]
fn test_from_value_option_missing_kind() {
let v = crate::value::Value(prost_types::Value { kind: None });
let err = Option::<i32>::from_value(&v, &types::int64()).unwrap_err();
assert!(format!("{}", err).contains("expected String, got Null"));
}
#[test]
fn test_from_value_json_primitives() {
let v = "hello".to_value();
let j = JsonValue::from_value(&v, &types::string()).unwrap();
assert_eq!(j, JsonValue::String("hello".to_string()));
let v = 42i64.to_value();
let j = JsonValue::from_value(&v, &types::int64()).unwrap();
assert_eq!(j, JsonValue::String("42".to_string()));
let v = true.to_value();
let j = JsonValue::from_value(&v, &types::bool()).unwrap();
assert_eq!(j, JsonValue::Bool(true));
let v = 1.5f64.to_value();
let j = JsonValue::from_value(&v, &types::float64()).unwrap();
assert_eq!(j, serde_json::json!(1.5));
let v: Option<i64> = None;
let j = JsonValue::from_value(&v.to_value(), &types::int64()).unwrap();
assert_eq!(j, JsonValue::Null);
let v = crate::value::Value(prost_types::Value { kind: None });
let j = JsonValue::from_value(&v, &types::string()).unwrap();
assert_eq!(j, JsonValue::Null);
}
#[test]
fn test_from_value_json_string_array() {
let str_array = vec!["a".to_string(), "b".to_string()];
let v = str_array.to_value();
let j = JsonValue::from_value(&v, &types::array(types::string())).unwrap();
assert_eq!(j, serde_json::json!(["a", "b"]));
}
#[test]
fn test_from_value_json_int_array() {
let int_array = vec![10i64, 20i64];
let v = int_array.to_value();
let j = JsonValue::from_value(&v, &types::array(types::int64())).unwrap();
assert_eq!(j, serde_json::json!(["10", "20"]));
}
#[test]
fn test_from_value_json_positional_struct() {
let struct_type = types::create_type(TypeCode::Struct);
let mut inner: mdl::Type = struct_type.0;
inner.struct_type = Some(Box::new(mdl::StructType {
fields: vec![
mdl::struct_type::Field::new()
.set_name("name")
.set_type(mdl::Type {
code: mdl::TypeCode::String,
..Default::default()
}),
mdl::struct_type::Field::new()
.set_name("age")
.set_type(mdl::Type {
code: mdl::TypeCode::Int64,
..Default::default()
}),
],
_unknown_fields: Default::default(),
}));
let spanner_type = Type(inner);
let v = crate::value::Value(prost_types::Value {
kind: Some(prost_types::value::Kind::ListValue(
prost_types::ListValue {
values: vec![
prost_types::Value {
kind: Some(prost_types::value::Kind::StringValue("Alice".to_string())),
},
prost_types::Value {
kind: Some(prost_types::value::Kind::StringValue("30".to_string())),
},
],
},
)),
});
let j = JsonValue::from_value(&v, &spanner_type).unwrap();
assert_eq!(j, serde_json::json!({"name": "Alice", "age": "30"}));
}
#[test]
fn test_from_value_json_array_of_structs() {
let elem_struct = mdl::Type {
code: mdl::TypeCode::Struct,
struct_type: Some(Box::new(mdl::StructType {
fields: vec![
mdl::struct_type::Field::new()
.set_name("a")
.set_type(mdl::Type {
code: mdl::TypeCode::String,
..Default::default()
}),
mdl::struct_type::Field::new()
.set_name("b")
.set_type(mdl::Type {
code: mdl::TypeCode::Int64,
..Default::default()
}),
],
_unknown_fields: Default::default(),
})),
..Default::default()
};
let array_type = mdl::Type {
code: mdl::TypeCode::Array,
array_element_type: Some(Box::new(elem_struct)),
..Default::default()
};
let spanner_type = Type(array_type);
let v = crate::value::Value(prost_types::Value {
kind: Some(prost_types::value::Kind::ListValue(
prost_types::ListValue {
values: vec![
prost_types::Value {
kind: Some(prost_types::value::Kind::ListValue(
prost_types::ListValue {
values: vec![
prost_types::Value {
kind: Some(prost_types::value::Kind::StringValue(
"x".to_string(),
)),
},
prost_types::Value {
kind: Some(prost_types::value::Kind::StringValue(
"1".to_string(),
)),
},
],
},
)),
},
prost_types::Value {
kind: Some(prost_types::value::Kind::ListValue(
prost_types::ListValue {
values: vec![
prost_types::Value {
kind: Some(prost_types::value::Kind::StringValue(
"y".to_string(),
)),
},
prost_types::Value {
kind: Some(prost_types::value::Kind::StringValue(
"2".to_string(),
)),
},
],
},
)),
},
],
},
)),
});
let j = JsonValue::from_value(&v, &spanner_type).unwrap();
assert_eq!(
j,
serde_json::json!([
{"a": "x", "b": "1"},
{"a": "y", "b": "2"},
])
);
}
#[test]
fn test_from_value_json_named_struct() {
let struct_type_mdl =
mdl::Type {
code: mdl::TypeCode::Struct,
struct_type: Some(Box::new(mdl::StructType {
fields: vec![mdl::struct_type::Field::new().set_name("name").set_type(
mdl::Type {
code: mdl::TypeCode::String,
..Default::default()
},
)],
_unknown_fields: Default::default(),
})),
..Default::default()
};
let spanner_type = Type(struct_type_mdl);
let mut s = prost_types::Struct::default();
s.fields.insert(
"name".to_string(),
prost_types::Value {
kind: Some(prost_types::value::Kind::StringValue("Alice".to_string())),
},
);
let v = crate::value::Value(prost_types::Value {
kind: Some(prost_types::value::Kind::StructValue(s)),
});
let j = JsonValue::from_value(&v, &spanner_type).unwrap();
assert_eq!(j, serde_json::json!({"name": "Alice"}));
}
#[test]
fn test_from_value_json_named_struct_missing_fields_become_null() {
let struct_type_mdl = mdl::Type {
code: mdl::TypeCode::Struct,
struct_type: Some(Box::new(mdl::StructType {
fields: vec![
mdl::struct_type::Field::new()
.set_name("present")
.set_type(mdl::Type {
code: mdl::TypeCode::String,
..Default::default()
}),
mdl::struct_type::Field::new()
.set_name("absent")
.set_type(mdl::Type {
code: mdl::TypeCode::Int64,
..Default::default()
}),
],
_unknown_fields: Default::default(),
})),
..Default::default()
};
let spanner_type = Type(struct_type_mdl);
let mut s = prost_types::Struct::default();
s.fields.insert(
"present".to_string(),
prost_types::Value {
kind: Some(prost_types::value::Kind::StringValue("here".to_string())),
},
);
let v = crate::value::Value(prost_types::Value {
kind: Some(prost_types::value::Kind::StructValue(s)),
});
let j = JsonValue::from_value(&v, &spanner_type).unwrap();
assert_eq!(j, serde_json::json!({"present": "here", "absent": null}));
}
#[test]
fn test_from_value_json_spanner_json_column() {
let json_str = r#"{"key": "value", "nested": [1, 2, 3]}"#;
let v = crate::value::Value(prost_types::Value {
kind: Some(prost_types::value::Kind::StringValue(json_str.to_string())),
});
let j = JsonValue::from_value(&v, &types::json()).unwrap();
assert_eq!(j, serde_json::json!({"key": "value", "nested": [1, 2, 3]}));
}
#[test]
fn test_from_value_json_nested_struct_in_struct() {
let inner_struct_type = mdl::Type {
code: mdl::TypeCode::Struct,
struct_type: Some(Box::new(mdl::StructType {
fields: vec![
mdl::struct_type::Field::new()
.set_name("x")
.set_type(mdl::Type {
code: mdl::TypeCode::Int64,
..Default::default()
}),
mdl::struct_type::Field::new()
.set_name("y")
.set_type(mdl::Type {
code: mdl::TypeCode::Bool,
..Default::default()
}),
],
_unknown_fields: Default::default(),
})),
..Default::default()
};
let outer_type = mdl::Type {
code: mdl::TypeCode::Struct,
struct_type: Some(Box::new(mdl::StructType {
fields: vec![
mdl::struct_type::Field::new()
.set_name("outer_field")
.set_type(mdl::Type {
code: mdl::TypeCode::String,
..Default::default()
}),
mdl::struct_type::Field::new()
.set_name("inner")
.set_type(inner_struct_type),
],
_unknown_fields: Default::default(),
})),
..Default::default()
};
let spanner_type = Type(outer_type);
let v = crate::value::Value(prost_types::Value {
kind: Some(prost_types::value::Kind::ListValue(
prost_types::ListValue {
values: vec![
prost_types::Value {
kind: Some(prost_types::value::Kind::StringValue("hello".to_string())),
},
prost_types::Value {
kind: Some(prost_types::value::Kind::ListValue(
prost_types::ListValue {
values: vec![
prost_types::Value {
kind: Some(prost_types::value::Kind::StringValue(
"42".to_string(),
)),
},
prost_types::Value {
kind: Some(prost_types::value::Kind::BoolValue(true)),
},
],
},
)),
},
],
},
)),
});
let j = JsonValue::from_value(&v, &spanner_type).unwrap();
assert_eq!(
j,
serde_json::json!({"outer_field": "hello", "inner": {"x": "42", "y": true}})
);
}
#[test]
fn test_from_value_json_null_in_struct() {
let struct_type_mdl = mdl::Type {
code: mdl::TypeCode::Struct,
struct_type: Some(Box::new(mdl::StructType {
fields: vec![
mdl::struct_type::Field::new()
.set_name("name")
.set_type(mdl::Type {
code: mdl::TypeCode::String,
..Default::default()
}),
mdl::struct_type::Field::new()
.set_name("value")
.set_type(mdl::Type {
code: mdl::TypeCode::Int64,
..Default::default()
}),
],
_unknown_fields: Default::default(),
})),
..Default::default()
};
let spanner_type = Type(struct_type_mdl);
let v = crate::value::Value(prost_types::Value {
kind: Some(prost_types::value::Kind::ListValue(
prost_types::ListValue {
values: vec![
prost_types::Value {
kind: Some(prost_types::value::Kind::StringValue("test".to_string())),
},
prost_types::Value {
kind: Some(prost_types::value::Kind::NullValue(0)),
},
],
},
)),
});
let j = JsonValue::from_value(&v, &spanner_type).unwrap();
assert_eq!(j, serde_json::json!({"name": "test", "value": null}));
}
#[test]
fn test_from_value_json_nan_becomes_null() {
let v = crate::value::Value(prost_types::Value {
kind: Some(prost_types::value::Kind::NumberValue(f64::NAN)),
});
let j = JsonValue::from_value(&v, &types::float64()).unwrap();
assert_eq!(j, JsonValue::Null);
let v = crate::value::Value(prost_types::Value {
kind: Some(prost_types::value::Kind::NumberValue(f64::INFINITY)),
});
let j = JsonValue::from_value(&v, &types::float64()).unwrap();
assert_eq!(j, JsonValue::Null);
let v = crate::value::Value(prost_types::Value {
kind: Some(prost_types::value::Kind::NumberValue(f64::NEG_INFINITY)),
});
let j = JsonValue::from_value(&v, &types::float64()).unwrap();
assert_eq!(j, JsonValue::Null);
let v = crate::value::Value(prost_types::Value {
kind: Some(prost_types::value::Kind::StringValue("NaN".to_string())),
});
let j = JsonValue::from_value(&v, &types::float64()).unwrap();
assert_eq!(j, JsonValue::Null);
let v = crate::value::Value(prost_types::Value {
kind: Some(prost_types::value::Kind::StringValue(
"Infinity".to_string(),
)),
});
let j = JsonValue::from_value(&v, &types::float64()).unwrap();
assert_eq!(j, JsonValue::Null);
let v = crate::value::Value(prost_types::Value {
kind: Some(prost_types::value::Kind::StringValue(
"-Infinity".to_string(),
)),
});
let j = JsonValue::from_value(&v, &types::float64()).unwrap();
assert_eq!(j, JsonValue::Null);
}
#[test]
fn test_from_value_json_option_wrapping() {
let v = "hello".to_value();
let j = Option::<JsonValue>::from_value(&v, &types::string()).unwrap();
assert_eq!(j, Some(JsonValue::String("hello".to_string())));
let v: Option<String> = None;
let j = Option::<JsonValue>::from_value(&v.to_value(), &types::string()).unwrap();
assert_eq!(j, None);
}
#[test]
fn test_from_value_json_empty_struct() {
let struct_type_mdl = mdl::Type {
code: mdl::TypeCode::Struct,
struct_type: Some(Box::new(mdl::StructType {
fields: vec![],
_unknown_fields: Default::default(),
})),
..Default::default()
};
let spanner_type = Type(struct_type_mdl);
let v = crate::value::Value(prost_types::Value {
kind: Some(prost_types::value::Kind::ListValue(
prost_types::ListValue { values: vec![] },
)),
});
let j = JsonValue::from_value(&v, &spanner_type).unwrap();
assert_eq!(j, serde_json::json!({}));
}
#[test]
fn test_from_value_json_unnamed_fields() {
let struct_type_mdl = mdl::Type {
code: mdl::TypeCode::Struct,
struct_type: Some(Box::new(mdl::StructType {
fields: vec![
mdl::struct_type::Field::new()
.set_name("")
.set_type(mdl::Type {
code: mdl::TypeCode::String,
..Default::default()
}),
mdl::struct_type::Field::new()
.set_name("")
.set_type(mdl::Type {
code: mdl::TypeCode::Int64,
..Default::default()
}),
],
_unknown_fields: Default::default(),
})),
..Default::default()
};
let spanner_type = Type(struct_type_mdl);
let v = crate::value::Value(prost_types::Value {
kind: Some(prost_types::value::Kind::ListValue(
prost_types::ListValue {
values: vec![
prost_types::Value {
kind: Some(prost_types::value::Kind::StringValue("val".to_string())),
},
prost_types::Value {
kind: Some(prost_types::value::Kind::StringValue("99".to_string())),
},
],
},
)),
});
let j = JsonValue::from_value(&v, &spanner_type).unwrap();
assert!(j.is_object());
assert_eq!(
j.as_object().unwrap().get("").unwrap(),
&serde_json::json!("99")
);
}
#[test]
fn test_from_value_json_vec_composition() {
let str_array = vec!["one".to_string(), "two".to_string()];
let v = str_array.to_value();
let res = Vec::<JsonValue>::from_value(&v, &types::array(types::string())).unwrap();
assert_eq!(res.len(), 2);
assert_eq!(res[0], JsonValue::String("one".to_string()));
assert_eq!(res[1], JsonValue::String("two".to_string()));
}
#[test]
fn test_from_value_json_struct_without_metadata() {
let mut s = prost_types::Struct::default();
s.fields.insert(
"raw_field".to_string(),
prost_types::Value {
kind: Some(prost_types::value::Kind::BoolValue(true)),
},
);
let v = crate::value::Value(prost_types::Value {
kind: Some(prost_types::value::Kind::StructValue(s)),
});
let j = JsonValue::from_value(&v, &Type::default()).unwrap();
assert_eq!(j, serde_json::json!({"raw_field": true}));
}
#[test]
fn test_from_value_json_list_without_type_info() {
let v = crate::value::Value(prost_types::Value {
kind: Some(prost_types::value::Kind::ListValue(
prost_types::ListValue {
values: vec![
prost_types::Value {
kind: Some(prost_types::value::Kind::StringValue("a".to_string())),
},
prost_types::Value {
kind: Some(prost_types::value::Kind::BoolValue(false)),
},
],
},
)),
});
let j = JsonValue::from_value(&v, &Type::default()).unwrap();
assert_eq!(j, serde_json::json!(["a", false]));
}
#[test]
fn test_from_value_json_invalid_json_column() {
let v = crate::value::Value(prost_types::Value {
kind: Some(prost_types::value::Kind::StringValue(
"not valid json{".to_string(),
)),
});
let err = JsonValue::from_value(&v, &types::json()).unwrap_err();
assert!(format!("{}", err).contains("cannot convert value"));
}
#[test]
fn test_from_value_json_missing_positional_fields_become_null() {
let struct_type_mdl = mdl::Type {
code: mdl::TypeCode::Struct,
struct_type: Some(Box::new(mdl::StructType {
fields: vec![
mdl::struct_type::Field::new()
.set_name("a")
.set_type(mdl::Type {
code: mdl::TypeCode::String,
..Default::default()
}),
mdl::struct_type::Field::new()
.set_name("b")
.set_type(mdl::Type {
code: mdl::TypeCode::Int64,
..Default::default()
}),
mdl::struct_type::Field::new()
.set_name("c")
.set_type(mdl::Type {
code: mdl::TypeCode::Bool,
..Default::default()
}),
],
_unknown_fields: Default::default(),
})),
..Default::default()
};
let spanner_type = Type(struct_type_mdl);
let v = crate::value::Value(prost_types::Value {
kind: Some(prost_types::value::Kind::ListValue(
prost_types::ListValue {
values: vec![prost_types::Value {
kind: Some(prost_types::value::Kind::StringValue("only_a".to_string())),
}],
},
)),
});
let j = JsonValue::from_value(&v, &spanner_type).unwrap();
assert_eq!(j, serde_json::json!({"a": "only_a", "b": null, "c": null}));
}
#[test]
fn test_from_value_json_array_of_json_columns() {
let json_type = mdl::Type {
code: mdl::TypeCode::Json,
..Default::default()
};
let array_type = mdl::Type {
code: mdl::TypeCode::Array,
array_element_type: Some(Box::new(json_type)),
..Default::default()
};
let spanner_type = Type(array_type);
let v = crate::value::Value(prost_types::Value {
kind: Some(prost_types::value::Kind::ListValue(
prost_types::ListValue {
values: vec![
prost_types::Value {
kind: Some(prost_types::value::Kind::StringValue(
r#"{"x":1}"#.to_string(),
)),
},
prost_types::Value {
kind: Some(prost_types::value::Kind::StringValue(
r#"{"y":2}"#.to_string(),
)),
},
],
},
)),
});
let j = JsonValue::from_value(&v, &spanner_type).unwrap();
assert_eq!(j, serde_json::json!([{"x": 1}, {"y": 2}]));
}
#[test]
fn test_from_value_json_nested_array_in_struct() {
let struct_type_mdl = mdl::Type {
code: mdl::TypeCode::Struct,
struct_type: Some(Box::new(mdl::StructType {
fields: vec![
mdl::struct_type::Field::new()
.set_name("tags")
.set_type(mdl::Type {
code: mdl::TypeCode::Array,
array_element_type: Some(Box::new(mdl::Type {
code: mdl::TypeCode::String,
..Default::default()
})),
..Default::default()
}),
mdl::struct_type::Field::new()
.set_name("id")
.set_type(mdl::Type {
code: mdl::TypeCode::Int64,
..Default::default()
}),
],
_unknown_fields: Default::default(),
})),
..Default::default()
};
let spanner_type = Type(struct_type_mdl);
let v = crate::value::Value(prost_types::Value {
kind: Some(prost_types::value::Kind::ListValue(
prost_types::ListValue {
values: vec![
prost_types::Value {
kind: Some(prost_types::value::Kind::ListValue(
prost_types::ListValue {
values: vec![
prost_types::Value {
kind: Some(prost_types::value::Kind::StringValue(
"foo".to_string(),
)),
},
prost_types::Value {
kind: Some(prost_types::value::Kind::StringValue(
"bar".to_string(),
)),
},
],
},
)),
},
prost_types::Value {
kind: Some(prost_types::value::Kind::StringValue("42".to_string())),
},
],
},
)),
});
let j = JsonValue::from_value(&v, &spanner_type).unwrap();
assert_eq!(j, serde_json::json!({"tags": ["foo", "bar"], "id": "42"}));
}
#[test]
fn test_from_value_json_recursion_depth_limit() {
let mut inner = prost_types::Value {
kind: Some(prost_types::value::Kind::BoolValue(true)),
};
for _ in 0..65 {
inner = prost_types::Value {
kind: Some(prost_types::value::Kind::ListValue(
prost_types::ListValue {
values: vec![inner],
},
)),
};
}
let v = crate::value::Value(inner);
let err = JsonValue::from_value(&v, &Type::default()).unwrap_err();
assert!(format!("{}", err).contains("nesting depth exceeded"));
}
#[test]
fn test_from_value_json_array_without_element_type() {
let array_type = mdl::Type {
code: mdl::TypeCode::Array,
array_element_type: None,
..Default::default()
};
let spanner_type = Type(array_type);
let v = crate::value::Value(prost_types::Value {
kind: Some(prost_types::value::Kind::ListValue(
prost_types::ListValue {
values: vec![prost_types::Value {
kind: Some(prost_types::value::Kind::StringValue("hello".to_string())),
}],
},
)),
});
let j = JsonValue::from_value(&v, &spanner_type).unwrap();
assert_eq!(j, serde_json::json!(["hello"]));
}
#[test]
fn test_from_value_json_struct_with_missing_field_type() {
let struct_type_mdl = mdl::Type {
code: mdl::TypeCode::Struct,
struct_type: Some(Box::new(mdl::StructType {
fields: vec![
mdl::struct_type::Field::new().set_name("a"), ],
_unknown_fields: Default::default(),
})),
..Default::default()
};
let spanner_type = Type(struct_type_mdl);
let v = crate::value::Value(prost_types::Value {
kind: Some(prost_types::value::Kind::ListValue(
prost_types::ListValue {
values: vec![prost_types::Value {
kind: Some(prost_types::value::Kind::StringValue("hello".to_string())),
}],
},
)),
});
let j = JsonValue::from_value(&v, &spanner_type).unwrap();
assert_eq!(j, serde_json::json!({"a": "hello"}));
}
}