1use std::fmt::Debug;
4
5use chrono::{DateTime, Utc};
6use instant_xml::{FromXml, Kind};
7
8use crate::common::EPP_XMLNS;
9
10#[derive(Debug, Eq, FromXml, PartialEq)]
12#[xml(rename = "undef", ns(EPP_XMLNS))]
13pub struct Undef;
14
15#[derive(Debug, Eq, FromXml, PartialEq)]
17#[xml(rename = "value", ns(EPP_XMLNS))]
18pub struct ResultValue {
19 pub undef: Undef,
21}
22
23#[derive(Debug, Eq, FromXml, PartialEq)]
25#[xml(rename = "extValue", ns(EPP_XMLNS))]
26pub struct ExtValue {
27 pub value: ResultValue,
29 pub reason: String,
31}
32
33#[derive(Debug, Eq, FromXml, PartialEq)]
35#[xml(rename = "result", ns(EPP_XMLNS))]
36pub struct EppResult {
37 #[xml(attribute)]
39 pub code: ResultCode,
40 #[xml(rename = "msg")]
42 pub message: String,
43 pub ext_value: Option<ExtValue>,
45}
46
47#[derive(Clone, Copy, Debug, Eq, PartialEq)]
49pub enum ResultCode {
50 CommandCompletedSuccessfully = 1000,
51 CommandCompletedSuccessfullyActionPending = 1001,
52 CommandCompletedSuccessfullyNoMessages = 1300,
53 CommandCompletedSuccessfullyAckToDequeue = 1301,
54 CommandCompletedSuccessfullyEndingSession = 1500,
55 UnknownCommand = 2000,
56 CommandSyntaxError = 2001,
57 CommandUseError = 2002,
58 RequiredParameterMissing = 2003,
59 ParameterValueRangeError = 2004,
60 ParameterValueSyntaxError = 2005,
61 UnimplementedProtocolVersion = 2100,
62 UnimplementedCommand = 2101,
63 UnimplementedOption = 2102,
64 UnimplementedExtension = 2103,
65 BillingFailure = 2104,
66 ObjectIsNotEligibleForRenewal = 2105,
67 ObjectIsNotEligibleForTransfer = 2106,
68 AuthenticationError = 2200,
69 AuthorizationError = 2201,
70 InvalidAuthorizationInformation = 2202,
71 ObjectPendingTransfer = 2300,
72 ObjectNotPendingTransfer = 2301,
73 ObjectExists = 2302,
74 ObjectDoesNotExist = 2303,
75 ObjectStatusProhibitsOperation = 2304,
76 ObjectAssociationProhibitsOperation = 2305,
77 ParameterValuePolicyError = 2306,
78 UnimplementedObjectService = 2307,
79 DataManagementPolicyViolation = 2308,
80 CommandFailed = 2400,
81 CommandFailedServerClosingConnection = 2500,
82 AuthenticationErrorServerClosingConnection = 2501,
83 SessionLimitExceededServerClosingConnection = 2502,
84}
85
86impl ResultCode {
87 pub fn from_u16(code: u16) -> Option<Self> {
88 match code {
89 1000 => Some(Self::CommandCompletedSuccessfully),
90 1001 => Some(Self::CommandCompletedSuccessfullyActionPending),
91 1300 => Some(Self::CommandCompletedSuccessfullyNoMessages),
92 1301 => Some(Self::CommandCompletedSuccessfullyAckToDequeue),
93 1500 => Some(Self::CommandCompletedSuccessfullyEndingSession),
94 2000 => Some(Self::UnknownCommand),
95 2001 => Some(Self::CommandSyntaxError),
96 2002 => Some(Self::CommandUseError),
97 2003 => Some(Self::RequiredParameterMissing),
98 2004 => Some(Self::ParameterValueRangeError),
99 2005 => Some(Self::ParameterValueSyntaxError),
100 2100 => Some(Self::UnimplementedProtocolVersion),
101 2101 => Some(Self::UnimplementedCommand),
102 2102 => Some(Self::UnimplementedOption),
103 2103 => Some(Self::UnimplementedExtension),
104 2104 => Some(Self::BillingFailure),
105 2105 => Some(Self::ObjectIsNotEligibleForRenewal),
106 2106 => Some(Self::ObjectIsNotEligibleForTransfer),
107 2200 => Some(Self::AuthenticationError),
108 2201 => Some(Self::AuthorizationError),
109 2202 => Some(Self::InvalidAuthorizationInformation),
110 2300 => Some(Self::ObjectPendingTransfer),
111 2301 => Some(Self::ObjectNotPendingTransfer),
112 2302 => Some(Self::ObjectExists),
113 2303 => Some(Self::ObjectDoesNotExist),
114 2304 => Some(Self::ObjectStatusProhibitsOperation),
115 2305 => Some(Self::ObjectAssociationProhibitsOperation),
116 2306 => Some(Self::ParameterValuePolicyError),
117 2307 => Some(Self::UnimplementedObjectService),
118 2308 => Some(Self::DataManagementPolicyViolation),
119 2400 => Some(Self::CommandFailed),
120 2500 => Some(Self::CommandFailedServerClosingConnection),
121 2501 => Some(Self::AuthenticationErrorServerClosingConnection),
122 2502 => Some(Self::SessionLimitExceededServerClosingConnection),
123 _ => None,
124 }
125 }
126
127 pub fn is_success(&self) -> bool {
128 use ResultCode::*;
129 matches!(
130 self,
131 CommandCompletedSuccessfully
132 | CommandCompletedSuccessfullyActionPending
133 | CommandCompletedSuccessfullyNoMessages
134 | CommandCompletedSuccessfullyAckToDequeue
135 | CommandCompletedSuccessfullyEndingSession
136 )
137 }
138
139 pub fn is_persistent(&self) -> bool {
144 use ResultCode::*;
145 match self {
146 UnknownCommand
148 | CommandSyntaxError
149 | RequiredParameterMissing
150 | ParameterValueRangeError
151 | ParameterValueSyntaxError
152 | UnimplementedProtocolVersion
153 | UnimplementedCommand
154 | UnimplementedOption
155 | UnimplementedExtension => true,
156 CommandFailedServerClosingConnection
158 | AuthenticationErrorServerClosingConnection
159 | SessionLimitExceededServerClosingConnection => true,
160 _ => false,
161 }
162 }
163}
164
165impl<'xml> FromXml<'xml> for ResultCode {
166 fn matches(id: instant_xml::Id<'_>, field: Option<instant_xml::Id<'_>>) -> bool {
167 match field {
168 Some(field) => id == field,
169 None => false,
170 }
171 }
172
173 fn deserialize<'cx>(
174 into: &mut Self::Accumulator,
175 field: &'static str,
176 deserializer: &mut instant_xml::Deserializer<'cx, 'xml>,
177 ) -> Result<(), instant_xml::Error> {
178 let mut value = None;
179 u16::deserialize(&mut value, field, deserializer)?;
180 if let Some(value) = value {
181 *into = match Self::from_u16(value) {
182 Some(value) => Some(value),
183 None => {
184 return Err(instant_xml::Error::UnexpectedValue(format!(
185 "unexpected result code '{value}'"
186 )))
187 }
188 };
189 }
190
191 Ok(())
192 }
193
194 type Accumulator = Option<Self>;
195 const KIND: instant_xml::Kind = Kind::Scalar;
196}
197
198#[derive(Debug, Eq, FromXml, PartialEq)]
200#[xml(rename = "trID", ns(EPP_XMLNS))]
201pub struct ResponseTRID {
202 #[xml(rename = "clTRID")]
204 pub client_tr_id: Option<String>,
205 #[xml(rename = "svTRID")]
207 pub server_tr_id: String,
208}
209
210#[derive(Debug, Eq, FromXml, PartialEq)]
212#[xml(rename = "msgQ", ns(EPP_XMLNS))]
213pub struct MessageQueue {
214 #[xml(attribute)]
216 pub count: u32,
217 #[xml(attribute)]
219 pub id: String,
220 #[xml(rename = "qDate")]
222 pub date: Option<DateTime<Utc>>,
223 #[xml(rename = "msg")]
225 pub message: Option<Message>,
226}
227
228#[derive(Debug, Eq, FromXml, PartialEq)]
229#[xml(rename = "msg", ns(EPP_XMLNS))]
230pub struct Message {
231 #[xml(attribute)]
232 pub lang: Option<String>,
233 #[xml(direct)]
234 pub text: String,
235}
236
237#[derive(Debug, FromXml, PartialEq)]
238#[xml(rename = "response", ns(EPP_XMLNS))]
241pub struct Response<D, E> {
242 pub result: EppResult,
244 #[xml(rename = "msgQ")]
246 pub message_queue: Option<MessageQueue>,
247 pub res_data: Option<ResponseData<D>>,
249 pub extension: Option<Extension<E>>,
251 pub tr_ids: ResponseTRID,
253}
254
255#[derive(Debug, Eq, FromXml, PartialEq)]
256#[xml(rename = "resData", ns(EPP_XMLNS))]
257pub struct ResponseData<D> {
258 data: D,
259}
260
261impl<D> ResponseData<D> {
262 pub fn into_inner(self) -> D {
263 self.data
264 }
265}
266
267#[derive(Debug, FromXml, PartialEq)]
268#[xml(rename = "response", ns(EPP_XMLNS))]
271pub struct ResponseStatus {
272 pub result: EppResult,
274 #[xml(rename = "trID")]
275 pub tr_ids: ResponseTRID,
277}
278
279impl<T, E> Response<T, E> {
280 pub fn res_data(&self) -> Option<&T> {
282 match &self.res_data {
283 Some(res_data) => Some(&res_data.data),
284 None => None,
285 }
286 }
287
288 pub fn extension(&self) -> Option<&E> {
289 match &self.extension {
290 Some(extension) => Some(&extension.data),
291 None => None,
292 }
293 }
294
295 pub fn message_queue(&self) -> Option<&MessageQueue> {
297 match &self.message_queue {
298 Some(queue) => Some(queue),
299 None => None,
300 }
301 }
302}
303
304#[derive(Debug, Eq, FromXml, PartialEq)]
305#[xml(rename = "extension", ns(EPP_XMLNS))]
306pub struct Extension<E> {
307 pub data: E,
308}
309
310#[cfg(test)]
311mod tests {
312 use super::{ResponseStatus, ResultCode};
313 use crate::tests::{get_xml, CLTRID, SVTRID};
314 use crate::xml;
315
316 #[test]
317 fn error() {
318 let xml = get_xml("response/error.xml").unwrap();
319 let object = xml::deserialize::<ResponseStatus>(xml.as_str()).unwrap();
320
321 assert_eq!(object.result.code, ResultCode::ObjectDoesNotExist);
322 assert_eq!(object.result.message, "Object does not exist");
323 assert_eq!(
324 object.result.ext_value.unwrap().reason,
325 "545 Object not found"
326 );
327 assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
328 assert_eq!(object.tr_ids.server_tr_id, SVTRID);
329 }
330}