deserialization boilerplate

This commit is contained in:
Ritesh Chitlangi 2021-07-20 15:45:01 +08:00
parent 0ced95dee7
commit 85ae3d601c
9 changed files with 254 additions and 114 deletions

View File

@ -5,8 +5,13 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[[example]]
name = "example-client"
path = "examples/client.rs"
[dependencies] [dependencies]
bytes = "1" bytes = "1"
chrono = "0.4"
confy = "0.4" confy = "0.4"
futures = "0.3" futures = "0.3"
lazy_static = "1.4" lazy_static = "1.4"

View File

@ -1,17 +1,12 @@
pub mod config; use epp_client::{epp::request, connection, epp::xml::EppXml, epp::response::EppResponse};
pub mod connection;
pub mod epp;
pub mod error;
use std::time::SystemTime;
use tokio::time::{sleep, Duration};
use crate::{epp::request};
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
let mut client = match connection::connect("hexonet").await { let mut client = match connection::connect("hexonet").await {
Ok(client) => { Ok(client) => {
println!("{}", client.greeting()); let greeting = client.greeting();
let greeting_object = EppResponse::deserialize(&greeting).unwrap();
println!("{:?}", greeting_object);
client client
}, },
Err(e) => panic!("Error: {}", e) Err(e) => panic!("Error: {}", e)

View File

@ -2,3 +2,4 @@ pub mod object;
pub mod quick_xml; pub mod quick_xml;
pub mod request; pub mod request;
pub mod xml; pub mod xml;
pub mod response;

View File

@ -1,5 +1,26 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::epp::xml::{EPP_XMLNS, EPP_XMLNS_XSI, EPP_XSI_SCHEMA_LOCATION};
#[derive(Serialize, Deserialize, Debug, PartialEq)]
pub struct StringValue(String);
impl Default for StringValue {
fn default() -> Self {
Self(String::from(""))
}
}
pub trait StringValueTrait {
fn to_string_value(&self) -> StringValue;
}
impl StringValueTrait for &str {
fn to_string_value(&self) -> StringValue {
StringValue(self.to_string())
}
}
#[derive(Serialize, Deserialize, Debug, PartialEq)] #[derive(Serialize, Deserialize, Debug, PartialEq)]
#[serde(rename = "epp")] #[serde(rename = "epp")]
pub struct EppObject<T> { pub struct EppObject<T> {
@ -7,6 +28,49 @@ pub struct EppObject<T> {
#[serde(rename = "xmlns:xsi")] #[serde(rename = "xmlns:xsi")]
pub xmlns_xsi: String, pub xmlns_xsi: String,
#[serde(rename = "xsi:schemaLocation")] #[serde(rename = "xsi:schemaLocation")]
pub xsi_schema_location: String, pub xsi_schema_location: String,
#[serde(alias = "greeting")]
pub data: T, pub data: T,
} }
#[derive(Serialize, Deserialize, Debug, PartialEq)]
#[serde(rename = "options")]
pub struct Options {
pub version: StringValue,
pub lang: StringValue,
}
impl Options {
pub fn build(version: &str, lang: &str) -> Options {
Options {
version: version.to_string_value(),
lang: lang.to_string_value(),
}
}
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
#[serde(rename = "svcExtension")]
pub struct ServiceExtension {
#[serde(rename = "extURI")]
pub ext_uris: Option<Vec<StringValue>>,
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
pub struct Services {
#[serde(rename = "objURI")]
pub obj_uris: Vec<StringValue>,
#[serde(rename = "svcExtension")]
pub svc_ext: Option<ServiceExtension>,
}
impl<T> EppObject<T> {
pub fn new(data: T) -> EppObject<T> {
EppObject {
data: data,
xmlns: EPP_XMLNS.to_string(),
xmlns_xsi: EPP_XMLNS_XSI.to_string(),
xsi_schema_location: EPP_XSI_SCHEMA_LOCATION.to_string(),
}
}
}

View File

@ -1,14 +1,24 @@
use quick_xml::de::from_str;
use quick_xml::se; use quick_xml::se;
use serde::Serialize; use serde::{de::DeserializeOwned, Serialize};
use std::error::Error; use std::error::Error;
use crate::epp::object::EppObject; use crate::epp::object::EppObject;
use crate::epp::xml::{EppXml, EPP_XML_HEADER}; use crate::epp::xml::{EppXml, EPP_XML_HEADER};
impl<T: Serialize> EppXml for EppObject<T> { impl<T: Serialize + DeserializeOwned> EppXml for EppObject<T> {
type Object = EppObject<T>;
fn serialize(&self) -> Result<String, Box<dyn Error>> { fn serialize(&self) -> Result<String, Box<dyn Error>> {
let epp_xml = format!("{}\r\n{}", EPP_XML_HEADER, se::to_string(self)?); let epp_xml = format!("{}\r\n{}", EPP_XML_HEADER, se::to_string(self)?);
Ok(epp_xml) Ok(epp_xml)
} }
fn deserialize(epp_xml: &str) -> Result<Self::Object, Box<dyn Error>> {
match from_str(epp_xml) {
Ok(v) => Ok(v),
Err(e) => Err(format!("epp-client Deserialization Error: {}", e).into()),
}
}
} }

View File

@ -2,39 +2,17 @@ use serde::{Deserialize, Serialize};
use std::error::Error; use std::error::Error;
use std::time::SystemTime; use std::time::SystemTime;
use crate::epp::object::EppObject; use crate::epp::object::{
EppObject, Options, ServiceExtension, Services, StringValue, StringValueTrait,
const EPP_XMLNS: &str = "urn:ietf:params:xml:ns:epp-1.0"; };
const EPP_XMLNS_XSI: &str = "http://www.w3.org/2001/XMLSchema-instance"; use crate::epp::xml::{EPP_LANG, EPP_VERSION, EPP_XMLNS, EPP_XMLNS_XSI, EPP_XSI_SCHEMA_LOCATION};
const EPP_XSI_SCHEMA_LOCATION: &str = "urn:ietf:params:xml:ns:epp-1.0 epp-1.0.xsd";
const EPP_VERSION: &str = "1.0";
const EPP_LANG: &str = "en";
#[derive(Serialize, Deserialize, Debug, PartialEq)]
pub struct StringValue(String);
impl Default for StringValue {
fn default() -> Self {
Self(String::from(""))
}
}
pub trait StringValueTrait {
fn to_string_value(&self) -> StringValue;
}
impl StringValueTrait for &str {
fn to_string_value(&self) -> StringValue {
StringValue(self.to_string())
}
}
#[derive(Serialize, Deserialize, Debug, PartialEq)] #[derive(Serialize, Deserialize, Debug, PartialEq)]
#[serde(rename_all = "lowercase")] #[serde(rename_all = "lowercase")]
pub enum RequestType { pub enum RequestType {
Hello, Hello,
Command { #[serde(rename = "command")]
CommandLogin {
login: Login, login: Login,
#[serde(rename = "clTRID")] #[serde(rename = "clTRID")]
client_tr_id: StringValue, client_tr_id: StringValue,
@ -48,15 +26,6 @@ pub enum RequestType {
} }
impl<RequestType> EppObject<RequestType> { impl<RequestType> EppObject<RequestType> {
pub fn new(data: RequestType) -> EppObject<RequestType> {
EppObject {
data: data,
xmlns: EPP_XMLNS.to_string(),
xmlns_xsi: EPP_XMLNS_XSI.to_string(),
xsi_schema_location: EPP_XSI_SCHEMA_LOCATION.to_string(),
}
}
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()))
@ -75,37 +44,6 @@ impl Hello {
} }
} }
#[derive(Serialize, Deserialize, Debug, PartialEq)]
#[serde(rename = "options")]
pub struct LoginOptions {
version: StringValue,
lang: StringValue,
}
impl LoginOptions {
pub fn build(version: &str, lang: &str) -> LoginOptions {
LoginOptions {
version: version.to_string_value(),
lang: lang.to_string_value(),
}
}
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
#[serde(rename = "svcExtension")]
pub struct ServiceExtension {
#[serde(rename = "extURI")]
ext_uris: Option<Vec<StringValue>>,
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
pub struct Services {
#[serde(rename = "objURI")]
obj_uris: Vec<StringValue>,
#[serde(rename = "svcExtension")]
svc_ext: Option<ServiceExtension>,
}
#[derive(Serialize, Deserialize, Debug, PartialEq)] #[derive(Serialize, Deserialize, Debug, PartialEq)]
pub struct Command { pub struct Command {
login: Login, login: Login,
@ -120,7 +58,7 @@ pub struct Login {
username: StringValue, username: StringValue,
#[serde(rename = "pw", default)] #[serde(rename = "pw", default)]
password: StringValue, password: StringValue,
options: LoginOptions, options: Options,
#[serde(rename = "svcs")] #[serde(rename = "svcs")]
services: Services, services: Services,
} }
@ -130,7 +68,7 @@ impl Login {
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(),
options: LoginOptions { options: Options {
version: EPP_VERSION.to_string_value(), version: EPP_VERSION.to_string_value(),
lang: EPP_LANG.to_string_value(), lang: EPP_LANG.to_string_value(),
}, },
@ -148,13 +86,13 @@ impl Login {
}, },
}; };
EppRequest::new(RequestType::Command { EppRequest::new(RequestType::CommandLogin {
login: login, login: login,
client_tr_id: client_tr_id.to_string_value(), client_tr_id: client_tr_id.to_string_value(),
}) })
} }
pub fn set_options(&mut self, options: LoginOptions) { pub fn set_options(&mut self, options: Options) {
self.options = options; self.options = options;
} }

View File

@ -1 +1,118 @@
// use chrono::{DateTime, Utc};
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};
#[derive(Serialize, Deserialize, Debug, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum ResponseType {
#[serde(rename = "greeting")]
Greeting(Greeting),
}
pub type EppResponse = EppObject<ResponseType>;
#[derive(Serialize, Debug, PartialEq)]
pub struct ServiceMenu {
pub options: Options,
pub services: Services,
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct FlattenedServiceMenu {
pub version: StringValue,
pub lang: StringValue,
#[serde(rename = "objURI")]
pub obj_uris: Vec<StringValue>,
#[serde(rename = "svcExtension")]
pub svc_ext: Option<ServiceExtension>,
}
impl<'de> Deserialize<'de> for ServiceMenu {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let flattened_svc_menu = FlattenedServiceMenu::deserialize(deserializer)?;
let svc_menu = ServiceMenu {
options: Options {
version: flattened_svc_menu.version,
lang: flattened_svc_menu.lang,
},
services: Services {
obj_uris: flattened_svc_menu.obj_uris,
svc_ext: flattened_svc_menu.svc_ext,
},
};
Ok(svc_menu)
}
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
pub struct All;
#[derive(Serialize, Deserialize, Debug, PartialEq)]
pub struct Access {
all: All,
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
pub struct Admin;
#[derive(Serialize, Deserialize, Debug, PartialEq)]
pub struct Prov;
#[derive(Serialize, Deserialize, Debug, PartialEq)]
pub struct Purpose {
admin: Admin,
prov: Prov,
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
pub struct Ours;
#[derive(Serialize, Deserialize, Debug, PartialEq)]
pub struct Public;
#[derive(Serialize, Deserialize, Debug, PartialEq)]
pub struct Recipient {
ours: Ours,
public: Public,
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
pub struct Stated;
#[derive(Serialize, Deserialize, Debug, PartialEq)]
pub struct Retention {
stated: Stated,
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
pub struct Statement {
purpose: Purpose,
recipient: Recipient,
retention: Retention,
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
pub struct Dcp {
access: Access,
statement: Statement,
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
#[serde(rename_all = "lowercase")]
pub struct Greeting {
#[serde(rename = "svID")]
service_id: String,
#[serde(rename = "svDate")]
service_date: String,
#[serde(rename = "svcMenu")]
svc_menu: ServiceMenu,
dcp: Dcp,
}

View File

@ -1,8 +1,18 @@
use std::error::Error; use std::error::Error;
// use crate::epp::object::EppObject;
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"?>"#;
pub const EPP_XMLNS: &str = "urn:ietf:params:xml:ns:epp-1.0";
pub const EPP_XMLNS_XSI: &str = "http://www.w3.org/2001/XMLSchema-instance";
pub const EPP_XSI_SCHEMA_LOCATION: &str = "urn:ietf:params:xml:ns:epp-1.0 epp-1.0.xsd";
pub const EPP_VERSION: &str = "1.0";
pub const EPP_LANG: &str = "en";
pub trait EppXml { pub trait EppXml {
type Object;
fn serialize(&self) -> Result<String, Box<dyn Error>>; fn serialize(&self) -> Result<String, Box<dyn Error>>;
// fn deserialize(&self) -> Result<Self, Box<dyn Error>>; fn deserialize(epp_xml: &str) -> Result<Self::Object, Box<dyn Error>>;
} }

View File

@ -1,34 +1,34 @@
// pub mod config; pub mod config;
// pub mod connection; pub mod connection;
// pub mod epp; pub mod epp;
// pub mod error; pub mod error;
// #[cfg(test)] #[cfg(test)]
// mod tests { mod tests {
// use super::config; use super::config;
// use super::connection; use super::connection;
// #[test] #[test]
// fn config() { fn config() {
// let servers = &config::CONFIG.servers; let servers = &config::CONFIG.servers;
// () ()
// } }
// macro_rules! aw { macro_rules! aw {
// ($e:expr) => { ($e:expr) => {
// tokio_test::block_on($e) tokio_test::block_on($e)
// }; };
// } }
// #[test] #[test]
// fn connect() { fn connect() {
// let mut client = match aw!(connection::connect("hexonet")) { let mut client = match aw!(connection::connect("hexonet")) {
// Ok(client) => { Ok(client) => {
// println!("{}", client.greeting()); println!("{}", client.greeting());
// client client
// }, },
// Err(e) => panic!("Error: {}", e) Err(e) => panic!("Error: {}", e)
// }; };
// } }
// } }