more intuitive serialization and object structure, and domain check command added
This commit is contained in:
parent
4de42f1951
commit
e879ce5d9a
|
@ -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]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
|
@ -12,7 +13,8 @@ async fn main() {
|
||||||
Err(e) => panic!("Error: {}", e)
|
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::<EppGreeting>(&epp_hello).await.unwrap();
|
let response = client.transact::<domain::EppDomainCheck, response::domain::EppDomainCheckResponse>(&domain_check).await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,8 +10,7 @@ use tokio::{net::TcpStream, io::AsyncWriteExt, io::AsyncReadExt, io::split, io::
|
||||||
|
|
||||||
use crate::config::{CONFIG, EppClientConnection};
|
use crate::config::{CONFIG, EppClientConnection};
|
||||||
use crate::error;
|
use crate::error;
|
||||||
use crate::epp::object::EppObject;
|
use crate::epp::request::{generate_client_tr_id, Login, EppLogin, Logout, EppLogout};
|
||||||
use crate::epp::request::{EppRequest, Login, Logout};
|
|
||||||
use crate::epp::response::EppCommandResponse;
|
use crate::epp::response::EppCommandResponse;
|
||||||
use crate::epp::xml::EppXml;
|
use crate::epp::xml::EppXml;
|
||||||
|
|
||||||
|
@ -144,15 +143,15 @@ impl EppClient {
|
||||||
credentials: credentials
|
credentials: credentials
|
||||||
};
|
};
|
||||||
|
|
||||||
let client_tr_id = EppRequest::generate_client_tr_id(&client.credentials.0)?;
|
let client_tr_id = generate_client_tr_id(&client.credentials.0)?;
|
||||||
let login_request = Login::new(&client.credentials.0, &client.credentials.1, client_tr_id.as_str());
|
let login_request = Login::epp_new(&client.credentials.0, &client.credentials.1, client_tr_id.as_str());
|
||||||
|
|
||||||
client.transact::<EppCommandResponse>(&login_request).await?;
|
client.transact::<EppLogin, EppCommandResponse>(&login_request).await?;
|
||||||
|
|
||||||
Ok(client)
|
Ok(client)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn transact<E: EppXml + Debug>(&mut self, request: &EppRequest) -> Result<E::Output, Box<dyn Error>> {
|
pub async fn transact<T: EppXml + Debug, E: EppXml + Debug>(&mut self, request: &T) -> Result<E::Output, Box<dyn Error>> {
|
||||||
let epp_xml = request.serialize()?;
|
let epp_xml = request.serialize()?;
|
||||||
|
|
||||||
println!("Request:\r\n{}", epp_xml);
|
println!("Request:\r\n{}", epp_xml);
|
||||||
|
@ -177,10 +176,10 @@ impl EppClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn logout(&mut self) {
|
pub async fn logout(&mut self) {
|
||||||
let client_tr_id = EppRequest::generate_client_tr_id(&self.credentials.0).unwrap();
|
let client_tr_id = generate_client_tr_id(&self.credentials.0).unwrap();
|
||||||
let epp_logout = Logout::new(client_tr_id.as_str());
|
let epp_logout = Logout::epp_new(client_tr_id.as_str());
|
||||||
|
|
||||||
self.transact::<EppCommandResponse>(&epp_logout).await;
|
self.transact::<EppLogout, EppCommandResponse>(&epp_logout).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
|
pub mod command;
|
||||||
pub mod object;
|
pub mod object;
|
||||||
pub mod quick_xml;
|
pub mod quick_xml;
|
||||||
pub mod request;
|
pub mod request;
|
||||||
pub mod xml;
|
|
||||||
pub mod response;
|
pub mod response;
|
||||||
|
pub mod xml;
|
||||||
|
pub use request::domain;
|
||||||
|
|
|
@ -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<T: ElementName> {
|
||||||
|
pub command: T,
|
||||||
|
#[serde(rename = "clTRID")]
|
||||||
|
pub client_tr_id: StringValue,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ElementName> ElementName for Command<T> {
|
||||||
|
fn element_name(&self) -> &'static str {
|
||||||
|
"command"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ElementName + Serialize> Serialize for Command<T> {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
|
@ -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};
|
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")]
|
#[serde(rename = "epp")]
|
||||||
pub struct EppObject<T> {
|
pub struct EppObject<T: ElementName> {
|
||||||
pub xmlns: String,
|
pub xmlns: String,
|
||||||
#[serde(rename = "xmlns:xsi")]
|
#[serde(rename = "xmlns:xsi")]
|
||||||
pub xmlns_xsi: String,
|
pub xmlns_xsi: String,
|
||||||
|
@ -33,6 +37,21 @@ pub struct EppObject<T> {
|
||||||
pub data: T,
|
pub data: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: ElementName + Serialize> Serialize for EppObject<T> {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
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)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq)]
|
||||||
#[serde(rename = "options")]
|
#[serde(rename = "options")]
|
||||||
pub struct Options {
|
pub struct Options {
|
||||||
|
@ -64,7 +83,7 @@ pub struct Services {
|
||||||
pub svc_ext: Option<ServiceExtension>,
|
pub svc_ext: Option<ServiceExtension>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> EppObject<T> {
|
impl<T: ElementName> EppObject<T> {
|
||||||
pub fn new(data: T) -> EppObject<T> {
|
pub fn new(data: T) -> EppObject<T> {
|
||||||
EppObject {
|
EppObject {
|
||||||
data: data,
|
data: data,
|
||||||
|
|
|
@ -3,10 +3,10 @@ use quick_xml::se;
|
||||||
use serde::{de::DeserializeOwned, Serialize};
|
use serde::{de::DeserializeOwned, Serialize};
|
||||||
use std::{error::Error, fmt::Debug};
|
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};
|
use crate::epp::xml::{EppXml, EPP_XML_HEADER};
|
||||||
|
|
||||||
impl<T: Serialize + DeserializeOwned + Debug> EppXml for EppObject<T> {
|
impl<T: Serialize + DeserializeOwned + ElementName + Debug> EppXml for EppObject<T> {
|
||||||
type Output = EppObject<T>;
|
type Output = EppObject<T>;
|
||||||
|
|
||||||
fn serialize(&self) -> Result<String, Box<dyn Error>> {
|
fn serialize(&self) -> Result<String, Box<dyn Error>> {
|
||||||
|
|
|
@ -1,54 +1,38 @@
|
||||||
|
pub mod domain;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::time::SystemTime;
|
use std::time::SystemTime;
|
||||||
|
|
||||||
|
pub use crate::epp::command::Command;
|
||||||
use crate::epp::object::{
|
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)]
|
pub type EppHello = EppObject<Hello>;
|
||||||
#[serde(rename_all = "lowercase")]
|
pub type EppLogin = EppObject<Command<Login>>;
|
||||||
pub enum RequestType {
|
pub type EppLogout = EppObject<Command<Logout>>;
|
||||||
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,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<RequestType> EppObject<RequestType> {
|
pub fn generate_client_tr_id(username: &str) -> Result<String, Box<dyn Error>> {
|
||||||
pub fn generate_client_tr_id(username: &str) -> Result<String, Box<dyn Error>> {
|
|
||||||
let timestamp = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?;
|
let timestamp = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?;
|
||||||
Ok(format!("{}:{}", username, timestamp.as_secs()))
|
Ok(format!("{}:{}", username, timestamp.as_secs()))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type EppRequest = EppObject<RequestType>;
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq)]
|
||||||
#[serde(rename_all = "lowercase")]
|
#[serde(rename = "hello")]
|
||||||
pub struct Hello;
|
pub struct Hello;
|
||||||
|
|
||||||
impl Hello {
|
impl ElementName for Hello {
|
||||||
pub fn new() -> EppRequest {
|
fn element_name(&self) -> &'static str {
|
||||||
EppRequest::new(RequestType::Hello)
|
"hello"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq)]
|
impl Hello {
|
||||||
pub struct Command {
|
pub fn epp_new() -> EppHello {
|
||||||
login: Login,
|
EppObject::new(Hello {})
|
||||||
#[serde(rename = "clTRID")]
|
}
|
||||||
client_tr_id: StringValue,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq)]
|
||||||
|
@ -63,8 +47,14 @@ pub struct Login {
|
||||||
services: Services,
|
services: Services,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ElementName for Login {
|
||||||
|
fn element_name(&self) -> &'static str {
|
||||||
|
"login"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl 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 {
|
let login = Login {
|
||||||
username: username.to_string_value(),
|
username: username.to_string_value(),
|
||||||
password: password.to_string_value(),
|
password: password.to_string_value(),
|
||||||
|
@ -86,8 +76,8 @@ impl Login {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
EppRequest::new(RequestType::CommandLogin {
|
EppObject::new(Command::<Login> {
|
||||||
login: login,
|
command: login,
|
||||||
client_tr_id: client_tr_id.to_string_value(),
|
client_tr_id: client_tr_id.to_string_value(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -106,10 +96,16 @@ impl Login {
|
||||||
pub struct Logout;
|
pub struct Logout;
|
||||||
|
|
||||||
impl Logout {
|
impl Logout {
|
||||||
pub fn new(client_tr_id: &str) -> EppRequest {
|
pub fn epp_new(client_tr_id: &str) -> EppLogout {
|
||||||
EppRequest::new(RequestType::CommandLogout {
|
EppObject::new(Command::<Logout> {
|
||||||
logout: Logout,
|
command: Logout,
|
||||||
client_tr_id: client_tr_id.to_string_value(),
|
client_tr_id: client_tr_id.to_string_value(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ElementName for Logout {
|
||||||
|
fn element_name(&self) -> &'static str {
|
||||||
|
"logout"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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<Command<DomainCheck>>;
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct DomainList {
|
||||||
|
pub xmlns: String,
|
||||||
|
#[serde(rename = "name")]
|
||||||
|
pub domains: Vec<StringValue>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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::<Vec<StringValue>>();
|
||||||
|
|
||||||
|
let domain_check = DomainCheck {
|
||||||
|
list: DomainList {
|
||||||
|
xmlns: EPP_DOMAIN_XMLNS.to_string(),
|
||||||
|
domains: domains,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
EppObject::new(Command::<DomainCheck> {
|
||||||
|
command: domain_check,
|
||||||
|
client_tr_id: client_tr_id.to_string_value(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,15 +1,13 @@
|
||||||
|
pub mod domain;
|
||||||
|
|
||||||
use serde::{Deserialize, Deserializer, Serialize};
|
use serde::{Deserialize, Deserializer, Serialize};
|
||||||
use std::error::Error;
|
|
||||||
|
|
||||||
use crate::epp::object::{EppObject, Options, ServiceExtension, Services, StringValue};
|
use crate::epp::object::{
|
||||||
use crate::epp::xml::{EPP_XMLNS, EPP_XMLNS_XSI, EPP_XSI_SCHEMA_LOCATION};
|
ElementName, EppObject, Options, ServiceExtension, Services, StringValue,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq)]
|
pub type EppGreeting = EppObject<Greeting>;
|
||||||
#[serde(rename_all = "lowercase")]
|
pub type EppCommandResponse = EppObject<CommandResponse<String>>;
|
||||||
pub struct ResponseType<T>(T);
|
|
||||||
|
|
||||||
pub type EppGreeting = EppObject<ResponseType<Greeting>>;
|
|
||||||
pub type EppCommandResponse = EppObject<ResponseType<CommandResponse>>;
|
|
||||||
|
|
||||||
#[derive(Serialize, Debug, PartialEq)]
|
#[derive(Serialize, Debug, PartialEq)]
|
||||||
pub struct ServiceMenu {
|
pub struct ServiceMenu {
|
||||||
|
@ -131,8 +129,22 @@ pub struct ResponseTRID {
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq)]
|
||||||
#[serde(rename_all = "lowercase")]
|
#[serde(rename_all = "lowercase")]
|
||||||
pub struct CommandResponse {
|
pub struct CommandResponse<T> {
|
||||||
pub result: EppResult,
|
pub result: EppResult,
|
||||||
|
#[serde(rename = "resData")]
|
||||||
|
pub res_data: Option<T>,
|
||||||
#[serde(rename = "trID")]
|
#[serde(rename = "trID")]
|
||||||
pub tr_ids: ResponseTRID,
|
pub tr_ids: ResponseTRID,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ElementName for Greeting {
|
||||||
|
fn element_name(&self) -> &'static str {
|
||||||
|
"greeting"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> ElementName for CommandResponse<T> {
|
||||||
|
fn element_name(&self) -> &'static str {
|
||||||
|
"command"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::epp::object::{EppObject, StringValue};
|
||||||
|
use crate::epp::response::CommandResponse;
|
||||||
|
|
||||||
|
pub type EppDomainCheckResponse = EppObject<CommandResponse<DomainCheckResult>>;
|
||||||
|
|
||||||
|
#[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<StringValue>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct DomainCheckData {
|
||||||
|
#[serde(rename = "cd")]
|
||||||
|
pub domain_list: Vec<DomainCheckDataItem>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct DomainCheckResult {
|
||||||
|
#[serde(rename = "chkData")]
|
||||||
|
pub check_data: DomainCheckData,
|
||||||
|
}
|
Loading…
Reference in New Issue