Skip to content

Commit

Permalink
Fixed Struct and ListValue serialization
Browse files Browse the repository at this point in the history
  • Loading branch information
fdeantoni committed Feb 18, 2023
1 parent 44d400d commit 12d7bdf
Show file tree
Hide file tree
Showing 5 changed files with 191 additions and 41 deletions.
4 changes: 1 addition & 3 deletions wkt-types/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,7 @@ fn build(dir: &Path, proto: &str) {
let mut prost_build = prost_build::Config::new();
prost_build
.compile_well_known_types()
.type_attribute("google.protobuf.Struct","#[derive(serde_derive::Serialize, serde_derive::Deserialize)] #[serde(default, rename_all=\"camelCase\")]")
.type_attribute("google.protobuf.ListValue","#[derive(serde_derive::Serialize, serde_derive::Deserialize)] #[serde(default, rename_all=\"camelCase\")]")
.type_attribute("google.protobuf.Duration","#[derive(serde_derive::Serialize, serde_derive::Deserialize)] #[serde(default, rename_all=\"camelCase\")]")
.type_attribute("google.protobuf.Duration","#[derive(serde_derive::Serialize, serde_derive::Deserialize)] #[serde(default)]")
.type_attribute("google.protobuf.Empty","#[derive(serde_derive::Serialize, serde_derive::Deserialize)]")
.file_descriptor_set_path(&descriptor_file)
.out_dir(&out)
Expand Down
140 changes: 109 additions & 31 deletions wkt-types/src/pbstruct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,32 @@ impl TryFrom<Value> for std::vec::Vec<Value> {
}
}

impl Serialize for ListValue {
fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
where
S: Serializer,
{
let mut seq = serializer.serialize_seq(Some(self.values.len()))?;
for e in &self.values {
seq.serialize_element(e)?;
}
seq.end()
}
}

impl Serialize for Struct {
fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
where
S: Serializer,
{
let mut map = serializer.serialize_map(Some(self.fields.len()))?;
for (k, v) in &self.fields {
map.serialize_entry( k, v)?;
}
map.end()
}
}

impl Serialize for Value {
fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
where
Expand All @@ -192,24 +218,78 @@ impl Serialize for Value {
Some(value::Kind::BoolValue(boolean)) => serializer.serialize_bool(*boolean),
Some(value::Kind::NullValue(_)) => serializer.serialize_none(),
Some(value::Kind::ListValue(list)) => {
let mut seq = serializer.serialize_seq(Some(list.values.len()))?;
for e in list.clone().values {
seq.serialize_element(&e)?;
}
seq.end()
list.serialize(serializer)
}
Some(value::Kind::StructValue(object)) => {
let mut map = serializer.serialize_map(Some(object.fields.len()))?;
for (k, v) in object.clone().fields {
map.serialize_entry(&k, &v)?;
}
map.end()
object.serialize(serializer)
}
_ => serializer.serialize_none(),
}
}
}

struct ListValueVisitor;
impl<'de> Visitor<'de> for ListValueVisitor {
type Value = crate::ListValue;

fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a prost_wkt_types::ListValue struct")
}

fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
let mut values: Vec<Value> = Vec::new();
while let Some(el) = seq.next_element()? {
values.push(el)
}
Ok(ListValue {
values
})
}
}

impl<'de> Deserialize<'de> for ListValue {
fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_seq(ListValueVisitor)
}
}

struct StructVisitor;
impl<'de> Visitor<'de> for StructVisitor {
type Value = crate::Struct;

fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a prost_wkt_types::Struct struct")
}

fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: MapAccess<'de>,
{
let mut fields: std::collections::HashMap<String, Value> =
std::collections::HashMap::new();
while let Some((key, value)) = map.next_entry::<String, Value>()? {
fields.insert(key, value);
}
Ok(Struct {
fields
})
}
}

impl<'de> Deserialize<'de> for Struct {
fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
where
D: Deserializer<'de> {
deserializer.deserialize_map(StructVisitor)
}
}

impl<'de> Deserialize<'de> for Value {
fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
where
Expand All @@ -221,7 +301,7 @@ impl<'de> Deserialize<'de> for Value {
type Value = crate::Value;

fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a prost_types::Value struct")
formatter.write_str("a prost_wkt_types::Value struct")
}

fn visit_bool<E>(self, value: bool) -> Result<Self::Value, E>
Expand Down Expand Up @@ -280,27 +360,24 @@ impl<'de> Deserialize<'de> for Value {
Ok(Value::null())
}

fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
fn visit_seq<A>(self, seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
let mut values: Vec<Value> = Vec::new();
while let Some(el) = seq.next_element()? {
values.push(el)
}
Ok(Value::from(values))
ListValueVisitor.visit_seq(seq).map(|lv| {
let kind = Some(value::Kind::ListValue(lv));
Value { kind }
})
}

fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
fn visit_map<A>(self, map: A) -> Result<Self::Value, A::Error>
where
A: MapAccess<'de>,
{
let mut fields: std::collections::HashMap<String, Value> =
std::collections::HashMap::new();
while let Some((key, value)) = map.next_entry()? {
fields.insert(key, value);
}
Ok(Value::from(fields))
StructVisitor.visit_map(map).map(|s| {
let kind = Some(value::Kind::StructValue(s));
Value { kind }
})
}
}
deserializer.deserialize_any(ValueVisitor)
Expand All @@ -324,8 +401,8 @@ mod tests {
let pb_list: Value = Value::from(list);
println!("List: {pb_list:?}");
let mut map: HashMap<String, Value> = HashMap::new();
map.insert(String::from("number"), number);
map.insert(String::from("null"), null);
map.insert(String::from("some_number"), number);
map.insert(String::from("a_null_value"), null);
map.insert(String::from("string"), string);
map.insert(String::from("list"), pb_list);
let pb_struct: Value = Value::from(map);
Expand All @@ -341,19 +418,20 @@ mod tests {
"data": {
"test_number": 1.0,
"test_bool": true,
"test_string": "hi there",
"test_list": [1.0, 2.0, 3.0, 4.0],
"test_inner_struct": {
"testString": "hi there",
"testList": [1.0, 2.0, 3.0, 4.0],
"testInnerStruct": {
"one": 1.0,
"two": 2.0
}
},
"list": []
}"#;
let sj: serde_json::Value = serde_json::from_str(data).unwrap();
println!("serde_json::Value: {sj:#?}");
let pj: Value = serde_json::from_value(sj.clone()).unwrap();
println!("prost_wkt_types Value: {pj:?}");
let string: String = serde_json::to_string(&pj).unwrap();
let string: String = serde_json::to_string_pretty(&pj).unwrap();
println!("prost_wkt_types String: {string}");
let back: serde_json::Value = serde_json::from_str(&string).unwrap();
assert_eq!(sj, back);
}
Expand Down
12 changes: 12 additions & 0 deletions wkt-types/src/pbtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,18 @@ mod tests {
use crate::pbtime::*;
use chrono::{DateTime, Utc};

#[test]
fn serialize_duration() {
let duration = Duration {
seconds: 10,
nanos: 100,
};
let json = serde_json::to_string_pretty(&duration).expect("json");
println!("{json}");
let back: Duration = serde_json::from_str(&json).expect("duration");
assert_eq!(duration, back);
}

#[test]
fn invalid_timestamp_test() {
let ts = Timestamp {
Expand Down
16 changes: 9 additions & 7 deletions wkt-types/tests/pbany_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pub struct Foo {
#[prost(bool, tag = "3")]
pub boolean: bool,
#[prost(message, optional, tag = "4")]
pub data: ::std::option::Option<::prost_wkt_types::Value>,
pub value_data: ::std::option::Option<::prost_wkt_types::Value>,
#[prost(string, repeated, tag = "5")]
pub list: ::std::vec::Vec<std::string::String>,
#[prost(message, optional, tag = "6")]
Expand Down Expand Up @@ -81,7 +81,7 @@ fn test_any_serialization() {
string: String::from("inner"),
timestamp: None,
boolean: false,
data: Some(create_struct()),
value_data: Some(create_struct()),
list: vec!["een".to_string(), "twee".to_string()],
payload: None,
};
Expand All @@ -93,7 +93,7 @@ fn test_any_serialization() {
nanos: 42,
}),
boolean: true,
data: Some(prost_wkt_types::Value::from("world".to_string())),
value_data: Some(prost_wkt_types::Value::from("world".to_string())),
list: vec!["one".to_string(), "two".to_string()],
payload: prost_wkt_types::Any::try_pack(inner).ok(),
};
Expand All @@ -112,7 +112,7 @@ fn test_any_deserialize_string() {
"string":"hello",
"timestamp":"1970-01-01T00:01:39.000000042Z",
"boolean":true,
"data": {
"valueData": {
"test_number": 1,
"test_bool": true,
"test_string": "hi there",
Expand All @@ -126,6 +126,8 @@ fn test_any_deserialize_string() {
}"#;
let msg: Foo = serde_json::from_str(data).unwrap();
println!("Deserialized from string: {msg:?}");
let serialized = serde_json::to_string_pretty(&msg).expect("serialized Foo");
println!("{serialized}")
}

#[test]
Expand All @@ -134,7 +136,7 @@ fn test_any_serialize_deserialize() {
string: String::from("inner"),
timestamp: None,
boolean: false,
data: None,
value_data: None,
list: vec!["een".to_string(), "twee".to_string()],
payload: None,
};
Expand All @@ -146,7 +148,7 @@ fn test_any_serialize_deserialize() {
nanos: 42,
}),
boolean: true,
data: Some(prost_wkt_types::Value::from("world".to_string())),
value_data: Some(prost_wkt_types::Value::from("world".to_string())),
list: vec!["one".to_string(), "two".to_string()],
payload: prost_wkt_types::Any::try_pack(inner).ok(),
};
Expand All @@ -165,7 +167,7 @@ fn test_any_unpack() {
string: String::from("hello payload"),
timestamp: None,
boolean: false,
data: None,
value_data: None,
list: vec!["een".to_string(), "twee".to_string()],
payload: None,
};
Expand Down
60 changes: 60 additions & 0 deletions wkt-types/tests/pbstruct_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
use prost_wkt_types::*;
use std::collections::HashMap;

fn create_struct() -> Value {
let number: Value = Value::from(10.0);
let null: Value = Value::null();
let string: Value = Value::from(String::from("Hello"));
let list = vec![Value::null(), Value::from(100.0)];
let pb_list: Value = Value::from(list);
let mut map: HashMap<String, Value> = HashMap::new();
map.insert(String::from("number"), number);
map.insert(String::from("null"), null);
map.insert(String::from("some_string"), string);
map.insert(String::from("list"), pb_list);
Value::from(map)
}

#[test]
fn test_serde() {
let value = create_struct();
let string = serde_json::to_string_pretty(&value).expect("Json string");
println!("{string}");
let back: Value = serde_json::from_str(&string).expect("Value");
println!("{back:?}");
assert_eq!(value, back);
}

#[test]
fn test_flatten_struct() {
let mut fields: HashMap<String, Value> = HashMap::new();
fields.insert("test".to_string(), create_struct());
let strct = Struct {
fields: fields.clone()
};
let string_strct = serde_json::to_string_pretty(&strct).expect("Serialized struct");
println!("{string_strct}");

let value = Value::from(fields);
let string = serde_json::to_string_pretty(&value).expect("A Value serialized to string");
println!("{string}");

assert_eq!(string_strct, string);
}

#[test]
fn test_flatten_list() {
let values: Vec<Value> = vec![Value::null(), Value::from(20.0), Value::from(true)];
let list: ListValue = ListValue {
values: values.clone()
};
let string_list = serde_json::to_string_pretty(&list).expect("Serialized list");
println!("{string_list}");

let value = Value::from(values);
let string = serde_json::to_string_pretty(&value).expect("A Value serialized to string");
println!("{string}");

assert_eq!(string_list, string);

}

0 comments on commit 12d7bdf

Please sign in to comment.