1use std::fmt::{self, Debug};
4
5use chrono::{DateTime, Utc};
6use serde::Deserialize;
7
8use crate::common::StringValue;
9
10#[derive(Deserialize, Debug, Eq, PartialEq)]
12pub struct Undef;
13
14#[derive(Deserialize, Debug, Eq, PartialEq)]
16pub struct ResultValue {
17 #[serde(rename = "xmlns:epp")]
19 xmlns: String,
20 pub undef: Undef,
22}
23
24#[derive(Deserialize, Debug, Eq, PartialEq)]
26pub struct ExtValue {
27 pub value: ResultValue,
29 pub reason: StringValue<'static>,
31}
32
33#[derive(Deserialize, Debug, Eq, PartialEq)]
35pub struct EppResult {
36 pub code: ResultCode,
38 #[serde(rename = "msg")]
40 pub message: StringValue<'static>,
41 #[serde(rename = "extValue")]
43 pub ext_value: Option<ExtValue>,
44}
45
46#[derive(Clone, Copy, Debug, Eq, PartialEq)]
48pub enum ResultCode {
49 CommandCompletedSuccessfully = 1000,
50 CommandCompletedSuccessfullyActionPending = 1001,
51 CommandCompletedSuccessfullyNoMessages = 1300,
52 CommandCompletedSuccessfullyAckToDequeue = 1301,
53 CommandCompletedSuccessfullyEndingSession = 1500,
54 UnknownCommand = 2000,
55 CommandSyntaxError = 2001,
56 CommandUseError = 2002,
57 RequiredParameterMissing = 2003,
58 ParameterValueRangeError = 2004,
59 ParameterValueSyntaxError = 2005,
60 UnimplementedProtocolVersion = 2100,
61 UnimplementedCommand = 2101,
62 UnimplementedOption = 2102,
63 UnimplementedExtension = 2103,
64 BillingFailure = 2104,
65 ObjectIsNotEligibleForRenewal = 2105,
66 ObjectIsNotEligibleForTransfer = 2106,
67 AuthenticationError = 2200,
68 AuthorizationError = 2201,
69 InvalidAuthorizationInformation = 2202,
70 ObjectPendingTransfer = 2300,
71 ObjectNotPendingTransfer = 2301,
72 ObjectExists = 2302,
73 ObjectDoesNotExist = 2303,
74 ObjectStatusProhibitsOperation = 2304,
75 ObjectAssociationProhibitsOperation = 2305,
76 ParameterValuePolicyError = 2306,
77 UnimplementedObjectService = 2307,
78 DataManagementPolicyViolation = 2308,
79 CommandFailed = 2400,
80 CommandFailedServerClosingConnection = 2500,
81 AuthenticationErrorServerClosingConnection = 2501,
82 SessionLimitExceededServerClosingConnection = 2502,
83}
84
85impl ResultCode {
86 pub fn from_u16(code: u16) -> Option<Self> {
87 match code {
88 1000 => Some(ResultCode::CommandCompletedSuccessfully),
89 1001 => Some(ResultCode::CommandCompletedSuccessfullyActionPending),
90 1300 => Some(ResultCode::CommandCompletedSuccessfullyNoMessages),
91 1301 => Some(ResultCode::CommandCompletedSuccessfullyAckToDequeue),
92 1500 => Some(ResultCode::CommandCompletedSuccessfullyEndingSession),
93 2000 => Some(ResultCode::UnknownCommand),
94 2001 => Some(ResultCode::CommandSyntaxError),
95 2002 => Some(ResultCode::CommandUseError),
96 2003 => Some(ResultCode::RequiredParameterMissing),
97 2004 => Some(ResultCode::ParameterValueRangeError),
98 2005 => Some(ResultCode::ParameterValueSyntaxError),
99 2100 => Some(ResultCode::UnimplementedProtocolVersion),
100 2101 => Some(ResultCode::UnimplementedCommand),
101 2102 => Some(ResultCode::UnimplementedOption),
102 2103 => Some(ResultCode::UnimplementedExtension),
103 2104 => Some(ResultCode::BillingFailure),
104 2105 => Some(ResultCode::ObjectIsNotEligibleForRenewal),
105 2106 => Some(ResultCode::ObjectIsNotEligibleForTransfer),
106 2200 => Some(ResultCode::AuthenticationError),
107 2201 => Some(ResultCode::AuthorizationError),
108 2202 => Some(ResultCode::InvalidAuthorizationInformation),
109 2300 => Some(ResultCode::ObjectPendingTransfer),
110 2301 => Some(ResultCode::ObjectNotPendingTransfer),
111 2302 => Some(ResultCode::ObjectExists),
112 2303 => Some(ResultCode::ObjectDoesNotExist),
113 2304 => Some(ResultCode::ObjectStatusProhibitsOperation),
114 2305 => Some(ResultCode::ObjectAssociationProhibitsOperation),
115 2306 => Some(ResultCode::ParameterValuePolicyError),
116 2307 => Some(ResultCode::UnimplementedObjectService),
117 2308 => Some(ResultCode::DataManagementPolicyViolation),
118 2400 => Some(ResultCode::CommandFailed),
119 2500 => Some(ResultCode::CommandFailedServerClosingConnection),
120 2501 => Some(ResultCode::AuthenticationErrorServerClosingConnection),
121 2502 => Some(ResultCode::SessionLimitExceededServerClosingConnection),
122 _ => None,
123 }
124 }
125
126 pub fn is_success(&self) -> bool {
127 use ResultCode::*;
128 matches!(
129 self,
130 CommandCompletedSuccessfully
131 | CommandCompletedSuccessfullyActionPending
132 | CommandCompletedSuccessfullyNoMessages
133 | CommandCompletedSuccessfullyAckToDequeue
134 | CommandCompletedSuccessfullyEndingSession
135 )
136 }
137}
138
139impl<'de> Deserialize<'de> for ResultCode {
140 fn deserialize<D>(deserializer: D) -> Result<ResultCode, D::Error>
141 where
142 D: serde::de::Deserializer<'de>,
143 {
144 deserializer.deserialize_u16(ResultCodeVisitor)
145 }
146}
147
148struct ResultCodeVisitor;
149
150impl<'de> serde::de::Visitor<'de> for ResultCodeVisitor {
151 type Value = ResultCode;
152
153 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
154 formatter.write_str("a valid EPP result code")
155 }
156
157 fn visit_u16<E>(self, v: u16) -> Result<Self::Value, E>
158 where
159 E: serde::de::Error,
160 {
161 use serde::de::Unexpected;
162 ResultCode::from_u16(v).ok_or_else(|| {
163 E::invalid_value(Unexpected::Unsigned(v as u64), &"unexpected result code")
164 })
165 }
166}
167
168#[derive(Deserialize, Debug, Eq, PartialEq)]
170pub struct ResponseTRID {
171 #[serde(rename = "clTRID")]
173 pub client_tr_id: Option<StringValue<'static>>,
174 #[serde(rename = "svTRID")]
176 pub server_tr_id: StringValue<'static>,
177}
178
179#[derive(Deserialize, Debug, Eq, PartialEq)]
181pub struct MessageQueue {
182 pub count: u32,
184 pub id: String,
186 #[serde(rename = "qDate")]
188 pub date: Option<DateTime<Utc>>,
189 #[serde(rename = "msg")]
191 pub message: Option<StringValue<'static>>,
192}
193
194#[derive(Deserialize, Debug, Eq, PartialEq)]
195pub struct Response<D, E> {
198 pub result: EppResult,
200 #[serde(rename = "msgQ")]
202 pub message_queue: Option<MessageQueue>,
203 #[serde(rename = "resData")]
204 pub res_data: Option<D>,
206 pub extension: Option<E>,
208 #[serde(rename = "trID")]
210 pub tr_ids: ResponseTRID,
211}
212
213#[derive(Deserialize, Debug, Eq, PartialEq)]
214#[serde(rename = "epp")]
215pub struct ResponseDocument<D, E> {
216 #[serde(rename = "response")]
217 pub data: Response<D, E>,
218}
219
220#[derive(Debug, Deserialize, Eq, PartialEq)]
221#[serde(rename = "epp")]
222pub struct ResultDocument {
223 #[serde(rename = "response")]
224 pub data: ResponseStatus,
225}
226
227#[derive(Deserialize, Debug, Eq, PartialEq)]
228pub struct ResponseStatus {
231 pub result: EppResult,
233 #[serde(rename = "trID")]
234 pub tr_ids: ResponseTRID,
236}
237
238impl<T, E> Response<T, E> {
239 pub fn res_data(&self) -> Option<&T> {
241 match &self.res_data {
242 Some(res_data) => Some(res_data),
243 None => None,
244 }
245 }
246 pub fn message_queue(&self) -> Option<&MessageQueue> {
248 match &self.message_queue {
249 Some(queue) => Some(queue),
250 None => None,
251 }
252 }
253}
254
255#[cfg(test)]
256mod tests {
257 use super::{ResultCode, ResultDocument};
258 use crate::tests::{get_xml, CLTRID, SVTRID};
259 use crate::xml;
260
261 #[test]
262 fn error() {
263 let xml = get_xml("response/error.xml").unwrap();
264 let object = xml::deserialize::<ResultDocument>(xml.as_str()).unwrap();
265
266 assert_eq!(object.data.result.code, ResultCode::ObjectDoesNotExist);
267 assert_eq!(object.data.result.message, "Object does not exist".into());
268 assert_eq!(
269 object.data.result.ext_value.unwrap().reason,
270 "545 Object not found".into()
271 );
272 assert_eq!(object.data.tr_ids.client_tr_id.unwrap(), CLTRID.into());
273 assert_eq!(object.data.tr_ids.server_tr_id, SVTRID.into());
274 }
275}