From e879ce5d9a47902062c892688a2b430e268ebbde Mon Sep 17 00:00:00 2001 From: Ritesh Chitlangi Date: Wed, 21 Jul 2021 00:10:56 +0800 Subject: [PATCH] more intuitive serialization and object structure, and domain check command added --- examples/client.rs | 8 ++-- src/connection.rs | 17 ++++---- src/epp.rs | 4 +- src/epp/command.rs | 29 ++++++++++++++ src/epp/object.rs | 27 +++++++++++-- src/epp/quick_xml.rs | 4 +- src/epp/request.rs | 80 ++++++++++++++++++-------------------- src/epp/request/domain.rs | 47 ++++++++++++++++++++++ src/epp/response.rs | 32 ++++++++++----- src/epp/response/domain.rs | 38 ++++++++++++++++++ 10 files changed, 215 insertions(+), 71 deletions(-) create mode 100644 src/epp/command.rs create mode 100644 src/epp/request/domain.rs create mode 100644 src/epp/response/domain.rs diff --git a/examples/client.rs b/examples/client.rs index d2d63b6..d660e1b 100644 --- a/examples/client.rs +++ b/examples/client.rs @@ -1,4 +1,5 @@ -use epp_client::{epp::request, connection, epp::xml::EppXml, epp::response::EppGreeting}; +use epp_client::{epp::request, epp::request::generate_client_tr_id, epp::request::domain, connection, epp::xml::EppXml, epp::response::EppGreeting, epp::object::ElementName}; +use epp_client::epp::response; #[tokio::main] async fn main() { @@ -12,7 +13,8 @@ async fn main() { Err(e) => panic!("Error: {}", e) }; - let epp_hello = request::Hello::new(); + let domains = vec!["eppdev.com", "hexonet.net"]; + let domain_check = domain::DomainCheck::epp_new(domains, generate_client_tr_id("eppdev").unwrap().as_str()); - client.transact::(&epp_hello).await.unwrap(); + let response = client.transact::(&domain_check).await.unwrap(); } diff --git a/src/connection.rs b/src/connection.rs index fdfc295..d9447a2 100644 --- a/src/connection.rs +++ b/src/connection.rs @@ -10,8 +10,7 @@ use tokio::{net::TcpStream, io::AsyncWriteExt, io::AsyncReadExt, io::split, io:: use crate::config::{CONFIG, EppClientConnection}; use crate::error; -use crate::epp::object::EppObject; -use crate::epp::request::{EppRequest, Login, Logout}; +use crate::epp::request::{generate_client_tr_id, Login, EppLogin, Logout, EppLogout}; use crate::epp::response::EppCommandResponse; use crate::epp::xml::EppXml; @@ -144,15 +143,15 @@ impl EppClient { credentials: credentials }; - let client_tr_id = EppRequest::generate_client_tr_id(&client.credentials.0)?; - let login_request = Login::new(&client.credentials.0, &client.credentials.1, client_tr_id.as_str()); + let client_tr_id = generate_client_tr_id(&client.credentials.0)?; + let login_request = Login::epp_new(&client.credentials.0, &client.credentials.1, client_tr_id.as_str()); - client.transact::(&login_request).await?; + client.transact::(&login_request).await?; Ok(client) } - pub async fn transact(&mut self, request: &EppRequest) -> Result> { + pub async fn transact(&mut self, request: &T) -> Result> { let epp_xml = request.serialize()?; println!("Request:\r\n{}", epp_xml); @@ -177,10 +176,10 @@ impl EppClient { } pub async fn logout(&mut self) { - let client_tr_id = EppRequest::generate_client_tr_id(&self.credentials.0).unwrap(); - let epp_logout = Logout::new(client_tr_id.as_str()); + let client_tr_id = generate_client_tr_id(&self.credentials.0).unwrap(); + let epp_logout = Logout::epp_new(client_tr_id.as_str()); - self.transact::(&epp_logout).await; + self.transact::(&epp_logout).await; } } diff --git a/src/epp.rs b/src/epp.rs index 9c77af7..8821ffc 100644 --- a/src/epp.rs +++ b/src/epp.rs @@ -1,5 +1,7 @@ +pub mod command; pub mod object; pub mod quick_xml; pub mod request; -pub mod xml; pub mod response; +pub mod xml; +pub use request::domain; diff --git a/src/epp/command.rs b/src/epp/command.rs new file mode 100644 index 0000000..59ef8d5 --- /dev/null +++ b/src/epp/command.rs @@ -0,0 +1,29 @@ +use crate::epp::object::{ElementName, StringValue}; +use serde::ser::{SerializeStruct, Serializer}; +use serde::{Deserialize, Serialize}; + +#[derive(Deserialize, Debug, PartialEq)] +pub struct Command { + pub command: T, + #[serde(rename = "clTRID")] + pub client_tr_id: StringValue, +} + +impl ElementName for Command { + fn element_name(&self) -> &'static str { + "command" + } +} + +impl Serialize for Command { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let command_name = self.command.element_name(); + let mut state = serializer.serialize_struct("command", 2)?; + state.serialize_field(command_name, &self.command)?; + state.serialize_field("clTRID", &self.client_tr_id)?; + state.end() + } +} diff --git a/src/epp/object.rs b/src/epp/object.rs index 58161d8..dde2e3f 100644 --- a/src/epp/object.rs +++ b/src/epp/object.rs @@ -1,4 +1,4 @@ -use serde::{Deserialize, Serialize}; +use serde::{ser::SerializeStruct, Deserialize, Serialize, Serializer}; use crate::epp::xml::{EPP_XMLNS, EPP_XMLNS_XSI, EPP_XSI_SCHEMA_LOCATION}; @@ -21,9 +21,13 @@ impl StringValueTrait for &str { } } -#[derive(Serialize, Deserialize, Debug, PartialEq)] +pub trait ElementName { + fn element_name(&self) -> &'static str; +} + +#[derive(Deserialize, Debug, PartialEq)] #[serde(rename = "epp")] -pub struct EppObject { +pub struct EppObject { pub xmlns: String, #[serde(rename = "xmlns:xsi")] pub xmlns_xsi: String, @@ -33,6 +37,21 @@ pub struct EppObject { pub data: T, } +impl Serialize for EppObject { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let data_name = self.data.element_name(); + let mut state = serializer.serialize_struct("epp", 4)?; + state.serialize_field("xmlns", &self.xmlns)?; + state.serialize_field("xmlns:xsi", &self.xmlns_xsi)?; + state.serialize_field("xsi:schemaLocation", &self.xsi_schema_location)?; + state.serialize_field(data_name, &self.data)?; + state.end() + } +} + #[derive(Serialize, Deserialize, Debug, PartialEq)] #[serde(rename = "options")] pub struct Options { @@ -64,7 +83,7 @@ pub struct Services { pub svc_ext: Option, } -impl EppObject { +impl EppObject { pub fn new(data: T) -> EppObject { EppObject { data: data, diff --git a/src/epp/quick_xml.rs b/src/epp/quick_xml.rs index 2488746..124c99a 100644 --- a/src/epp/quick_xml.rs +++ b/src/epp/quick_xml.rs @@ -3,10 +3,10 @@ use quick_xml::se; use serde::{de::DeserializeOwned, Serialize}; use std::{error::Error, fmt::Debug}; -use crate::epp::object::EppObject; +use crate::epp::object::{ElementName, EppObject}; use crate::epp::xml::{EppXml, EPP_XML_HEADER}; -impl EppXml for EppObject { +impl EppXml for EppObject { type Output = EppObject; fn serialize(&self) -> Result> { diff --git a/src/epp/request.rs b/src/epp/request.rs index e484c25..59e9601 100644 --- a/src/epp/request.rs +++ b/src/epp/request.rs @@ -1,54 +1,38 @@ +pub mod domain; + use serde::{Deserialize, Serialize}; use std::error::Error; use std::time::SystemTime; +pub use crate::epp::command::Command; use crate::epp::object::{ - EppObject, Options, ServiceExtension, Services, StringValue, StringValueTrait, + ElementName, EppObject, Options, ServiceExtension, Services, StringValue, StringValueTrait, }; -use crate::epp::xml::{EPP_LANG, EPP_VERSION, EPP_XMLNS, EPP_XMLNS_XSI, EPP_XSI_SCHEMA_LOCATION}; +use crate::epp::xml::{EPP_LANG, EPP_VERSION}; -#[derive(Serialize, Deserialize, Debug, PartialEq)] -#[serde(rename_all = "lowercase")] -pub enum RequestType { - Hello, - #[serde(rename = "command")] - CommandLogin { - login: Login, - #[serde(rename = "clTRID")] - client_tr_id: StringValue, - }, - #[serde(rename = "command")] - CommandLogout { - logout: Logout, - #[serde(rename = "clTRID")] - client_tr_id: StringValue, - }, +pub type EppHello = EppObject; +pub type EppLogin = EppObject>; +pub type EppLogout = EppObject>; + +pub fn generate_client_tr_id(username: &str) -> Result> { + let timestamp = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?; + Ok(format!("{}:{}", username, timestamp.as_secs())) } -impl EppObject { - pub fn generate_client_tr_id(username: &str) -> Result> { - let timestamp = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?; - Ok(format!("{}:{}", username, timestamp.as_secs())) - } -} - -pub type EppRequest = EppObject; - #[derive(Serialize, Deserialize, Debug, PartialEq)] -#[serde(rename_all = "lowercase")] +#[serde(rename = "hello")] pub struct Hello; -impl Hello { - pub fn new() -> EppRequest { - EppRequest::new(RequestType::Hello) +impl ElementName for Hello { + fn element_name(&self) -> &'static str { + "hello" } } -#[derive(Serialize, Deserialize, Debug, PartialEq)] -pub struct Command { - login: Login, - #[serde(rename = "clTRID")] - client_tr_id: StringValue, +impl Hello { + pub fn epp_new() -> EppHello { + EppObject::new(Hello {}) + } } #[derive(Serialize, Deserialize, Debug, PartialEq)] @@ -63,8 +47,14 @@ pub struct Login { services: Services, } +impl ElementName for Login { + fn element_name(&self) -> &'static str { + "login" + } +} + impl Login { - pub fn new(username: &str, password: &str, client_tr_id: &str) -> EppRequest { + pub fn epp_new(username: &str, password: &str, client_tr_id: &str) -> EppLogin { let login = Login { username: username.to_string_value(), password: password.to_string_value(), @@ -86,8 +76,8 @@ impl Login { }, }; - EppRequest::new(RequestType::CommandLogin { - login: login, + EppObject::new(Command:: { + command: login, client_tr_id: client_tr_id.to_string_value(), }) } @@ -106,10 +96,16 @@ impl Login { pub struct Logout; impl Logout { - pub fn new(client_tr_id: &str) -> EppRequest { - EppRequest::new(RequestType::CommandLogout { - logout: Logout, + pub fn epp_new(client_tr_id: &str) -> EppLogout { + EppObject::new(Command:: { + command: Logout, client_tr_id: client_tr_id.to_string_value(), }) } } + +impl ElementName for Logout { + fn element_name(&self) -> &'static str { + "logout" + } +} diff --git a/src/epp/request/domain.rs b/src/epp/request/domain.rs new file mode 100644 index 0000000..b7d0dae --- /dev/null +++ b/src/epp/request/domain.rs @@ -0,0 +1,47 @@ +use crate::epp::command::Command; +use crate::epp::object::{ElementName, EppObject, StringValue, StringValueTrait}; +use serde::{Deserialize, Serialize}; + +const EPP_DOMAIN_XMLNS: &str = "urn:ietf:params:xml:ns:domain-1.0"; + +pub type EppDomainCheck = EppObject>; + +#[derive(Serialize, Deserialize, Debug)] +pub struct DomainList { + pub xmlns: String, + #[serde(rename = "name")] + pub domains: Vec, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct DomainCheck { + #[serde(rename = "check")] + list: DomainList, +} + +impl ElementName for DomainCheck { + fn element_name(&self) -> &'static str { + "check" + } +} + +impl DomainCheck { + pub fn epp_new(domains: Vec<&str>, client_tr_id: &str) -> EppDomainCheck { + let domains = domains + .iter() + .filter_map(|d| Some(d.to_string_value())) + .collect::>(); + + let domain_check = DomainCheck { + list: DomainList { + xmlns: EPP_DOMAIN_XMLNS.to_string(), + domains: domains, + }, + }; + + EppObject::new(Command:: { + command: domain_check, + client_tr_id: client_tr_id.to_string_value(), + }) + } +} diff --git a/src/epp/response.rs b/src/epp/response.rs index 85d21fc..7d16254 100644 --- a/src/epp/response.rs +++ b/src/epp/response.rs @@ -1,15 +1,13 @@ +pub mod domain; + use serde::{Deserialize, Deserializer, Serialize}; -use std::error::Error; -use crate::epp::object::{EppObject, Options, ServiceExtension, Services, StringValue}; -use crate::epp::xml::{EPP_XMLNS, EPP_XMLNS_XSI, EPP_XSI_SCHEMA_LOCATION}; +use crate::epp::object::{ + ElementName, EppObject, Options, ServiceExtension, Services, StringValue, +}; -#[derive(Serialize, Deserialize, Debug, PartialEq)] -#[serde(rename_all = "lowercase")] -pub struct ResponseType(T); - -pub type EppGreeting = EppObject>; -pub type EppCommandResponse = EppObject>; +pub type EppGreeting = EppObject; +pub type EppCommandResponse = EppObject>; #[derive(Serialize, Debug, PartialEq)] pub struct ServiceMenu { @@ -131,8 +129,22 @@ pub struct ResponseTRID { #[derive(Serialize, Deserialize, Debug, PartialEq)] #[serde(rename_all = "lowercase")] -pub struct CommandResponse { +pub struct CommandResponse { pub result: EppResult, + #[serde(rename = "resData")] + pub res_data: Option, #[serde(rename = "trID")] pub tr_ids: ResponseTRID, } + +impl ElementName for Greeting { + fn element_name(&self) -> &'static str { + "greeting" + } +} + +impl ElementName for CommandResponse { + fn element_name(&self) -> &'static str { + "command" + } +} diff --git a/src/epp/response/domain.rs b/src/epp/response/domain.rs new file mode 100644 index 0000000..c16ebbe --- /dev/null +++ b/src/epp/response/domain.rs @@ -0,0 +1,38 @@ +use serde::{Deserialize, Serialize}; + +use crate::epp::object::{EppObject, StringValue}; +use crate::epp::response::CommandResponse; + +pub type EppDomainCheckResponse = EppObject>; + +#[derive(Serialize, Deserialize, Debug)] +pub enum Availability { + Unavailable, + Available, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct DomainCheck { + #[serde(rename = "$value")] + pub name: StringValue, + #[serde(rename = "avail")] + pub avail: u16, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct DomainCheckDataItem { + pub name: DomainCheck, + pub reason: Option, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct DomainCheckData { + #[serde(rename = "cd")] + pub domain_list: Vec, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct DomainCheckResult { + #[serde(rename = "chkData")] + pub check_data: DomainCheckData, +}