Skip to main content

rust_eureka/response/
datacenterinfo.rs

1use super::AmazonMetaData;
2use super::DcName;
3use serde::de::{Deserialize, Deserializer, Error as DeError, MapAccess, Visitor};
4use serde::ser::{Serialize, SerializeStruct, Serializer};
5use std::fmt;
6
7// Field name constants
8const NAME: &str = "name";
9const METADATA: &str = "metadata";
10// The eureka API has some awful cruft
11const CLASS: &str = "@class";
12const CLASS_VALUE: &str = "com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo";
13const DATA_CENTER_INFO: &str = "DataCenterInfo";
14const FIELDS: &[&str] = &[CLASS, NAME, METADATA];
15
16#[derive(Debug, PartialEq)]
17pub struct DataCenterInfo {
18    pub name: DcName,
19    pub metadata: Option<AmazonMetaData>,
20}
21
22impl Serialize for DataCenterInfo {
23    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
24    where
25        S: Serializer,
26    {
27        let mut s = serializer.serialize_struct(DATA_CENTER_INFO, 2)?;
28        // weird netflix field
29        s.serialize_field(CLASS, CLASS_VALUE)?;
30        s.serialize_field(NAME, &self.name)?;
31        s.serialize_field(METADATA, &self.metadata)?;
32        s.end()
33    }
34}
35
36impl<'de> Deserialize<'de> for DataCenterInfo {
37    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
38    where
39        D: Deserializer<'de>,
40    {
41        enum Field {
42            Name,
43            Metadata,
44            Class,
45        }
46
47        impl<'de> Deserialize<'de> for Field {
48            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
49            where
50                D: Deserializer<'de>,
51            {
52                struct FieldVisitor;
53
54                impl<'de> Visitor<'de> for FieldVisitor {
55                    type Value = Field;
56
57                    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
58                        formatter.write_str("Expecting `name` or `metadata` ")
59                    }
60
61                    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
62                    where
63                        E: DeError,
64                    {
65                        match v {
66                            NAME => Ok(Field::Name),
67                            METADATA => Ok(Field::Metadata),
68                            CLASS => Ok(Field::Class),
69                            _ => Err(DeError::unknown_field(v, FIELDS)),
70                        }
71                    }
72                }
73                deserializer.deserialize_identifier(FieldVisitor)
74            }
75        }
76
77        struct DataCenterInfoVisitor;
78
79        impl<'de> Visitor<'de> for DataCenterInfoVisitor {
80            type Value = DataCenterInfo;
81
82            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
83                formatter.write_str("struct DataCenterInfo")
84            }
85            fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
86            where
87                A: MapAccess<'de>,
88            {
89                let mut maybe_name = None;
90                let mut maybe_metadata = None;
91                let mut maybe_class: Option<&str> = None;
92
93                while let Some(key) = map.next_key()? {
94                    match key {
95                        Field::Name => {
96                            if maybe_name.is_some() {
97                                return Err(DeError::duplicate_field(NAME));
98                            }
99                            maybe_name = Some(map.next_value()?);
100                        }
101                        Field::Metadata => {
102                            if maybe_metadata.is_some() {
103                                return Err(DeError::duplicate_field(METADATA));
104                            }
105                            maybe_metadata = Some(map.next_value()?);
106                        }
107                        Field::Class => {
108                            maybe_class = Some(map.next_value()?);
109                        }
110                    }
111                }
112                let name = maybe_name.ok_or_else(|| DeError::missing_field(NAME));
113                debug!("Found ignored field @class {:?} ?", maybe_class);
114                Ok(DataCenterInfo {
115                    name: name?,
116                    metadata: maybe_metadata,
117                })
118            }
119        }
120        deserializer.deserialize_struct(DATA_CENTER_INFO, FIELDS, DataCenterInfoVisitor)
121    }
122}
123
124#[cfg(test)]
125pub mod test {
126    use super::super::amazonmetadata::tests::sample_meta_data;
127    use super::*;
128    use serde_json;
129
130    #[test]
131    fn test_serialize_data_center_info() {
132        let dci = DataCenterInfo {
133            name: DcName::Amazon,
134            metadata: Some(AmazonMetaData {
135                ami_launch_index: "001a".to_string(),
136                local_hostname: "localhost0".to_string(),
137                availability_zone: "US_East1a".to_string(),
138                instance_id: "instance1a".to_string(),
139                public_ipv4: "32.23.21.212".to_string(),
140                public_hostname: "foo.coma".to_string(),
141                ami_manifest_path: "/dev/nulla".to_string(),
142                local_ipv4: "127.0.0.12".to_string(),
143                hostname: "privatefoo.coma".to_string(),
144                ami_id: "ami0023".to_string(),
145                instance_type: "c4xlarged".to_string(),
146            }),
147        };
148        let json = sample_data_center();
149        let result = serde_json::to_string(&dci).unwrap();
150        assert_eq!(json, result);
151    }
152
153    #[test]
154    fn test_deserialize_data_center_info() {
155        let dci = DataCenterInfo {
156            name: DcName::Amazon,
157            metadata: Some(AmazonMetaData {
158                ami_launch_index: "001a".to_string(),
159                local_hostname: "localhost0".to_string(),
160                availability_zone: "US_East1a".to_string(),
161                instance_id: "instance1a".to_string(),
162                public_ipv4: "32.23.21.212".to_string(),
163                public_hostname: "foo.coma".to_string(),
164                ami_manifest_path: "/dev/nulla".to_string(),
165                local_ipv4: "127.0.0.12".to_string(),
166                hostname: "privatefoo.coma".to_string(),
167                ami_id: "ami0023".to_string(),
168                instance_type: "c4xlarged".to_string(),
169            }),
170        };
171        let json = sample_data_center();
172        println!("json {}", json);
173        let result = serde_json::from_str(&json).unwrap();
174        assert_eq!(dci, result);
175    }
176
177    fn sample_data_center() -> String {
178        format!("{{\"@class\":\"com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo\",\"name\":\"Amazon\",\"metadata\":{}}}", sample_meta_data())
179    }
180}