rust_eureka/request/
amazonmetadata.rs

1use serde::ser::{Serialize, Serializer, SerializeStruct};
2use serde::de::{Deserialize, Deserializer, Visitor, Error as DeError, MapAccess};
3use std::fmt;
4
5const AMI_LAUNCH_INDEX: &'static str = "ami-launch-index";
6const LOCAL_HOSTNAME: &'static str = "local-hostname";
7const AVAILABILITY_ZONE: &'static str = "availability-zone";
8const INSTANCE_ID: &'static str = "instance-id";
9const PUBLIC_IPV4: &'static str = "public-ipv4";
10const PUBLIC_HOSTNAME: &'static str = "public-hostname";
11const AMI_MANIFEST_PATH: &'static str = "ami-manifest-path";
12const LOCAL_IPV4: &'static str = "local-ipv4";
13const HOSTNAME: &'static str = "hostname";
14const AMI_ID: &'static str = "ami-id";
15const INSTANCE_TYPE: &'static str = "instance-type";
16const JSON_FIELDS: &'static [&'static str] = &[AMI_LAUNCH_INDEX, LOCAL_HOSTNAME, AVAILABILITY_ZONE, INSTANCE_ID,
17    PUBLIC_IPV4, PUBLIC_HOSTNAME, AMI_MANIFEST_PATH, LOCAL_IPV4,
18    HOSTNAME, AMI_ID, INSTANCE_TYPE];
19const RUST_FIELDS: &'static [&'static str] = &["ami_launch_index", "local_hostname", "availability_zone", "instance_id",
20    "public_ip4", "public_hostname", "ami_manifest_path", "local_ip4", "hostname", "ami_id", "instance_type"];
21const AMAZON_META_DATA: &'static str = "AmazonMetaData";
22
23#[derive(Debug, PartialEq)]
24pub struct AmazonMetaData {
25    pub ami_launch_index: String,
26    pub local_hostname: String,
27    pub availability_zone: String,
28    pub instance_id: String,
29    pub public_ip4: String,
30    pub public_hostname: String,
31    pub ami_manifest_path: String,
32    pub local_ip4: String,
33    pub hostname: String,
34    pub ami_id: String,
35    pub instance_type: String
36}
37
38impl Serialize for AmazonMetaData {
39    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where
40        S: Serializer {
41        let mut s = serializer.serialize_struct(AMAZON_META_DATA, 11)?;
42        s.serialize_field(AMI_LAUNCH_INDEX, &self.ami_launch_index)?;
43        s.serialize_field(LOCAL_HOSTNAME, &self.local_hostname)?;
44        s.serialize_field(AVAILABILITY_ZONE, &self.availability_zone)?;
45        s.serialize_field(INSTANCE_ID, &self.instance_id)?;
46        s.serialize_field(PUBLIC_IPV4, &self.public_ip4)?;
47        s.serialize_field(PUBLIC_HOSTNAME, &self.public_hostname)?;
48        s.serialize_field(AMI_MANIFEST_PATH, &self.ami_manifest_path)?;
49        s.serialize_field(LOCAL_IPV4, &self.local_ip4)?;
50        s.serialize_field(HOSTNAME, &self.hostname)?;
51        s.serialize_field(AMI_ID, &self.ami_id)?;
52        s.serialize_field(INSTANCE_TYPE, &self.instance_type)?;
53        s.end()
54    }
55}
56
57impl<'de> Deserialize<'de> for AmazonMetaData {
58    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where
59        D: Deserializer<'de> {
60        enum Field { AmiLaunchIndex, LocalHostname, AvailabilityZone, InstanceId, PublicIp4, PublicHostname, AmiManifestPath, LocalIp4, Hostname, AmiId, InstanceType };
61
62        impl<'de> Deserialize<'de> for Field {
63            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where
64                D: Deserializer<'de> {
65                struct FieldVisitor;
66
67                impl<'de> Visitor<'de> for FieldVisitor {
68                    type Value = Field;
69
70                    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
71                        formatter.write_str("An AmazonMetaData field (see schema)")
72                    }
73
74                    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> where
75                        E: DeError {
76                        match v {
77                            AMI_LAUNCH_INDEX => Ok(Field::AmiLaunchIndex),
78                            LOCAL_HOSTNAME => Ok(Field::LocalHostname),
79                            AVAILABILITY_ZONE => Ok(Field::AvailabilityZone),
80                            INSTANCE_ID => Ok(Field::InstanceId),
81                            PUBLIC_IPV4 => Ok(Field::PublicIp4),
82                            PUBLIC_HOSTNAME => Ok(Field::PublicHostname),
83                            AMI_MANIFEST_PATH => Ok(Field::AmiManifestPath),
84                            LOCAL_IPV4 => Ok(Field::LocalIp4),
85                            HOSTNAME => Ok(Field::Hostname),
86                            AMI_ID => Ok(Field::AmiId),
87                            INSTANCE_TYPE => Ok(Field::InstanceType),
88                            _ => Err(DeError::unknown_field(v, JSON_FIELDS))
89                        }
90                    }
91                }
92
93                deserializer.deserialize_identifier(FieldVisitor)
94            }
95        }
96
97        struct AmazonMetaDataVisitor;
98
99        impl<'de> Visitor<'de> for AmazonMetaDataVisitor {
100            type Value = AmazonMetaData;
101
102            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
103                formatter.write_str("struct AmazonMetaDataVisitor")
104            }
105
106            fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error> where
107                A: MapAccess<'de> {
108                let mut maybe_ami_launch_index = None;
109                let mut maybe_local_hostname = None;
110                let mut maybe_availability_zone = None;
111                let mut maybe_instance_id = None;
112                let mut maybe_public_ip4 = None;
113                let mut maybe_public_hostname = None;
114                let mut maybe_ami_manifest_path = None;
115                let mut maybe_local_ip4 = None;
116                let mut maybe_hostname = None;
117                let mut maybe_ami_id = None;
118                let mut maybe_instance_type = None;
119
120                while let Some(key) = map.next_key()? {
121                    match key {
122                        Field::AmiLaunchIndex => {
123                            if maybe_ami_launch_index.is_some() {
124                                return Err(DeError::duplicate_field(AMI_LAUNCH_INDEX));
125                            }
126                            maybe_ami_launch_index = Some(map.next_value()?)
127                        },
128                        Field::LocalHostname => {
129                            if maybe_local_hostname.is_some() {
130                                return Err(DeError::duplicate_field(LOCAL_HOSTNAME));
131                            }
132                            maybe_local_hostname = Some(map.next_value()?)
133                        },
134                        Field::AvailabilityZone => {
135                            if maybe_availability_zone.is_some() {
136                                return Err(DeError::duplicate_field(AVAILABILITY_ZONE));
137                            }
138                            maybe_availability_zone= Some(map.next_value()?)
139                        },
140                        Field::InstanceId => {
141                            if maybe_instance_id.is_some() {
142                                return Err(DeError::duplicate_field(INSTANCE_ID));
143                            }
144                            maybe_instance_id= Some(map.next_value()?)
145                        },
146                        Field::PublicIp4 => {
147                            if maybe_public_ip4.is_some() {
148                                return Err(DeError::duplicate_field(PUBLIC_IPV4));
149                            }
150                            maybe_public_ip4= Some(map.next_value()?)
151                        },
152                        Field::PublicHostname => {
153                            if maybe_public_hostname.is_some() {
154                                return Err(DeError::duplicate_field(PUBLIC_HOSTNAME));
155                            }
156                            maybe_public_hostname= Some(map.next_value()?)
157                        },
158                        Field::AmiManifestPath => {
159                            if maybe_ami_manifest_path.is_some() {
160                                return Err(DeError::duplicate_field(AMI_MANIFEST_PATH));
161                            }
162                            maybe_ami_manifest_path= Some(map.next_value()?)
163                        },
164                        Field::LocalIp4 => {
165                            if maybe_local_ip4.is_some() {
166                                return Err(DeError::duplicate_field(LOCAL_IPV4));
167                            }
168                            maybe_local_ip4= Some(map.next_value()?)
169                        },
170                        Field::Hostname => {
171                            if maybe_hostname.is_some() {
172                                return Err(DeError::duplicate_field(HOSTNAME));
173                            }
174                            maybe_hostname= Some(map.next_value()?)
175                        },
176                        Field::AmiId => {
177                            if maybe_ami_id.is_some() {
178                                return Err(DeError::duplicate_field(AMI_ID));
179                            }
180                            maybe_ami_id= Some(map.next_value()?)
181                        },
182                        Field::InstanceType => {
183                            if maybe_instance_type.is_some() {
184                                return Err(DeError::duplicate_field(INSTANCE_TYPE));
185                            }
186                            maybe_instance_type= Some(map.next_value()?)
187                        }
188                    }
189                }
190
191                let ami_launch_index = maybe_ami_launch_index.ok_or_else(|| DeError::missing_field(AMI_LAUNCH_INDEX));
192                let local_hostname = maybe_local_hostname.ok_or_else(|| DeError::missing_field(LOCAL_HOSTNAME));
193                let availability_zone = maybe_availability_zone.ok_or_else(|| DeError::missing_field(AVAILABILITY_ZONE));
194                let instance_id = maybe_instance_id.ok_or_else(|| DeError::missing_field(INSTANCE_ID));
195                let public_ip4 = maybe_public_ip4.ok_or_else(|| DeError::missing_field(PUBLIC_IPV4));
196                let public_hostname = maybe_public_hostname.ok_or_else(|| DeError::missing_field(PUBLIC_HOSTNAME));
197                let ami_manifest_path = maybe_ami_manifest_path.ok_or_else(|| DeError::missing_field(AMI_MANIFEST_PATH));
198                let local_ip4 = maybe_local_ip4.ok_or_else(|| DeError::missing_field(LOCAL_IPV4));
199                let hostname = maybe_hostname.ok_or_else(|| DeError::missing_field(HOSTNAME));
200                let ami_id = maybe_ami_id.ok_or_else(|| DeError::missing_field(AMI_ID));
201                let instance_type = maybe_instance_type.ok_or_else(|| DeError::missing_field(INSTANCE_TYPE));
202
203                Ok(AmazonMetaData {
204                    ami_launch_index: ami_launch_index?,
205                    local_hostname: local_hostname?,
206                    availability_zone: availability_zone?,
207                    instance_id: instance_id?,
208                    public_ip4: public_ip4?,
209                    public_hostname: public_hostname?,
210                    ami_manifest_path: ami_manifest_path?,
211                    local_ip4: local_ip4?,
212                    hostname: hostname?,
213                    ami_id: ami_id?,
214                    instance_type: instance_type?
215                })
216            }
217        }
218        deserializer.deserialize_struct(AMAZON_META_DATA, RUST_FIELDS, AmazonMetaDataVisitor)
219    }
220}
221
222#[cfg(test)]
223pub mod test {
224    use super::*;
225    use serde_json;
226
227    #[test]
228    fn test_serialize_amazon_meta_data() {
229        let md = AmazonMetaData {
230            ami_launch_index: "001a".to_string(),
231            local_hostname: "localhost0".to_string(),
232            availability_zone: "US_East1a".to_string(),
233            instance_id: "instance1a".to_string(),
234            public_ip4: "32.23.21.212".to_string(),
235            public_hostname: "foo.coma".to_string(),
236            ami_manifest_path: "/dev/nulla".to_string(),
237            local_ip4: "127.0.0.12".to_string(),
238            hostname: "privatefoo.coma".to_string(),
239            ami_id: "ami0023".to_string(),
240            instance_type: "c4xlarged".to_string()
241        };
242        let json = sample_meta_data();
243
244        let result = serde_json::to_string(&md).unwrap();
245        assert_eq!(json, result);
246    }
247
248    #[test]
249    fn test_deserialize_amazon_meta_data() {
250        let md = AmazonMetaData {
251            ami_launch_index: "001a".to_string(),
252            local_hostname: "localhost0".to_string(),
253            availability_zone: "US_East1a".to_string(),
254            instance_id: "instance1a".to_string(),
255            public_ip4: "32.23.21.212".to_string(),
256            public_hostname: "foo.coma".to_string(),
257            ami_manifest_path: "/dev/nulla".to_string(),
258            local_ip4: "127.0.0.12".to_string(),
259            hostname: "privatefoo.coma".to_string(),
260            ami_id: "ami0023".to_string(),
261            instance_type: "c4xlarged".to_string()
262        };
263        let json = sample_meta_data();
264        let result = serde_json::from_str(&json).unwrap();
265        assert_eq!(md, result);
266    }
267
268    pub fn sample_meta_data() -> String {
269        r#"{ "ami-launch-index": "001a",
270            "local-hostname": "localhost0",
271            "availability-zone": "US_East1a",
272            "instance-id": "instance1a",
273            "public-ipv4": "32.23.21.212",
274            "public-hostname": "foo.coma",
275            "ami-manifest-path": "/dev/nulla",
276            "local-ipv4": "127.0.0.12",
277            "hostname": "privatefoo.coma",
278            "ami-id": "ami0023",
279            "instance-type": "c4xlarged" }"#
280            .to_string()
281            .replace(" ", "")
282            .replace("\n", "")
283    }
284}