diff --git a/Cargo.toml b/Cargo.toml index a10791f..a0dcf33 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ default = ["tokio-rustls"] async-trait = "0.1.52" celes = "2.1" chrono = { version = "0.4.23", features = ["serde"] } -quick-xml = { version = "0.26", features = [ "serialize" ] } +instant-xml = { version = "0.1", features = ["chrono"] } serde = { version = "1.0", features = ["derive"] } tokio = { version = "1.0", features = ["io-util", "net", "time"] } tokio-rustls = { version = "0.23", optional = true } @@ -26,3 +26,4 @@ regex = "1.5" tokio = { version = "1.0", features = ["macros", "rt-multi-thread"] } tokio-test = "0.4" tracing-subscriber = "0.3.3" +similar-asserts = "1.4.2" diff --git a/src/client.rs b/src/client.rs index dd2e21c..054f043 100644 --- a/src/client.rs +++ b/src/client.rs @@ -18,9 +18,9 @@ use crate::common::{Certificate, NoExtension, PrivateKey}; pub use crate::connection::Connector; use crate::connection::{self, EppConnection}; use crate::error::Error; -use crate::hello::{Greeting, GreetingDocument, HelloDocument}; -use crate::request::{Command, CommandDocument, Extension, Transaction}; -use crate::response::{Response, ResponseDocument, ResponseStatus}; +use crate::hello::{Greeting, Hello}; +use crate::request::{Command, CommandWrapper, Extension, Transaction}; +use crate::response::{Response, ResponseStatus}; use crate::xml; /// An `EppClient` provides an interface to sending EPP requests to a registry @@ -35,9 +35,9 @@ use crate::xml; /// # use std::net::ToSocketAddrs; /// # use std::time::Duration; /// # -/// use epp_client::EppClient; -/// use epp_client::domain::DomainCheck; -/// use epp_client::common::NoExtension; +/// use instant_epp::EppClient; +/// use instant_epp::domain::DomainCheck; +/// use instant_epp::common::NoExtension; /// /// # #[tokio::main] /// # async fn main() { @@ -55,9 +55,12 @@ use crate::xml; /// // Execute an EPP Command against the registry with distinct request and response objects /// let domain_check = DomainCheck { domains: &["eppdev.com", "eppdev.net"] }; /// let response = client.transact(&domain_check, "transaction-id").await.unwrap(); -/// response.res_data.unwrap().list +/// response +/// .res_data() +/// .unwrap() +/// .list /// .iter() -/// .for_each(|chk| println!("Domain: {}, Available: {}", chk.id, chk.available)); +/// .for_each(|chk| println!("Domain: {}, Available: {}", chk.inner.id, chk.inner.available)); /// # } /// ``` /// @@ -103,13 +106,13 @@ impl EppClient { /// Executes an EPP Hello call and returns the response as a `Greeting` pub async fn hello(&mut self) -> Result { - let xml = xml::serialize(&HelloDocument::default())?; + let xml = xml::serialize(Hello)?; debug!("{}: hello: {}", self.connection.registry, &xml); let response = self.connection.transact(&xml)?.await?; debug!("{}: greeting: {}", self.connection.registry, &response); - Ok(xml::deserialize::(&response)?.data) + xml::deserialize::(&response) } pub async fn transact<'c, 'e, Cmd, Ext>( @@ -122,29 +125,28 @@ impl EppClient { Ext: Extension + 'e, { let data = data.into(); - let document = CommandDocument::new(data.command, data.extension, id); + let document = CommandWrapper::new(data.command, data.extension, id); let xml = xml::serialize(&document)?; debug!("{}: request: {}", self.connection.registry, &xml); let response = self.connection.transact(&xml)?.await?; debug!("{}: response: {}", self.connection.registry, &response); - let rsp = - match xml::deserialize::>(&response) { - Ok(rsp) => rsp, - Err(e) => { - error!(%response, "failed to deserialize response for transaction: {e}"); - return Err(e); - } - }; + let rsp = match xml::deserialize::>(&response) { + Ok(rsp) => rsp, + Err(e) => { + error!(%response, "failed to deserialize response for transaction: {e}"); + return Err(e); + } + }; - if rsp.data.result.code.is_success() { - return Ok(rsp.data); + if rsp.result.code.is_success() { + return Ok(rsp); } let err = crate::error::Error::Command(Box::new(ResponseStatus { - result: rsp.data.result, - tr_ids: rsp.data.tr_ids, + result: rsp.result, + tr_ids: rsp.tr_ids, })); error!(%response, "Failed to deserialize response for transaction: {}", err); @@ -164,7 +166,7 @@ impl EppClient { /// Returns the greeting received on establishment of the connection as an `Greeting` pub fn greeting(&self) -> Result { - xml::deserialize::(&self.connection.greeting).map(|obj| obj.data) + xml::deserialize::(&self.connection.greeting) } pub async fn reconnect(&mut self) -> Result<(), Error> { diff --git a/src/common.rs b/src/common.rs index e0f0a74..d32d773 100644 --- a/src/common.rs +++ b/src/common.rs @@ -1,135 +1,45 @@ //! Common data types included in EPP Requests and Responses -use std::ops::Deref; -use std::{borrow::Cow, fmt::Display, net::IpAddr}; +use std::borrow::Cow; -use serde::ser::SerializeSeq; -use serde::{Deserialize, Serialize}; +use instant_xml::{FromXml, ToXml}; use crate::request::Extension; pub(crate) const EPP_XMLNS: &str = "urn:ietf:params:xml:ns:epp-1.0"; -/// Wraps String for easier serialization to and from values that are inner text -/// for tags rather than attributes -#[derive(Default, Serialize, Deserialize, Debug, Eq, PartialEq, Clone)] -pub struct StringValue<'a>(Cow<'a, str>); - -impl Deref for StringValue<'_> { - type Target = str; - - fn deref(&self) -> &Self::Target { - self.0.as_ref() - } -} - -impl<'a> AsRef for StringValue<'a> { - fn as_ref(&self) -> &str { - self.0.as_ref() - } -} - -impl Display for StringValue<'_> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.0) - } -} - -impl<'a> From<&'a str> for StringValue<'a> { - fn from(s: &'a str) -> Self { - Self(s.into()) - } -} - -impl From for StringValue<'static> { - fn from(s: String) -> Self { - Self(s.into()) - } -} - -#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] -/// An empty placeholder tag. To be refactored to something more compliant later. +#[derive(Debug, Eq, PartialEq, ToXml)] pub struct NoExtension; +impl<'xml> FromXml<'xml> for NoExtension { + fn matches(_: instant_xml::Id<'_>, _: Option>) -> bool { + false + } + + fn deserialize<'cx>( + _: &mut Self::Accumulator, + _: &'static str, + _: &mut instant_xml::Deserializer<'cx, 'xml>, + ) -> Result<(), instant_xml::Error> { + unreachable!() + } + + type Accumulator = Option; + const KIND: instant_xml::Kind = instant_xml::Kind::Element; +} + impl Extension for NoExtension { type Response = NoExtension; } -/// Type that represents the <name> tag for host check response -#[derive(Deserialize, Debug)] -struct Available { - /// The resource name - #[serde(rename = "$value")] - pub id: StringValue<'static>, - /// The resource (un)availability - #[serde(rename = "avail")] - pub available: bool, -} - -/// Type that represents the <cd> tag for domain check response -#[derive(Deserialize, Debug)] -struct CheckResponseDataItem { - /// Data under the <name> tag - #[serde(rename = "name", alias = "id")] - pub resource: Available, - /// The reason for (un)availability - pub reason: Option>, -} - -/// Type that represents the <chkData> tag for host check response -#[derive(Deserialize, Debug)] -struct CheckData { - /// Data under the <cd> tag - #[serde(rename = "cd")] - pub list: Vec, -} - -/// Type that represents the <resData> tag for host check response -#[derive(Deserialize, Debug)] -struct DeserializedCheckResponse { - /// Data under the <chkData> tag - #[serde(rename = "chkData")] - pub check_data: CheckData, -} - -#[derive(Debug)] -pub struct Checked { - pub id: String, - pub available: bool, - pub reason: Option, -} - -#[derive(Deserialize, Debug)] -#[serde(from = "DeserializedCheckResponse")] -pub struct CheckResponse { - pub list: Vec, -} - -impl From for CheckResponse { - fn from(rsp: DeserializedCheckResponse) -> Self { - Self { - list: rsp - .check_data - .list - .into_iter() - .map(|item| Checked { - id: item.resource.id.0.into_owned(), - available: item.resource.available, - reason: item.reason.map(|r| r.0.into_owned()), - }) - .collect(), - } - } -} - /// The - \ No newline at end of file + diff --git a/tests/resources/response/host/check.xml b/tests/resources/response/host/check.xml index b85c4b2..ce43906 100644 --- a/tests/resources/response/host/check.xml +++ b/tests/resources/response/host/check.xml @@ -19,4 +19,4 @@ RO-6879-1627224678242975 - \ No newline at end of file + diff --git a/tests/resources/response/message/poll_host_info.xml b/tests/resources/response/message/poll_host_info.xml index 37bd2f9..aa04484 100644 --- a/tests/resources/response/message/poll_host_info.xml +++ b/tests/resources/response/message/poll_host_info.xml @@ -21,18 +21,9 @@ 2021-12-01T22:40:48Z - - - delete - 2022-01-02T11:30:45Z - 1234 - regy_batch - Unused objects policy - - cltrid:1626454866 RO-6879-1627224678242975 - \ No newline at end of file +