use super::AmazonMetaData;
use super::DcName;
use serde::de::{Deserialize, Deserializer, Error as DeError, MapAccess, Visitor};
use serde::ser::{Serialize, SerializeStruct, Serializer};
use std::fmt;
const NAME: &str = "name";
const METADATA: &str = "metadata";
const CLASS: &str = "@class";
const CLASS_VALUE: &str = "com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo";
const DATA_CENTER_INFO: &str = "DataCenterInfo";
const FIELDS: &[&str] = &[CLASS, NAME, METADATA];
#[derive(Debug, PartialEq)]
pub struct DataCenterInfo {
pub name: DcName,
pub metadata: Option<AmazonMetaData>,
}
impl Serialize for DataCenterInfo {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut s = serializer.serialize_struct(DATA_CENTER_INFO, 2)?;
s.serialize_field(CLASS, CLASS_VALUE)?;
s.serialize_field(NAME, &self.name)?;
s.serialize_field(METADATA, &self.metadata)?;
s.end()
}
}
impl<'de> Deserialize<'de> for DataCenterInfo {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
enum Field {
Name,
Metadata,
Class,
}
impl<'de> Deserialize<'de> for Field {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct FieldVisitor;
impl<'de> Visitor<'de> for FieldVisitor {
type Value = Field;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("Expecting `name` or `metadata` ")
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: DeError,
{
match v {
NAME => Ok(Field::Name),
METADATA => Ok(Field::Metadata),
CLASS => Ok(Field::Class),
_ => Err(DeError::unknown_field(v, FIELDS)),
}
}
}
deserializer.deserialize_identifier(FieldVisitor)
}
}
struct DataCenterInfoVisitor;
impl<'de> Visitor<'de> for DataCenterInfoVisitor {
type Value = DataCenterInfo;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("struct DataCenterInfo")
}
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: MapAccess<'de>,
{
let mut maybe_name = None;
let mut maybe_metadata = None;
let mut maybe_class: Option<&str> = None;
while let Some(key) = map.next_key()? {
match key {
Field::Name => {
if maybe_name.is_some() {
return Err(DeError::duplicate_field(NAME));
}
maybe_name = Some(map.next_value()?);
}
Field::Metadata => {
if maybe_metadata.is_some() {
return Err(DeError::duplicate_field(METADATA));
}
maybe_metadata = Some(map.next_value()?);
}
Field::Class => {
maybe_class = Some(map.next_value()?);
}
}
}
let name = maybe_name.ok_or_else(|| DeError::missing_field(NAME));
debug!("Found ignored field @class {:?} ?", maybe_class);
Ok(DataCenterInfo {
name: name?,
metadata: maybe_metadata,
})
}
}
deserializer.deserialize_struct(DATA_CENTER_INFO, FIELDS, DataCenterInfoVisitor)
}
}
#[cfg(test)]
pub mod test {
use super::super::amazonmetadata::tests::sample_meta_data;
use super::*;
use serde_json;
#[test]
fn test_serialize_data_center_info() {
let dci = DataCenterInfo {
name: DcName::Amazon,
metadata: Some(AmazonMetaData {
ami_launch_index: "001a".to_string(),
local_hostname: "localhost0".to_string(),
availability_zone: "US_East1a".to_string(),
instance_id: "instance1a".to_string(),
public_ipv4: "32.23.21.212".to_string(),
public_hostname: "foo.coma".to_string(),
ami_manifest_path: "/dev/nulla".to_string(),
local_ipv4: "127.0.0.12".to_string(),
hostname: "privatefoo.coma".to_string(),
ami_id: "ami0023".to_string(),
instance_type: "c4xlarged".to_string(),
}),
};
let json = sample_data_center();
let result = serde_json::to_string(&dci).unwrap();
assert_eq!(json, result);
}
#[test]
fn test_deserialize_data_center_info() {
let dci = DataCenterInfo {
name: DcName::Amazon,
metadata: Some(AmazonMetaData {
ami_launch_index: "001a".to_string(),
local_hostname: "localhost0".to_string(),
availability_zone: "US_East1a".to_string(),
instance_id: "instance1a".to_string(),
public_ipv4: "32.23.21.212".to_string(),
public_hostname: "foo.coma".to_string(),
ami_manifest_path: "/dev/nulla".to_string(),
local_ipv4: "127.0.0.12".to_string(),
hostname: "privatefoo.coma".to_string(),
ami_id: "ami0023".to_string(),
instance_type: "c4xlarged".to_string(),
}),
};
let json = sample_data_center();
println!("json {}", json);
let result = serde_json::from_str(&json).unwrap();
assert_eq!(dci, result);
}
fn sample_data_center() -> String {
format!("{{\"@class\":\"com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo\",\"name\":\"Amazon\",\"metadata\":{}}}", sample_meta_data())
}
}