rust_eureka/request/
instance.rs

1use serde::ser::{Serialize, Serializer, SerializeStruct};
2use serde::de::{Deserialize, Deserializer, Visitor, Error as DeError, MapAccess};
3use serde_json::{Map, Value};
4use std::fmt;
5use std::str::FromStr;
6use super::DataCenterInfo;
7use super::LeaseInfo;
8use super::Status;
9
10// Field name constants
11const INSTANCE: &'static str = "Instance";
12const HOST_NAME: &'static str = "hostName";
13const APP: &'static str = "app";
14const IP_ADDR: &'static str = "ipAddr";
15const VIP_ADDRESS: &'static str = "vipAddress";
16const SECURE_VIP_ADDRESS: &'static str = "secureVipAddress";
17const STATUS: &'static str = "status";
18const PORT: &'static str = "port";
19const SECURE_PORT: &'static str = "securePort";
20const HOME_PAGE_URL: &'static str = "homePageUrl";
21const STATUS_PAGE_URL: &'static str = "statusPageUrl";
22const HEALTH_CHECK_URL: &'static str = "healthCheckUrl";
23const DATA_CENTER_INFO: &'static str = "dataCenterInfo";
24const LEASE_INFO: &'static str = "leaseInfo";
25const METADATA: &'static str = "metadata";
26const OVERRIDDENSTATUS: &'static str = "overriddenstatus";
27const COUNTRY_ID: &'static str = "countryId";
28const JSON_FIELDS: &'static [&'static str] = &[INSTANCE, HOST_NAME, APP, IP_ADDR, VIP_ADDRESS, SECURE_VIP_ADDRESS,
29    STATUS, PORT, SECURE_PORT, HOME_PAGE_URL, STATUS_PAGE_URL, HEALTH_CHECK_URL,
30    DATA_CENTER_INFO, LEASE_INFO, METADATA, OVERRIDDENSTATUS, COUNTRY_ID];
31const RUST_FIELDS: &'static [&'static str] = &["host_name", "app", "ip_addr", "vip_address", "secure_vip_address",
32    "status", "port Option", "secure_port", "homepage_url", "status_page_url",
33    "health_check_url", "data_center_info", "lease_info", "metadata", OVERRIDDENSTATUS, "country_id"];
34
35const PORT_DOLLAR: &'static str = "$";
36const PORT_ENABLED: &'static str = "@enabled";
37const PORT_FIELDS: &'static [&'static str] = &[PORT_DOLLAR, PORT_ENABLED];
38
39#[derive(Debug, PartialEq)]
40pub struct Instance {
41    pub host_name: String,
42    pub app: String,
43    pub ip_addr: String,
44    pub vip_address: String,
45    pub secure_vip_address: String,
46    pub status: Status,
47    pub port: Option<u16>,
48    pub secure_port: Option<u16>,
49    pub homepage_url: String,
50    pub status_page_url: String,
51    pub health_check_url: String,
52    pub data_center_info: DataCenterInfo,
53    pub lease_info: Option<LeaseInfo>,
54    pub metadata: Map<String, Value>
55}
56
57struct Port {
58    port: u16
59}
60
61impl Port {
62    fn new(port: u16) -> Port {
63        Port { port: port }
64    }
65}
66
67impl Serialize for Port {
68    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where
69        S: Serializer {
70        let mut s = serializer.serialize_struct("Port", 2)?;
71        s.serialize_field(PORT_DOLLAR, &self.port.to_string())?;
72        s.serialize_field(PORT_ENABLED, "true")?;
73        s.end()
74    }
75}
76
77impl<'de> Deserialize<'de> for Port {
78    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where
79        D: Deserializer<'de> {
80        enum Field { DollarSign, Enabled };
81
82
83        impl<'de> Deserialize<'de> for Field {
84            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where
85                D: Deserializer<'de> {
86                struct FieldVisitor;
87
88                impl<'de> Visitor<'de> for FieldVisitor {
89                    type Value = Field;
90
91                    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
92                        formatter.write_str("'$' or 'enabled'")
93                    }
94                    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> where
95                        E: DeError, {
96                        match v {
97                            PORT_DOLLAR => Ok(Field::DollarSign),
98                            PORT_ENABLED => Ok(Field::Enabled),
99                            _ => Err(DeError::unknown_field(v, PORT_FIELDS))
100                        }
101                    }
102                }
103                deserializer.deserialize_identifier(FieldVisitor)
104            }
105        }
106
107        struct PortVisitor;
108        impl<'de> Visitor<'de> for PortVisitor {
109            type Value = Port;
110
111            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
112                formatter.write_str("struct Port")
113            }
114
115            fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error> where
116                A: MapAccess<'de>, {
117                let mut maybe_dollar: Option<String> = None;
118                let mut maybe_enabled: Option<String> = None;
119
120                while let Some(key) = map.next_key()? {
121                    match key {
122                        Field::DollarSign => {
123                            if maybe_dollar.is_some() {
124                                return Err(DeError::duplicate_field(PORT_DOLLAR));
125                            }
126                            maybe_dollar = Some(map.next_value()?);
127                        }
128                        Field::Enabled => {
129                            if maybe_enabled.is_some() {
130                                return Err(DeError::duplicate_field(PORT_ENABLED));
131                            }
132                            maybe_enabled = Some(map.next_value()?);
133                        }
134                    }
135                }
136
137                let dollar = maybe_dollar
138                    .map(|s| u16::from_str(s.as_ref()).unwrap())
139                    .ok_or_else(|| DeError::missing_field(PORT_DOLLAR))?;
140                maybe_enabled.ok_or_else(|| DeError::missing_field(PORT_ENABLED))?;
141                // ignore enabled
142                Ok(Port::new(dollar))
143            }
144        }
145
146        deserializer.deserialize_struct("Port", PORT_FIELDS, PortVisitor)
147    }
148}
149
150impl Serialize for Instance {
151    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where
152        S: Serializer {
153        let mut s = serializer.serialize_struct(INSTANCE, 14)?;
154        s.serialize_field(HOST_NAME, &self.host_name)?;
155        s.serialize_field(APP, &self.app)?;
156        s.serialize_field(IP_ADDR, &self.ip_addr)?;
157        s.serialize_field(VIP_ADDRESS, &self.vip_address)?;
158        s.serialize_field(SECURE_VIP_ADDRESS, &self.secure_vip_address)?;
159        s.serialize_field(STATUS, &self.status)?;
160
161        if let &Some(p) = &self.port {
162            let port = Port::new(p);
163            s.serialize_field(PORT, &port)?;
164        }
165
166        if let &Some(p) = &self.secure_port {
167            let port = Port::new(p);
168            s.serialize_field(SECURE_PORT, &port)?;
169        }
170
171        s.serialize_field(HOME_PAGE_URL, &self.homepage_url)?;
172        s.serialize_field(STATUS_PAGE_URL, &self.status_page_url)?;
173        s.serialize_field(HEALTH_CHECK_URL, &self.health_check_url)?;
174        s.serialize_field(DATA_CENTER_INFO, &self.data_center_info)?;
175
176        if let &Some(ref lease_info) = &self.lease_info {
177            s.serialize_field(LEASE_INFO, lease_info)?;
178        }
179
180        if !&self.metadata.is_empty() {
181            s.serialize_field(METADATA, &self.metadata)?;
182        }
183
184        s.end()
185    }
186}
187
188impl<'de> Deserialize<'de> for Instance {
189    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where
190        D: Deserializer<'de> {
191        enum Field {
192            HostName,
193            App,
194            IpAddr,
195            VipAddress,
196            SecureVipAddress,
197            Status,
198            Port,
199            SecurePort,
200            HomepageUrl,
201            StatusPageUrl,
202            HealthCheckUrl,
203            DataCenterInfo,
204            LeaseInfo,
205            Metadata
206        }
207
208        impl<'de> Deserialize<'de> for Field {
209            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where
210                D: Deserializer<'de> {
211                struct FieldVisitor;
212
213                impl<'de> Visitor<'de> for FieldVisitor {
214                    type Value = Field;
215
216                    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
217                        formatter.write_str("An Instance field (see schema)")
218                    }
219                    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> where
220                        E: DeError {
221                        match v {
222                            HOST_NAME => Ok(Field::HostName),
223                            APP => Ok(Field::App),
224                            IP_ADDR => Ok(Field::IpAddr),
225                            VIP_ADDRESS => Ok(Field::VipAddress),
226                            SECURE_VIP_ADDRESS => Ok(Field::SecureVipAddress),
227                            STATUS => Ok(Field::Status),
228                            PORT => Ok(Field::Port),
229                            SECURE_PORT => Ok(Field::SecurePort),
230                            HOME_PAGE_URL => Ok(Field::HomepageUrl),
231                            STATUS_PAGE_URL => Ok(Field::StatusPageUrl),
232                            HEALTH_CHECK_URL => Ok(Field::HealthCheckUrl),
233                            DATA_CENTER_INFO => Ok(Field::DataCenterInfo),
234                            LEASE_INFO => Ok(Field::LeaseInfo),
235                            METADATA => Ok(Field::Metadata),
236                            _ => Err(DeError::unknown_field(v, JSON_FIELDS))
237                        }
238                    }
239                }
240
241                deserializer.deserialize_identifier(FieldVisitor)
242            }
243        }
244
245        struct InstanceVisitor;
246
247        impl<'de> Visitor<'de> for InstanceVisitor {
248            type Value = Instance;
249
250            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
251                formatter.write_str("struct Instance")
252            }
253
254            fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error> where
255                A: MapAccess<'de> {
256                let mut maybe_host_name = None;
257                let mut maybe_app = None;
258                let mut maybe_ip_addr = None;
259                let mut maybe_vip_address = None;
260                let mut maybe_secure_vip_address = None;
261                let mut maybe_status = None;
262                let mut maybe_port: Option<Port> = None;
263                let mut maybe_secure_port: Option<Port> = None;
264                let mut maybe_homepage_url = None;
265                let mut maybe_status_page_url = None;
266                let mut maybe_health_check_url = None;
267                let mut maybe_data_center_info = None;
268                let mut maybe_lease_info = None;
269                let mut maybe_metadata = None;
270
271                while let Some(key) = map.next_key()? {
272                    match key {
273                        Field::HomepageUrl => {
274                            if maybe_homepage_url.is_some() {
275                                return Err(DeError::duplicate_field(HOME_PAGE_URL));
276                            }
277                            maybe_homepage_url = Some(map.next_value()?);
278                        },
279                        Field::App => {
280                            if maybe_app.is_some() {
281                                return Err(DeError::duplicate_field(APP));
282                            }
283                            maybe_app = Some(map.next_value()?);
284                        },
285                        Field::IpAddr => {
286                            if maybe_ip_addr.is_some() {
287                                return Err(DeError::duplicate_field(IP_ADDR));
288                            }
289                            maybe_ip_addr = Some(map.next_value()?);
290                        },
291                        Field::VipAddress => {
292                            if maybe_vip_address.is_some() {
293                                return Err(DeError::duplicate_field(VIP_ADDRESS));
294                            }
295                            maybe_vip_address = Some(map.next_value()?);
296                        },
297                        Field::SecureVipAddress => {
298                            if maybe_secure_vip_address.is_some() {
299                                return Err(DeError::duplicate_field(SECURE_VIP_ADDRESS));
300                            }
301                            maybe_secure_vip_address = Some(map.next_value()?);
302                        },
303                        Field::Status => {
304                            if maybe_status.is_some() {
305                                return Err(DeError::duplicate_field(STATUS));
306                            }
307                            maybe_status = Some(map.next_value()?);
308                        },
309                        Field::Port => {
310                            if maybe_port.is_some() {
311                                return Err(DeError::duplicate_field(PORT));
312                            }
313                            maybe_port = Some(map.next_value()?);
314                        },
315                        Field::SecurePort => {
316                            if maybe_secure_port.is_some() {
317                                return Err(DeError::duplicate_field(SECURE_PORT));
318                            }
319                            maybe_secure_port = Some(map.next_value()?);
320                        },
321                        Field::StatusPageUrl => {
322                            if maybe_status_page_url.is_some() {
323                                return Err(DeError::duplicate_field(STATUS_PAGE_URL));
324                            }
325                            maybe_status_page_url = Some(map.next_value()?);
326                        },
327                        Field::HealthCheckUrl => {
328                            if maybe_health_check_url.is_some() {
329                                return Err(DeError::duplicate_field(HEALTH_CHECK_URL));
330                            }
331                            maybe_health_check_url = Some(map.next_value()?);
332                        },
333                        Field::DataCenterInfo => {
334                            if maybe_data_center_info.is_some() {
335                                return Err(DeError::duplicate_field(DATA_CENTER_INFO));
336                            }
337                            maybe_data_center_info = Some(map.next_value()?);
338                        },
339                        Field::LeaseInfo => {
340                            if maybe_lease_info.is_some() {
341                                return Err(DeError::duplicate_field(LEASE_INFO));
342                            }
343                            maybe_lease_info = Some(map.next_value()?);
344                        },
345                        Field::Metadata => {
346                            if maybe_metadata.is_some() {
347                                return Err(DeError::duplicate_field(METADATA));
348                            }
349                            maybe_metadata = Some(map.next_value()?);
350                        },
351                        Field::HostName => {
352                            if maybe_host_name.is_some() {
353                                return Err(DeError::duplicate_field(HOST_NAME));
354                            }
355                            maybe_host_name = Some(map.next_value()?);
356                        }
357                    }
358                }
359
360                let host_name = maybe_host_name.ok_or_else(|| DeError::missing_field(HOST_NAME));
361                let app = maybe_app.ok_or_else(|| DeError::missing_field(APP));
362                let ip_addr = maybe_ip_addr.ok_or_else(|| DeError::missing_field(IP_ADDR));
363                let vip_address = maybe_vip_address.ok_or_else(|| DeError::missing_field(VIP_ADDRESS));
364                let secure_vip_address = maybe_secure_vip_address.ok_or_else(|| DeError::missing_field(SECURE_VIP_ADDRESS));
365                let status = maybe_status.ok_or_else(|| DeError::missing_field(STATUS));
366                let homepage_url = maybe_homepage_url.ok_or_else(|| DeError::missing_field(HOME_PAGE_URL));
367                let status_page_url = maybe_status_page_url.ok_or_else(|| DeError::missing_field(STATUS_PAGE_URL));
368                let health_check_url = maybe_health_check_url.ok_or_else(|| DeError::missing_field(HEALTH_CHECK_URL));
369                let data_center_info = maybe_data_center_info.ok_or_else(|| DeError::missing_field(DATA_CENTER_INFO));
370                let metadata = maybe_metadata.unwrap_or(Map::new());
371
372                Ok(Instance {
373                    host_name: host_name?,
374                    app: app?,
375                    ip_addr: ip_addr?,
376                    vip_address: vip_address?,
377                    secure_vip_address: secure_vip_address?,
378                    status: status?,
379                    port: maybe_port.map(|p| p.port),
380                    secure_port: maybe_secure_port.map(|p| p.port),
381                    homepage_url: homepage_url?,
382                    status_page_url: status_page_url?,
383                    health_check_url: health_check_url?,
384                    data_center_info: data_center_info?,
385                    lease_info: maybe_lease_info,
386                    metadata: metadata,
387                })
388            }
389        }
390        deserializer.deserialize_struct(INSTANCE, RUST_FIELDS, InstanceVisitor)
391    }
392}
393
394#[cfg(test)]
395pub mod tests {
396    use super::*;
397    use serde_json;
398    use super::super::DcName;
399    use super::super::AmazonMetaData;
400
401    #[test]
402    fn test_instance_serialization() {
403        let json = build_test_instance_json();
404        let instance = build_test_instance();
405        let result = serde_json::to_string(&instance).unwrap();
406
407        //        let combined = json.chars().zip(result.chars());
408        //        for (a, b) in combined {
409        //            print!("{}", b);
410        //            assert_eq!(a, b);
411        //        }
412        assert_eq!(json, result);
413    }
414
415    #[test]
416    fn test_instance_deserialization() {
417        let json = build_test_instance_json();
418        let instance = build_test_instance();
419        let result = serde_json::from_str(&json).unwrap();
420        assert_eq!(instance, result);
421    }
422
423    pub fn build_test_instance_json() -> String {
424        r#"{
425           "hostName": "Foo",
426           "app": "Bar",
427           "ipAddr": "3.128.2.12",
428           "vipAddress": "127.0.0.1",
429           "secureVipAddress": "127.0.0.2",
430           "status": "UP",
431           "port": { "$": "80", "@enabled": "true" },
432           "securePort": { "$": "443", "@enabled": "true" },
433           "homePageUrl": "http://google.com",
434           "statusPageUrl": "http://nytimes.com",
435           "healthCheckUrl": "http://washingtonpost.com",
436           "dataCenterInfo": { "@class": "com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo", "name":"Amazon","metadata":
437           {
438                "ami-launch-index": "001a",
439                "local-hostname": "localhost0",
440                "availability-zone": "US_East1a",
441                "instance-id": "instance1a",
442                "public-ipv4": "32.23.21.212",
443                "public-hostname": "foo.coma",
444                "ami-manifest-path": "/dev/nulla",
445                "local-ipv4": "127.0.0.12",
446                "hostname": "privatefoo.coma",
447                "ami-id": "ami0023",
448                "instance-type": "c4xlarged"
449           }},
450           "leaseInfo": {"evictionDurationInSecs":9600},
451           "metadata": {"something": "somethingelse"}
452        }"#
453            .to_string()
454            .replace(" ", "")
455            .replace("\n", "")
456    }
457
458    pub fn build_test_instance() -> Instance {
459        let mut metadata = Map::new();
460        metadata.insert("something".to_owned(), Value::String("somethingelse".to_owned()));
461        Instance {
462            host_name: "Foo".to_string(),
463            app: "Bar".to_string(),
464            ip_addr: "3.128.2.12".to_string(),
465            vip_address: "127.0.0.1".to_string(),
466            secure_vip_address: "127.0.0.2".to_string(),
467            status: Status::Up,
468            port: Some(80),
469            secure_port: Some(443),
470            homepage_url: "http://google.com".to_string(),
471            status_page_url: "http://nytimes.com".to_string(),
472            health_check_url: "http://washingtonpost.com".to_string(),
473            data_center_info: DataCenterInfo {
474                name: DcName::Amazon,
475                metadata: Some(AmazonMetaData {
476                    ami_launch_index: "001a".to_string(),
477                    local_hostname: "localhost0".to_string(),
478                    availability_zone: "US_East1a".to_string(),
479                    instance_id: "instance1a".to_string(),
480                    public_ip4: "32.23.21.212".to_string(),
481                    public_hostname: "foo.coma".to_string(),
482                    ami_manifest_path: "/dev/nulla".to_string(),
483                    local_ip4: "127.0.0.12".to_string(),
484                    hostname: "privatefoo.coma".to_string(),
485                    ami_id: "ami0023".to_string(),
486                    instance_type: "c4xlarged".to_string()
487                })
488            },
489            lease_info: Some(LeaseInfo {
490                eviction_duration_in_secs: Some(9600)
491            }),
492            metadata: metadata
493        }
494    }
495}
496