#[derive(Clone, Debug, Default, PartialEq)]
#[non_exhaustive]
pub struct Any(serde_json::Map<String, serde_json::Value>);
#[derive(thiserror::Error, Debug)]
pub enum AnyError {
#[error("cannot serialize object into an Any, source={0:?}")]
SerializationError(#[source] Box<dyn std::error::Error>),
#[error("cannot deserialize from an Any, source={0:?}")]
DeserializationError(#[source] Box<dyn std::error::Error>),
#[error("expected type mismatch in Any deserialization type={0}")]
TypeMismatchError(String),
}
type Error = AnyError;
impl Any {
pub fn from<T>(message: &T) -> Result<Self, Error>
where
T: serde::ser::Serialize,
{
use serde_json::Value;
let value =
serde_json::to_value(message).map_err(|e| Error::SerializationError(e.into()))?;
let value = match value {
Value::Object(mut map) => {
map.insert("@type".to_string(), serde_json::json!(""));
Ok(map)
}
Value::String(s) => {
let mut map = serde_json::Map::new();
map.insert(
"@type".to_string(),
serde_json::Value::String("type.googleapis.com/google.protobuf.".into()),
);
map.insert("value".to_string(), serde_json::Value::String(s));
Ok(map)
}
_ => Err(Error::SerializationError(Box::from(
"unexpected JSON type, only Object and String are supported",
))),
}?;
Ok(Any(value))
}
fn map_de_err(e: Box<dyn std::error::Error>) -> Error {
Error::DeserializationError(e)
}
fn map_de_str(s: String) -> Error {
Error::DeserializationError(Box::from(s))
}
pub fn try_into_message<T>(&self) -> Result<T, Error>
where
T: serde::de::DeserializeOwned,
{
let map = &self.0;
let r#type = map
.get("@type")
.and_then(|v| v.as_str())
.ok_or_else(|| "@type field is missing or is not a string".to_string())
.map_err(Self::map_de_str)?;
if r#type.starts_with("type.googleapis.com/google.protobuf.") {
return map
.get("value")
.map(|v| serde_json::from_value::<T>(v.clone()))
.ok_or_else(|| Self::map_de_str("value field is missing".to_string()))?
.map_err(|e| Self::map_de_err(e.into()));
}
serde_json::from_value::<T>(serde_json::Value::Object(map.clone()))
.map_err(|e| Self::map_de_err(e.into()))
}
}
impl serde::ser::Serialize for Any {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: serde::ser::Serializer,
{
self.0.serialize(serializer)
}
}
impl<'de> serde::de::Deserialize<'de> for Any {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let value = serde_json::Map::<String, serde_json::Value>::deserialize(deserializer)?;
Ok(Any(value))
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::duration::*;
use serde_json::json;
type Result = std::result::Result<(), Box<dyn std::error::Error>>;
#[derive(Clone, Debug, Default, PartialEq, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct Stored {
#[serde(skip_serializing_if = "String::is_empty")]
pub parent: String,
#[serde(skip_serializing_if = "String::is_empty")]
pub id: String,
}
#[test]
fn serialize_duration() -> Result {
let d = Duration::clamp(60, 0);
let any = Any::from(&d)?;
let got = serde_json::to_value(any)?;
let want = json!({"@type": "type.googleapis.com/google.protobuf.", "value": "60s"});
assert_eq!(got, want);
Ok(())
}
#[test]
fn deserialize_duration() -> Result {
let input = json!({"@type": "type.googleapis.com/google.protobuf.", "value": "60s"});
let any = Any(input.as_object().unwrap().clone());
let d = any.try_into_message::<Duration>()?;
assert_eq!(d, Duration::clamp(60, 0));
Ok(())
}
#[test]
fn serialize_generic() -> Result {
let d = Stored {
parent: "parent".to_string(),
id: "id".to_string(),
};
let any = Any::from(&d)?;
let got = serde_json::to_value(any)?;
let want = json!({"@type": "", "parent": "parent", "id": "id"});
assert_eq!(got, want);
Ok(())
}
#[test]
fn deserialize_generic() -> Result {
let input = json!({"@type": "", "parent": "parent", "id": "id"});
let any = Any(input.as_object().unwrap().clone());
let d = any.try_into_message::<Stored>()?;
assert_eq!(
d,
Stored {
parent: "parent".to_string(),
id: "id".to_string()
}
);
Ok(())
}
#[test]
fn serialize_error() -> Result {
use std::collections::BTreeMap;
let mut input = BTreeMap::new();
input.insert(vec![2, 3], "unused");
let got = Any::from(&input);
assert!(got.is_err());
match got.as_ref().err().unwrap() {
Error::SerializationError(_) => assert!(true),
_ => assert!(false, "unexpected error {got:?}"),
};
let input = vec![2, 3, 4];
let got = Any::from(&input);
assert!(got.is_err());
match got.as_ref().err().unwrap() {
Error::SerializationError(_) => assert!(true),
_ => assert!(false, "unexpected error {got:?}"),
};
Ok(())
}
#[test]
fn deserialize_error() -> Result {
let input = json!({"@type-is-missing": ""});
let any = serde_json::from_value::<Any>(input)?;
let got = any.try_into_message::<Stored>();
assert!(got.is_err());
let input = json!({"@type": [1, 2, 3]});
let any = serde_json::from_value::<Any>(input)?;
let got = any.try_into_message::<Stored>();
assert!(got.is_err());
let input = json!({"@type": "type.googleapis.com/google.protobuf.Duration", "value-is-missing": "1.2s"});
let any = serde_json::from_value::<Any>(input)?;
let got = any.try_into_message::<Stored>();
assert!(got.is_err());
let input =
json!({"@type": "type.googleapis.com/google.protobuf.Duration", "value": ["1.2s"]});
let any = serde_json::from_value::<Any>(input)?;
let got = any.try_into_message::<Stored>();
assert!(got.is_err());
Ok(())
}
}