//! Types for EPP responses use std::fmt::{self, Debug}; use chrono::{DateTime, Utc}; use serde::Deserialize; use crate::common::StringValue; /// Type corresponding to the tag an EPP response XML #[derive(Deserialize, Debug, Eq, PartialEq)] pub struct Undef; /// Type corresponding to the tag under in an EPP response XML #[derive(Deserialize, Debug, Eq, PartialEq)] pub struct ResultValue { /// The XML namespace for the tag #[serde(rename = "xmlns:epp")] xmlns: String, /// The element pub undef: Undef, } /// Type corresponding to the tag in an EPP response XML #[derive(Deserialize, Debug, Eq, PartialEq)] pub struct ExtValue { /// Data under the tag pub value: ResultValue, /// Data under the tag pub reason: StringValue<'static>, } /// Type corresponding to the tag in an EPP response XML #[derive(Deserialize, Debug, Eq, PartialEq)] pub struct EppResult { /// The result code pub code: ResultCode, /// The result message #[serde(rename = "msg")] pub message: StringValue<'static>, /// Data under the tag #[serde(rename = "extValue")] pub ext_value: Option, } /// Response codes as enumerated in section 3 of RFC 5730 #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum ResultCode { CommandCompletedSuccessfully = 1000, CommandCompletedSuccessfullyActionPending = 1001, CommandCompletedSuccessfullyNoMessages = 1300, CommandCompletedSuccessfullyAckToDequeue = 1301, CommandCompletedSuccessfullyEndingSession = 1500, UnknownCommand = 2000, CommandSyntaxError = 2001, CommandUseError = 2002, RequiredParameterMissing = 2003, ParameterValueRangeError = 2004, ParameterValueSyntaxError = 2005, UnimplementedProtocolVersion = 2100, UnimplementedCommand = 2101, UnimplementedOption = 2102, UnimplementedExtension = 2103, BillingFailure = 2104, ObjectIsNotEligibleForRenewal = 2105, ObjectIsNotEligibleForTransfer = 2106, AuthenticationError = 2200, AuthorizationError = 2201, InvalidAuthorizationInformation = 2202, ObjectPendingTransfer = 2300, ObjectNotPendingTransfer = 2301, ObjectExists = 2302, ObjectDoesNotExist = 2303, ObjectStatusProhibitsOperation = 2304, ObjectAssociationProhibitsOperation = 2305, ParameterValuePolicyError = 2306, UnimplementedObjectService = 2307, DataManagementPolicyViolation = 2308, CommandFailed = 2400, CommandFailedServerClosingConnection = 2500, AuthenticationErrorServerClosingConnection = 2501, SessionLimitExceededServerClosingConnection = 2502, } impl ResultCode { pub fn from_u16(code: u16) -> Option { match code { 1000 => Some(ResultCode::CommandCompletedSuccessfully), 1001 => Some(ResultCode::CommandCompletedSuccessfullyActionPending), 1300 => Some(ResultCode::CommandCompletedSuccessfullyNoMessages), 1301 => Some(ResultCode::CommandCompletedSuccessfullyAckToDequeue), 1500 => Some(ResultCode::CommandCompletedSuccessfullyEndingSession), 2000 => Some(ResultCode::UnknownCommand), 2001 => Some(ResultCode::CommandSyntaxError), 2002 => Some(ResultCode::CommandUseError), 2003 => Some(ResultCode::RequiredParameterMissing), 2004 => Some(ResultCode::ParameterValueRangeError), 2005 => Some(ResultCode::ParameterValueSyntaxError), 2100 => Some(ResultCode::UnimplementedProtocolVersion), 2101 => Some(ResultCode::UnimplementedCommand), 2102 => Some(ResultCode::UnimplementedOption), 2103 => Some(ResultCode::UnimplementedExtension), 2104 => Some(ResultCode::BillingFailure), 2105 => Some(ResultCode::ObjectIsNotEligibleForRenewal), 2106 => Some(ResultCode::ObjectIsNotEligibleForTransfer), 2200 => Some(ResultCode::AuthenticationError), 2201 => Some(ResultCode::AuthorizationError), 2202 => Some(ResultCode::InvalidAuthorizationInformation), 2300 => Some(ResultCode::ObjectPendingTransfer), 2301 => Some(ResultCode::ObjectNotPendingTransfer), 2302 => Some(ResultCode::ObjectExists), 2303 => Some(ResultCode::ObjectDoesNotExist), 2304 => Some(ResultCode::ObjectStatusProhibitsOperation), 2305 => Some(ResultCode::ObjectAssociationProhibitsOperation), 2306 => Some(ResultCode::ParameterValuePolicyError), 2307 => Some(ResultCode::UnimplementedObjectService), 2308 => Some(ResultCode::DataManagementPolicyViolation), 2400 => Some(ResultCode::CommandFailed), 2500 => Some(ResultCode::CommandFailedServerClosingConnection), 2501 => Some(ResultCode::AuthenticationErrorServerClosingConnection), 2502 => Some(ResultCode::SessionLimitExceededServerClosingConnection), _ => None, } } pub fn is_success(&self) -> bool { use ResultCode::*; matches!( self, CommandCompletedSuccessfully | CommandCompletedSuccessfullyActionPending | CommandCompletedSuccessfullyNoMessages | CommandCompletedSuccessfullyAckToDequeue | CommandCompletedSuccessfullyEndingSession ) } } impl<'de> Deserialize<'de> for ResultCode { fn deserialize(deserializer: D) -> Result where D: serde::de::Deserializer<'de>, { deserializer.deserialize_u16(ResultCodeVisitor) } } struct ResultCodeVisitor; impl<'de> serde::de::Visitor<'de> for ResultCodeVisitor { type Value = ResultCode; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a valid EPP result code") } fn visit_u16(self, v: u16) -> Result where E: serde::de::Error, { use serde::de::Unexpected; ResultCode::from_u16(v).ok_or_else(|| { E::invalid_value(Unexpected::Unsigned(v as u64), &"unexpected result code") }) } } /// Type corresponding to the tag in an EPP response XML #[derive(Deserialize, Debug, Eq, PartialEq)] pub struct ResponseTRID { /// The client TRID #[serde(rename = "clTRID")] pub client_tr_id: Option>, /// The server TRID #[serde(rename = "svTRID")] pub server_tr_id: StringValue<'static>, } /// Type corresponding to the tag in an EPP response XML #[derive(Deserialize, Debug, Eq, PartialEq)] pub struct MessageQueue { /// The message count pub count: u32, /// The message ID pub id: String, /// The message date #[serde(rename = "qDate")] pub date: Option>, /// The message text #[serde(rename = "msg")] pub message: Option>, } #[derive(Deserialize, Debug, Eq, PartialEq)] /// Type corresponding to the <response> tag in an EPP response XML /// containing an <extension> tag pub struct Response { /// Data under the tag pub result: EppResult, /// Data under the tag #[serde(rename = "msgQ")] pub message_queue: Option, #[serde(rename = "resData")] /// Data under the <resData> tag pub res_data: Option, /// Data under the <extension> tag pub extension: Option, /// Data under the tag #[serde(rename = "trID")] pub tr_ids: ResponseTRID, } #[derive(Deserialize, Debug, Eq, PartialEq)] #[serde(rename = "epp")] pub struct ResponseDocument { #[serde(rename = "response")] pub data: Response, } #[derive(Debug, Deserialize, Eq, PartialEq)] #[serde(rename = "epp")] pub struct ResultDocument { #[serde(rename = "response")] pub data: ResponseStatus, } #[derive(Deserialize, Debug, Eq, PartialEq)] /// Type corresponding to the <response> tag in an EPP response XML /// without or <resData> sections. Generally used for error handling pub struct ResponseStatus { /// Data under the tag pub result: EppResult, #[serde(rename = "trID")] /// Data under the tag pub tr_ids: ResponseTRID, } impl Response { /// Returns the data under the corresponding <resData> from the EPP XML pub fn res_data(&self) -> Option<&T> { match &self.res_data { Some(res_data) => Some(res_data), None => None, } } /// Returns the data under the corresponding from the EPP XML pub fn message_queue(&self) -> Option<&MessageQueue> { match &self.message_queue { Some(queue) => Some(queue), None => None, } } } #[cfg(test)] mod tests { use super::{ResultCode, ResultDocument}; use crate::tests::{get_xml, CLTRID, SVTRID}; use crate::xml; #[test] fn error() { let xml = get_xml("response/error.xml").unwrap(); let object = xml::deserialize::(xml.as_str()).unwrap(); assert_eq!(object.data.result.code, ResultCode::ObjectDoesNotExist); assert_eq!(object.data.result.message, "Object does not exist".into()); assert_eq!( object.data.result.ext_value.unwrap().reason, "545 Object not found".into() ); assert_eq!(object.data.tr_ids.client_tr_id.unwrap(), CLTRID.into()); assert_eq!(object.data.tr_ids.server_tr_id, SVTRID.into()); } }