instant_epp/host/
info.rs

1//! Types for EPP host info request
2
3use std::net::IpAddr;
4use std::str::FromStr;
5
6use chrono::{DateTime, Utc};
7use instant_xml::{FromXml, ToXml};
8
9use super::{HostAddr, Status, XMLNS};
10use crate::common::{NoExtension, EPP_XMLNS};
11use crate::request::{Command, Transaction};
12
13impl<'a> Transaction<NoExtension> for HostInfo<'a> {}
14
15impl<'a> Command for HostInfo<'a> {
16    type Response = InfoData;
17    const COMMAND: &'static str = "info";
18}
19
20impl<'a> HostInfo<'a> {
21    pub fn new(name: &'a str) -> Self {
22        Self {
23            info: HostInfoRequestData { name },
24        }
25    }
26}
27
28// Request
29
30/// Type for data under the host `<info>` tag
31#[derive(Debug, ToXml)]
32#[xml(rename = "info", ns(XMLNS))]
33pub struct HostInfoRequestData<'a> {
34    /// The name of the host to be queried
35    name: &'a str,
36}
37
38/// Type for EPP XML `<info>` command for hosts
39#[derive(Debug, ToXml)]
40#[xml(rename = "info", ns(EPP_XMLNS))]
41pub struct HostInfo<'a> {
42    /// The instance holding the data for the host query
43    #[xml(rename = "host:info")]
44    info: HostInfoRequestData<'a>,
45}
46
47// Response
48
49/// Type that represents the `<infData>` tag for host info response
50#[derive(Debug, FromXml)]
51#[xml(rename = "infData", ns(XMLNS))]
52pub struct InfoData {
53    /// The host name
54    pub name: String,
55    /// The host ROID
56    pub roid: String,
57    /// The list of host statuses
58    #[xml(rename = "status")]
59    pub statuses: Vec<Status>,
60    /// The list of host IP addresses
61    #[xml(rename = "addr", deserialize_with = "deserialize_host_addrs")]
62    pub addresses: Vec<IpAddr>,
63    /// The epp user to whom the host belongs
64    #[xml(rename = "clID")]
65    pub client_id: String,
66    /// THe epp user that created the host
67    #[xml(rename = "crID")]
68    pub creator_id: String,
69    /// The host creation date
70    #[xml(rename = "crDate")]
71    pub created_at: DateTime<Utc>,
72    /// The epp user that last updated the host
73    #[xml(rename = "upID")]
74    pub updater_id: Option<String>,
75    /// The host last update date
76    #[xml(rename = "upDate")]
77    pub updated_at: Option<DateTime<Utc>>,
78    /// The host transfer date
79    #[xml(rename = "trDate")]
80    pub transferred_at: Option<DateTime<Utc>>,
81}
82
83fn deserialize_host_addrs(
84    into: &mut Vec<IpAddr>,
85    field: &'static str,
86    deserializer: &mut instant_xml::de::Deserializer<'_, '_>,
87) -> Result<(), instant_xml::Error> {
88    let mut addrs = Vec::new();
89    Vec::<HostAddr<'static>>::deserialize(&mut addrs, field, deserializer)?;
90
91    for addr in addrs {
92        match IpAddr::from_str(&addr.address) {
93            Ok(ip) => into.push(ip),
94            Err(_) => {
95                return Err(instant_xml::Error::UnexpectedValue(format!(
96                    "invalid IP address '{}'",
97                    &addr.address
98                )))
99            }
100        }
101    }
102
103    Ok(())
104}
105
106/*
107/// Type that represents the `<resData>` tag for host info response
108#[derive(Debug, FromXml)]
109#[xml(rename = "infData", ns(XMLNS))]
110pub struct HostInfoResponse {
111    /// Data under the `<infData>` tag
112    #[xml(rename = "infData")]
113    pub info_data: HostInfoResponseData,
114}
115*/
116
117#[cfg(test)]
118mod tests {
119    use chrono::{TimeZone, Utc};
120
121    use super::{HostInfo, IpAddr};
122    use crate::host::Status;
123    use crate::response::ResultCode;
124    use crate::tests::{assert_serialized, response_from_file, CLTRID, SUCCESS_MSG, SVTRID};
125
126    #[test]
127    fn command() {
128        let object = HostInfo::new("ns1.eppdev-1.com");
129        assert_serialized("request/host/info.xml", &object);
130    }
131
132    #[test]
133    fn response() {
134        let object = response_from_file::<HostInfo>("response/host/info.xml");
135        let result = object.res_data().unwrap();
136
137        assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
138        assert_eq!(object.result.message, SUCCESS_MSG);
139        assert_eq!(result.name, "host2.eppdev-1.com");
140        assert_eq!(result.roid, "UNDEF-ROID");
141        assert_eq!(result.statuses[0], Status::Ok);
142        assert_eq!(result.addresses[0], IpAddr::from([29, 245, 122, 14]));
143        assert_eq!(
144            result.addresses[1],
145            IpAddr::from([0x2404, 0x6800, 0x4001, 0x801, 0, 0, 0, 0x200e])
146        );
147        assert_eq!(result.client_id, "eppdev");
148        assert_eq!(result.creator_id, "creator");
149        assert_eq!(
150            result.created_at,
151            Utc.with_ymd_and_hms(2021, 7, 26, 5, 28, 55).unwrap()
152        );
153        assert_eq!(*(result.updater_id.as_ref().unwrap()), "creator");
154        assert_eq!(
155            result.updated_at,
156            Utc.with_ymd_and_hms(2021, 7, 26, 5, 28, 55).single()
157        );
158        assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
159        assert_eq!(object.tr_ids.server_tr_id, SVTRID);
160    }
161}