rust_eureka/request/
datacenterinfo.rs

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