diff --git a/epp-client/src/connection/client.rs b/epp-client/src/connection/client.rs index f5394ee..5bd2885 100644 --- a/epp-client/src/connection/client.rs +++ b/epp-client/src/connection/client.rs @@ -55,8 +55,8 @@ use crate::error; use crate::hello::{EppGreeting, EppHello}; use crate::login::{EppLogin, EppLoginResponse}; use crate::logout::{EppLogout, EppLogoutResponse}; -use crate::request::generate_client_tr_id; -use crate::response::{EppCommandResponse, EppCommandResponseError}; +use crate::request::{generate_client_tr_id, EppExtension, EppRequest}; +use crate::response::{CommandResponseWithExtension, EppCommandResponse, EppCommandResponseError}; use crate::xml::EppXml; /// Instances of the EppClient type are used to transact with the registry. /// Once initialized, the EppClient instance can serialize EPP requests to XML and send them @@ -159,6 +159,22 @@ impl EppClient { } } + pub async fn transact_new( + &mut self, + request: T, + id: &str, + ) -> Result>::Output, E::Response>, error::Error> + where + T: EppRequest + Debug, + E: EppExtension, + { + let epp_xml = request.serialize_request(id)?; + + let response = self.connection.transact(&epp_xml).await?; + + T::deserialize_response(&response) + } + /// Fetches the username used in the registry connection pub fn username(&self) -> String { self.credentials.0.to_string() diff --git a/epp-client/src/request.rs b/epp-client/src/request.rs index 9d758aa..900905c 100644 --- a/epp-client/src/request.rs +++ b/epp-client/src/request.rs @@ -1,15 +1,60 @@ //! Types for EPP requests -use serde::{ser::SerializeStruct, ser::Serializer, Deserialize, Serialize}; +use serde::{de::DeserializeOwned, ser::SerializeStruct, ser::Serializer, Deserialize, Serialize}; use std::error::Error; +use std::fmt::Debug; use std::time::SystemTime; -use crate::common::{ElementName, EmptyTag, Extension, StringValue}; +use crate::{ + common::{ElementName, EmptyTag, EppObject, Extension, StringValue}, + response::{CommandResponseStatus, CommandResponseWithExtension}, + xml::EppXml, +}; use epp_client_macros::ElementName; pub const EPP_VERSION: &str = "1.0"; pub const EPP_LANG: &str = "en"; +/// Trait to set correct value for xml tags when tags are being generated from generic types +pub trait EppRequest: Sized + Debug { + type Input: ElementName + DeserializeOwned + Serialize + Sized + Debug; + type Output: DeserializeOwned + Serialize + Debug; + + fn into_parts(self) -> (Self::Input, Option); + + fn serialize_request(self, client_tr_id: &str) -> Result> { + let (command, extension) = self.into_parts(); + let extension = extension.map(|data| Extension { data }); + EppXml::serialize(&EppObject::build(CommandWithExtension { + command, + extension, + client_tr_id: client_tr_id.into(), + })) + } + + fn deserialize_response( + epp_xml: &str, + ) -> Result, crate::error::Error> { + let rsp = + > as EppXml>::deserialize( + epp_xml, + )?; + match rsp.data.result.code { + 0..=2000 => Ok(rsp.data), + _ => Err(crate::error::Error::EppCommandError(EppObject::build( + CommandResponseStatus { + result: rsp.data.result, + tr_ids: rsp.data.tr_ids, + }, + ))), + } + } +} + +pub trait EppExtension: ElementName + DeserializeOwned + Serialize + Sized + Debug { + type Response: ElementName + DeserializeOwned + Serialize + Debug; +} + /// Type corresponding to the <command> tag in an EPP XML request /// without an <extension> tag pub type Command = CommandWithExtension;