Flatten XML abstraction

This commit is contained in:
Dirkjan Ochtman 2022-03-12 20:36:46 +01:00 committed by masalachai
parent 3f91647370
commit ca19e545fd
6 changed files with 28 additions and 57 deletions

View File

@ -21,7 +21,7 @@ use crate::error::Error;
use crate::hello::{Greeting, GreetingDocument, HelloDocument}; use crate::hello::{Greeting, GreetingDocument, HelloDocument};
use crate::request::{Command, CommandDocument, Extension, Transaction}; use crate::request::{Command, CommandDocument, Extension, Transaction};
use crate::response::{Response, ResponseDocument, ResponseStatus}; use crate::response::{Response, ResponseDocument, ResponseStatus};
use crate::xml::EppXml; use crate::xml;
/// An `EppClient` provides an interface to sending EPP requests to a registry /// An `EppClient` provides an interface to sending EPP requests to a registry
/// ///
@ -107,13 +107,13 @@ impl<C: Connector> EppClient<C> {
/// Executes an EPP Hello call and returns the response as a `Greeting` /// Executes an EPP Hello call and returns the response as a `Greeting`
pub async fn hello(&mut self) -> Result<Greeting, Error> { pub async fn hello(&mut self) -> Result<Greeting, Error> {
let hello_xml = HelloDocument::default().serialize()?; let xml = xml::serialize(&HelloDocument::default())?;
debug!("{}: hello: {}", self.connection.registry, &hello_xml); debug!("{}: hello: {}", self.connection.registry, &xml);
let response = self.connection.transact(&hello_xml)?.await?; let response = self.connection.transact(&xml)?.await?;
debug!("{}: greeting: {}", self.connection.registry, &response); debug!("{}: greeting: {}", self.connection.registry, &response);
Ok(GreetingDocument::deserialize(&response)?.data) Ok(xml::deserialize::<GreetingDocument>(&response)?.data)
} }
pub async fn transact<'c, 'e, Cmd, Ext>( pub async fn transact<'c, 'e, Cmd, Ext>(
@ -127,14 +127,13 @@ impl<C: Connector> EppClient<C> {
{ {
let data = data.into(); let data = data.into();
let document = CommandDocument::new(data.command, data.extension, id); let document = CommandDocument::new(data.command, data.extension, id);
let xml = document.serialize()?; let xml = xml::serialize(&document)?;
debug!("{}: request: {}", self.connection.registry, &xml); debug!("{}: request: {}", self.connection.registry, &xml);
let response = self.connection.transact(&xml)?.await?; let response = self.connection.transact(&xml)?.await?;
debug!("{}: response: {}", self.connection.registry, &response); debug!("{}: response: {}", self.connection.registry, &response);
let rsp = let rsp = xml::deserialize::<ResponseDocument<Cmd::Response, Ext::Response>>(&response)?;
<ResponseDocument<Cmd::Response, Ext::Response> as EppXml>::deserialize(&response)?;
if rsp.data.result.code.is_success() { if rsp.data.result.code.is_success() {
return Ok(rsp.data); return Ok(rsp.data);
} }
@ -161,7 +160,7 @@ impl<C: Connector> EppClient<C> {
/// Returns the greeting received on establishment of the connection as an `Greeting` /// Returns the greeting received on establishment of the connection as an `Greeting`
pub fn greeting(&self) -> Result<Greeting, Error> { pub fn greeting(&self) -> Result<Greeting, Error> {
GreetingDocument::deserialize(&self.connection.greeting).map(|obj| obj.data) xml::deserialize::<GreetingDocument>(&self.connection.greeting).map(|obj| obj.data)
} }
pub async fn reconnect(&mut self) -> Result<(), Error> { pub async fn reconnect(&mut self) -> Result<(), Error> {

View File

@ -4,7 +4,6 @@ use chrono::{DateTime, Utc};
use serde::{Deserialize, Deserializer, Serialize}; use serde::{Deserialize, Deserializer, Serialize};
use crate::common::{Options, ServiceExtension, Services, StringValue, EPP_XMLNS}; use crate::common::{Options, ServiceExtension, Services, StringValue, EPP_XMLNS};
use crate::xml::EppXml;
// Request // Request
@ -27,8 +26,6 @@ impl Default for HelloDocument {
} }
} }
impl EppXml for HelloDocument {}
// Response // Response
/// Type for data within the <svcMenu> section of an EPP greeting /// Type for data within the <svcMenu> section of an EPP greeting
@ -302,20 +299,18 @@ pub struct GreetingDocument {
pub data: Greeting, pub data: Greeting,
} }
impl EppXml for GreetingDocument {}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use chrono::{TimeZone, Utc}; use chrono::{TimeZone, Utc};
use super::{ExpiryType, GreetingDocument, HelloDocument, Relative}; use super::{ExpiryType, GreetingDocument, HelloDocument, Relative};
use crate::tests::get_xml; use crate::tests::get_xml;
use crate::xml::EppXml; use crate::xml;
#[test] #[test]
fn hello() { fn hello() {
let xml = get_xml("request/hello.xml").unwrap(); let xml = get_xml("request/hello.xml").unwrap();
let serialized = HelloDocument::default().serialize().unwrap(); let serialized = xml::serialize(&HelloDocument::default()).unwrap();
assert_eq!(xml, serialized); assert_eq!(xml, serialized);
} }
@ -323,7 +318,7 @@ mod tests {
#[test] #[test]
fn greeting() { fn greeting() {
let xml = get_xml("response/greeting.xml").unwrap(); let xml = get_xml("response/greeting.xml").unwrap();
let object = GreetingDocument::deserialize(xml.as_str()).unwrap(); let object = xml::deserialize::<GreetingDocument>(xml.as_str()).unwrap();
assert_eq!(object.data.service_id, "ISPAPI EPP Server"); assert_eq!(object.data.service_id, "ISPAPI EPP Server");
assert_eq!( assert_eq!(

View File

@ -3,10 +3,7 @@
use serde::{de::DeserializeOwned, ser::SerializeStruct, ser::Serializer, Serialize}; use serde::{de::DeserializeOwned, ser::SerializeStruct, ser::Serializer, Serialize};
use std::fmt::Debug; use std::fmt::Debug;
use crate::{ use crate::common::{StringValue, EPP_XMLNS};
common::{StringValue, EPP_XMLNS},
xml::EppXml,
};
pub const EPP_VERSION: &str = "1.0"; pub const EPP_VERSION: &str = "1.0";
pub const EPP_LANG: &str = "en"; pub const EPP_LANG: &str = "en";
@ -73,5 +70,3 @@ impl<'a, Cmd, Ext> CommandDocument<'a, Cmd, Ext> {
} }
} }
} }
impl<'a, D: Serialize, E: Serialize> EppXml for CommandDocument<'a, D, E> {}

View File

@ -3,10 +3,9 @@
use std::fmt::{self, Debug}; use std::fmt::{self, Debug};
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use serde::{de::DeserializeOwned, Deserialize}; use serde::Deserialize;
use crate::common::StringValue; use crate::common::StringValue;
use crate::xml::EppXml;
/// Type corresponding to the <undef> tag an EPP response XML /// Type corresponding to the <undef> tag an EPP response XML
#[derive(Deserialize, Debug, PartialEq)] #[derive(Deserialize, Debug, PartialEq)]
@ -218,8 +217,6 @@ pub struct ResponseDocument<D, E> {
pub data: Response<D, E>, pub data: Response<D, E>,
} }
impl<D: DeserializeOwned, E: DeserializeOwned> EppXml for ResponseDocument<D, E> {}
#[derive(Debug, Deserialize, PartialEq)] #[derive(Debug, Deserialize, PartialEq)]
#[serde(rename = "epp")] #[serde(rename = "epp")]
pub struct ResultDocument { pub struct ResultDocument {
@ -227,8 +224,6 @@ pub struct ResultDocument {
pub data: ResponseStatus, pub data: ResponseStatus,
} }
impl EppXml for ResultDocument {}
#[derive(Deserialize, Debug, PartialEq)] #[derive(Deserialize, Debug, PartialEq)]
/// Type corresponding to the &lt;response&gt; tag in an EPP response XML /// Type corresponding to the &lt;response&gt; tag in an EPP response XML
/// without <msgQ> or &lt;resData&gt; sections. Generally used for error handling /// without <msgQ> or &lt;resData&gt; sections. Generally used for error handling
@ -261,12 +256,12 @@ impl<T, E> Response<T, E> {
mod tests { mod tests {
use super::{ResultCode, ResultDocument}; use super::{ResultCode, ResultDocument};
use crate::tests::{get_xml, CLTRID, SVTRID}; use crate::tests::{get_xml, CLTRID, SVTRID};
use crate::xml::EppXml; use crate::xml;
#[test] #[test]
fn error() { fn error() {
let xml = get_xml("response/error.xml").unwrap(); let xml = get_xml("response/error.xml").unwrap();
let object = ResultDocument::deserialize(xml.as_str()).unwrap(); let object = xml::deserialize::<ResultDocument>(xml.as_str()).unwrap();
assert_eq!(object.data.result.code, ResultCode::ObjectDoesNotExist); assert_eq!(object.data.result.code, ResultCode::ObjectDoesNotExist);
assert_eq!(object.data.result.message, "Object does not exist".into()); assert_eq!(object.data.result.message, "Object does not exist".into());

View File

@ -9,7 +9,7 @@ use crate::{
common::NoExtension, common::NoExtension,
request::{Command, CommandDocument, Extension, Transaction}, request::{Command, CommandDocument, Extension, Transaction},
response::{Response, ResponseDocument}, response::{Response, ResponseDocument},
xml::EppXml, xml,
}; };
pub(crate) const RESOURCES_DIR: &str = "./tests/resources"; pub(crate) const RESOURCES_DIR: &str = "./tests/resources";
@ -47,8 +47,7 @@ pub(crate) fn assert_serialized<'c, 'e, Cmd, Ext>(
let expected = get_xml(path).unwrap(); let expected = get_xml(path).unwrap();
let req = req.into(); let req = req.into();
let document = CommandDocument::new(req.command, req.extension, CLTRID); let document = CommandDocument::new(req.command, req.extension, CLTRID);
let actual = EppXml::serialize(&document).unwrap(); assert_eq!(expected, xml::serialize(&document).unwrap());
assert_eq!(expected, actual);
} }
pub(crate) fn response_from_file<'c, Cmd>( pub(crate) fn response_from_file<'c, Cmd>(
@ -68,8 +67,7 @@ where
Ext: Extension, Ext: Extension,
{ {
let xml = get_xml(path).unwrap(); let xml = get_xml(path).unwrap();
let rsp = let rsp = xml::deserialize::<ResponseDocument<Cmd::Response, Ext::Response>>(&xml).unwrap();
<ResponseDocument<Cmd::Response, Ext::Response> as EppXml>::deserialize(&xml).unwrap();
assert!(rsp.data.result.code.is_success()); assert!(rsp.data.result.code.is_success());
rsp.data rsp.data
} }

View File

@ -6,25 +6,14 @@ use crate::error::Error;
pub const EPP_XML_HEADER: &str = r#"<?xml version="1.0" encoding="UTF-8" standalone="no"?>"#; pub const EPP_XML_HEADER: &str = r#"<?xml version="1.0" encoding="UTF-8" standalone="no"?>"#;
/// Trait to be implemented by serializers. Currently the only included serializer is `quick-xml` pub(crate) fn serialize(doc: &impl Serialize) -> Result<String, Error> {
pub trait EppXml: Sized {
/// Serializes the EppObject instance to an EPP XML document
fn serialize(&self) -> Result<String, Error>
where
Self: Serialize,
{
Ok(format!( Ok(format!(
"{}\r\n{}", "{}\r\n{}",
EPP_XML_HEADER, EPP_XML_HEADER,
quick_xml::se::to_string(self).map_err(|e| Error::Xml(e.into()))? quick_xml::se::to_string(doc).map_err(|e| Error::Xml(e.into()))?
)) ))
} }
/// Deserializes an EPP XML document to an EppObject instance pub(crate) fn deserialize<T: DeserializeOwned>(xml: &str) -> Result<T, Error> {
fn deserialize(epp_xml: &str) -> Result<Self, Error> quick_xml::de::from_str(xml).map_err(|e| Error::Xml(e.into()))
where
Self: DeserializeOwned + Sized,
{
quick_xml::de::from_str::<Self>(epp_xml).map_err(|e| Error::Xml(e.into()))
}
} }