use std::fmt::Debug; use chrono::{DateTime, Utc}; use serde::{Deserialize, Deserializer, Serialize}; use crate::common::{Options, ServiceExtension, Services, StringValue, EPP_XMLNS}; // Request #[derive(Debug, PartialEq, Serialize)] struct Hello; #[derive(Debug, PartialEq, Serialize)] #[serde(rename = "epp")] pub struct HelloDocument { xmlns: &'static str, hello: Hello, } impl Default for HelloDocument { fn default() -> Self { Self { xmlns: EPP_XMLNS, hello: Hello, } } } // Response /// Type for data within the section of an EPP greeting #[derive(Debug, Eq, PartialEq)] pub struct ServiceMenu { pub options: Options<'static>, pub services: Services<'static>, } /// Simplified service menu type for deserialization to `ServiceMenu` type from EPP greeting XML #[derive(Deserialize, Debug, PartialEq)] struct FlattenedServiceMenu { pub version: StringValue<'static>, pub lang: StringValue<'static>, #[serde(rename = "objURI")] pub obj_uris: Vec>, #[serde(rename = "svcExtension")] pub svc_ext: Option>, } impl<'a, 'de: 'a> Deserialize<'de> for ServiceMenu { /// Deserializes the data to the `ServiceMenu` type fn deserialize(deserializer: D) -> Result 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) } } /// Type corresponding to in the EPP greeting XML #[derive(Deserialize, Debug, Eq, PartialEq)] pub struct All; /// Type corresponding to in the EPP greeting XML #[derive(Deserialize, Debug, Eq, PartialEq)] pub struct NoAccess; /// Type corresponding to in the EPP greeting XML #[derive(Deserialize, Debug, Eq, PartialEq)] pub struct Null; /// Type corresponding to in the EPP greeting XML #[derive(Deserialize, Debug, Eq, PartialEq)] pub struct Personal; /// Type corresponding to in the EPP greeting XML #[derive(Deserialize, Debug, Eq, PartialEq)] pub struct PersonalAndOther; /// Type corresponding to in the EPP greeting XML #[derive(Deserialize, Debug, Eq, PartialEq)] pub struct Other; /// Type corresponding to possible type values #[derive(Deserialize, Debug, Eq, PartialEq)] pub enum AccessType { /// Data for the tag #[serde(rename = "all")] All(All), /// Data for the tag #[serde(rename = "none")] NoAccess(NoAccess), /// Data for the tag #[serde(rename = "null")] Null(Null), /// Data for the tag #[serde(rename = "personal")] Personal(Personal), /// Data for the tag #[serde(rename = "personalAndOther")] PersonalAndOther(PersonalAndOther), /// Data for the tag #[serde(rename = "other")] Other(Other), } /// Type corresponding to in the EPP greeting XML #[derive(Deserialize, Debug, Eq, PartialEq)] pub struct Access { #[serde(flatten)] pub ty: AccessType, } /// Type corresponding to possible type values #[derive(Deserialize, Debug, Eq, PartialEq)] pub enum PurposeType { /// Data for the tag #[serde(rename = "admin")] Admin, /// Data for the tag #[serde(rename = "contact")] Contact, /// Data for the tag #[serde(rename = "prov")] Prov, /// Data for the tag #[serde(rename = "other")] OtherPurpose, } /// Type corresponding to in the EPP greeting XML #[derive(Deserialize, Debug, Eq, PartialEq)] pub struct Purpose { #[serde(rename = "$value")] pub purpose: Vec, } /// Type corresponding to possible type values #[derive(Deserialize, Debug, Eq, PartialEq)] pub enum RecipientType { /// Data for the tag #[serde(rename = "other")] Other, /// Data for the tag #[serde(rename = "ours")] Ours, /// Data for the tag #[serde(rename = "public")] Public, /// Data for the tag #[serde(rename = "same")] Same, /// Data for the tag #[serde(rename = "unrelated")] Unrelated, } /// Type corresponding to in the EPP greeting XML #[derive(Deserialize, Debug, Eq, PartialEq)] pub struct Recipient { #[serde(rename = "$value")] pub recipient: Vec, } /// Type corresponding to in the EPP greeting XML #[derive(Deserialize, Debug, Eq, PartialEq)] pub struct Business; /// Type corresponding to in the EPP greeting XML #[derive(Deserialize, Debug, Eq, PartialEq)] pub struct Indefinite; /// Type corresponding to in the EPP greeting XML #[derive(Deserialize, Debug, Eq, PartialEq)] pub struct Legal; /// Type corresponding to in the EPP greeting XML #[derive(Deserialize, Debug, Eq, PartialEq)] pub struct No; /// Type corresponding to in the EPP greeting XML #[derive(Deserialize, Debug, Eq, PartialEq)] pub struct Stated; /// Type corresponding to possible type values #[derive(Deserialize, Debug, Eq, PartialEq)] pub enum RetentionType { /// Data for the tag #[serde(rename = "business")] Business(Business), /// Data for the tag #[serde(rename = "indefinite")] Indefinite(Indefinite), /// Data for the tag #[serde(rename = "legal")] Legal(Legal), /// Data for the tag #[serde(rename = "none")] No(No), /// Data for the tag #[serde(rename = "stated")] Stated(Stated), } /// Type corresponding to in the EPP greeting XML #[derive(Deserialize, Debug, Eq, PartialEq)] pub struct Retention { #[serde(flatten)] pub ty: RetentionType, } /// Type corresponding to in the EPP greeting XML (pending more compliant implementation) #[derive(Deserialize, Debug, Eq, PartialEq)] pub struct Statement { /// Data for the tag pub purpose: Purpose, /// Data for the tag pub recipient: Recipient, /// Data for the tag pub retention: Retention, } /// Type corresponding to value in the EPP greeting XML #[derive(Deserialize, Debug, Eq, PartialEq)] pub struct Absolute { #[serde(rename = "$value")] pub absolute: StringValue<'static>, } /// Type corresponding to value in the EPP greeting XML #[derive(Deserialize, Debug, Eq, PartialEq)] pub struct Relative { #[serde(rename = "$value")] pub relative: StringValue<'static>, } /// Type corresponding to possible type values #[derive(Deserialize, Debug, Eq, PartialEq)] pub enum ExpiryType { /// Data for the tag #[serde(rename = "absolute")] Absolute(Absolute), /// Data for the tag #[serde(rename = "relative")] Relative(Relative), } /// Type corresponding to in the EPP greeting XML #[derive(Deserialize, Debug, Eq, PartialEq)] pub struct Expiry { #[serde(flatten)] pub ty: ExpiryType, } /// Type corresponding to in the EPP greeting XML #[derive(Deserialize, Debug, Eq, PartialEq)] pub struct Dcp { /// Data for the tag pub access: Access, /// Data for the tags pub statement: Vec, /// Data for the tag pub expiry: Option, } #[derive(Deserialize, Debug, Eq, PartialEq)] #[serde(rename_all = "lowercase")] /// Type corresponding to the tag in the EPP greeting XML pub struct Greeting { /// The service ID #[serde(rename = "svID")] pub service_id: String, /// The date from the EPP server #[serde(rename = "svDate")] pub service_date: DateTime, /// Data under the element #[serde(rename = "svcMenu")] pub svc_menu: ServiceMenu, /// Data under the element pub dcp: Dcp, } #[derive(Deserialize, Debug, Eq, PartialEq)] #[serde(rename = "epp")] pub struct GreetingDocument { #[serde(rename = "greeting")] pub data: Greeting, } #[cfg(test)] mod tests { use chrono::{TimeZone, Utc}; use super::{ExpiryType, GreetingDocument, HelloDocument, Relative}; use crate::tests::get_xml; use crate::xml; #[test] fn hello() { let xml = get_xml("request/hello.xml").unwrap(); let serialized = xml::serialize(&HelloDocument::default()).unwrap(); assert_eq!(xml, serialized); } #[test] fn greeting() { let xml = get_xml("response/greeting.xml").unwrap(); let object = xml::deserialize::(xml.as_str()).unwrap(); assert_eq!(object.data.service_id, "ISPAPI EPP Server"); assert_eq!( object.data.service_date, Utc.with_ymd_and_hms(2021, 7, 25, 14, 51, 17).unwrap() ); assert_eq!(object.data.svc_menu.options.version, "1.0".into()); assert_eq!(object.data.svc_menu.options.lang, "en".into()); assert_eq!(object.data.svc_menu.services.obj_uris.len(), 4); assert_eq!( object .data .svc_menu .services .svc_ext .unwrap() .ext_uris .unwrap() .len(), 5 ); assert_eq!(object.data.dcp.statement.len(), 2); assert_eq!( object.data.dcp.expiry.unwrap().ty, ExpiryType::Relative(Relative { relative: "P1M".into() }) ); } }