use std::net::IpAddr;
use std::str::FromStr;
use chrono::{DateTime, Utc};
use instant_xml::{FromXml, ToXml};
use super::{HostAddr, Status, XMLNS};
use crate::common::{NoExtension, EPP_XMLNS};
use crate::request::{Command, Transaction};
impl<'a> Transaction<NoExtension> for HostInfo<'a> {}
impl<'a> Command for HostInfo<'a> {
type Response = InfoData;
const COMMAND: &'static str = "info";
}
impl<'a> HostInfo<'a> {
pub fn new(name: &'a str) -> Self {
Self {
info: HostInfoRequestData { name },
}
}
}
#[derive(Debug, ToXml)]
#[xml(rename = "info", ns(XMLNS))]
pub struct HostInfoRequestData<'a> {
name: &'a str,
}
#[derive(Debug, ToXml)]
#[xml(rename = "info", ns(EPP_XMLNS))]
pub struct HostInfo<'a> {
#[xml(rename = "host:info")]
info: HostInfoRequestData<'a>,
}
#[derive(Debug, FromXml)]
#[xml(rename = "infData", ns(XMLNS))]
pub struct InfoData {
pub name: String,
pub roid: String,
#[xml(rename = "status")]
pub statuses: Vec<Status>,
#[xml(rename = "addr", deserialize_with = "deserialize_host_addrs")]
pub addresses: Vec<IpAddr>,
#[xml(rename = "clID")]
pub client_id: String,
#[xml(rename = "crID")]
pub creator_id: String,
#[xml(rename = "crDate")]
pub created_at: DateTime<Utc>,
#[xml(rename = "upID")]
pub updater_id: Option<String>,
#[xml(rename = "upDate")]
pub updated_at: Option<DateTime<Utc>>,
#[xml(rename = "trDate")]
pub transferred_at: Option<DateTime<Utc>>,
}
fn deserialize_host_addrs(
into: &mut Vec<IpAddr>,
field: &'static str,
deserializer: &mut instant_xml::de::Deserializer<'_, '_>,
) -> Result<(), instant_xml::Error> {
let mut addrs = Vec::new();
Vec::<HostAddr<'static>>::deserialize(&mut addrs, field, deserializer)?;
for addr in addrs {
match IpAddr::from_str(&addr.address) {
Ok(ip) => into.push(ip),
Err(_) => {
return Err(instant_xml::Error::UnexpectedValue(format!(
"invalid IP address '{}'",
&addr.address
)))
}
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use chrono::{TimeZone, Utc};
use super::{HostInfo, IpAddr};
use crate::host::Status;
use crate::response::ResultCode;
use crate::tests::{assert_serialized, response_from_file, CLTRID, SUCCESS_MSG, SVTRID};
#[test]
fn command() {
let object = HostInfo::new("ns1.eppdev-1.com");
assert_serialized("request/host/info.xml", &object);
}
#[test]
fn response() {
let object = response_from_file::<HostInfo>("response/host/info.xml");
let result = object.res_data().unwrap();
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
assert_eq!(object.result.message, SUCCESS_MSG);
assert_eq!(result.name, "host2.eppdev-1.com");
assert_eq!(result.roid, "UNDEF-ROID");
assert_eq!(result.statuses[0], Status::Ok);
assert_eq!(result.addresses[0], IpAddr::from([29, 245, 122, 14]));
assert_eq!(
result.addresses[1],
IpAddr::from([0x2404, 0x6800, 0x4001, 0x801, 0, 0, 0, 0x200e])
);
assert_eq!(result.client_id, "eppdev");
assert_eq!(result.creator_id, "creator");
assert_eq!(
result.created_at,
Utc.with_ymd_and_hms(2021, 7, 26, 5, 28, 55).unwrap()
);
assert_eq!(*(result.updater_id.as_ref().unwrap()), "creator");
assert_eq!(
result.updated_at,
Utc.with_ymd_and_hms(2021, 7, 26, 5, 28, 55).single()
);
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
assert_eq!(object.tr_ids.server_tr_id, SVTRID);
}
}