1use std::fmt::Debug;
2
3use chrono::{DateTime, Utc};
4use serde::{Deserialize, Deserializer, Serialize};
5
6use crate::common::{Options, ServiceExtension, Services, StringValue, EPP_XMLNS};
7
8#[derive(Debug, PartialEq, Serialize)]
11struct Hello;
12
13#[derive(Debug, PartialEq, Serialize)]
14#[serde(rename = "epp")]
15pub struct HelloDocument {
16 xmlns: &'static str,
17 hello: Hello,
18}
19
20impl Default for HelloDocument {
21 fn default() -> Self {
22 Self {
23 xmlns: EPP_XMLNS,
24 hello: Hello,
25 }
26 }
27}
28
29#[derive(Debug, Eq, PartialEq)]
33pub struct ServiceMenu {
34 pub options: Options<'static>,
35 pub services: Services<'static>,
36}
37
38#[derive(Deserialize, Debug, PartialEq)]
40struct FlattenedServiceMenu {
41 pub version: StringValue<'static>,
42 pub lang: StringValue<'static>,
43 #[serde(rename = "objURI")]
44 pub obj_uris: Vec<StringValue<'static>>,
45 #[serde(rename = "svcExtension")]
46 pub svc_ext: Option<ServiceExtension<'static>>,
47}
48
49impl<'a, 'de: 'a> Deserialize<'de> for ServiceMenu {
50 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
52 where
53 D: Deserializer<'de>,
54 {
55 let flattened_svc_menu = FlattenedServiceMenu::deserialize(deserializer)?;
56
57 let svc_menu = ServiceMenu {
58 options: Options {
59 version: flattened_svc_menu.version,
60 lang: flattened_svc_menu.lang,
61 },
62 services: Services {
63 obj_uris: flattened_svc_menu.obj_uris,
64 svc_ext: flattened_svc_menu.svc_ext,
65 },
66 };
67
68 Ok(svc_menu)
69 }
70}
71
72#[derive(Deserialize, Debug, Eq, PartialEq)]
74pub struct All;
75
76#[derive(Deserialize, Debug, Eq, PartialEq)]
78pub struct NoAccess;
79
80#[derive(Deserialize, Debug, Eq, PartialEq)]
82pub struct Null;
83
84#[derive(Deserialize, Debug, Eq, PartialEq)]
86pub struct Personal;
87
88#[derive(Deserialize, Debug, Eq, PartialEq)]
90pub struct PersonalAndOther;
91
92#[derive(Deserialize, Debug, Eq, PartialEq)]
94pub struct Other;
95
96#[derive(Deserialize, Debug, Eq, PartialEq)]
98pub enum AccessType {
99 #[serde(rename = "all")]
101 All(All),
102 #[serde(rename = "none")]
104 NoAccess(NoAccess),
105 #[serde(rename = "null")]
107 Null(Null),
108 #[serde(rename = "personal")]
110 Personal(Personal),
111 #[serde(rename = "personalAndOther")]
113 PersonalAndOther(PersonalAndOther),
114 #[serde(rename = "other")]
116 Other(Other),
117}
118
119#[derive(Deserialize, Debug, Eq, PartialEq)]
121pub struct Access {
122 #[serde(flatten)]
123 pub ty: AccessType,
124}
125
126#[derive(Deserialize, Debug, Eq, PartialEq)]
128pub enum PurposeType {
129 #[serde(rename = "admin")]
131 Admin,
132 #[serde(rename = "contact")]
134 Contact,
135 #[serde(rename = "prov")]
137 Prov,
138 #[serde(rename = "other")]
140 OtherPurpose,
141}
142
143#[derive(Deserialize, Debug, Eq, PartialEq)]
145pub struct Purpose {
146 #[serde(rename = "$value")]
147 pub purpose: Vec<PurposeType>,
148}
149
150#[derive(Deserialize, Debug, Eq, PartialEq)]
152pub enum RecipientType {
153 #[serde(rename = "other")]
155 Other,
156 #[serde(rename = "ours")]
158 Ours,
159 #[serde(rename = "public")]
161 Public,
162 #[serde(rename = "same")]
164 Same,
165 #[serde(rename = "unrelated")]
167 Unrelated,
168}
169
170#[derive(Deserialize, Debug, Eq, PartialEq)]
172pub struct Recipient {
173 #[serde(rename = "$value")]
174 pub recipient: Vec<RecipientType>,
175}
176
177#[derive(Deserialize, Debug, Eq, PartialEq)]
179pub struct Business;
180
181#[derive(Deserialize, Debug, Eq, PartialEq)]
183pub struct Indefinite;
184
185#[derive(Deserialize, Debug, Eq, PartialEq)]
187pub struct Legal;
188
189#[derive(Deserialize, Debug, Eq, PartialEq)]
191pub struct No;
192
193#[derive(Deserialize, Debug, Eq, PartialEq)]
195pub struct Stated;
196
197#[derive(Deserialize, Debug, Eq, PartialEq)]
199pub enum RetentionType {
200 #[serde(rename = "business")]
202 Business(Business),
203 #[serde(rename = "indefinite")]
205 Indefinite(Indefinite),
206 #[serde(rename = "legal")]
208 Legal(Legal),
209 #[serde(rename = "none")]
211 No(No),
212 #[serde(rename = "stated")]
214 Stated(Stated),
215}
216
217#[derive(Deserialize, Debug, Eq, PartialEq)]
219pub struct Retention {
220 #[serde(flatten)]
221 pub ty: RetentionType,
222}
223
224#[derive(Deserialize, Debug, Eq, PartialEq)]
226pub struct Statement {
227 pub purpose: Purpose,
229 pub recipient: Recipient,
231 pub retention: Retention,
233}
234
235#[derive(Deserialize, Debug, Eq, PartialEq)]
237pub struct Absolute {
238 #[serde(rename = "$value")]
239 pub absolute: StringValue<'static>,
240}
241
242#[derive(Deserialize, Debug, Eq, PartialEq)]
244pub struct Relative {
245 #[serde(rename = "$value")]
246 pub relative: StringValue<'static>,
247}
248
249#[derive(Deserialize, Debug, Eq, PartialEq)]
251pub enum ExpiryType {
252 #[serde(rename = "absolute")]
254 Absolute(Absolute),
255 #[serde(rename = "relative")]
257 Relative(Relative),
258}
259
260#[derive(Deserialize, Debug, Eq, PartialEq)]
262pub struct Expiry {
263 #[serde(flatten)]
264 pub ty: ExpiryType,
265}
266
267#[derive(Deserialize, Debug, Eq, PartialEq)]
269pub struct Dcp {
270 pub access: Access,
272 pub statement: Vec<Statement>,
274 pub expiry: Option<Expiry>,
276}
277
278#[derive(Deserialize, Debug, Eq, PartialEq)]
279#[serde(rename_all = "lowercase")]
280pub struct Greeting {
282 #[serde(rename = "svID")]
284 pub service_id: String,
285 #[serde(rename = "svDate")]
287 pub service_date: DateTime<Utc>,
288 #[serde(rename = "svcMenu")]
290 pub svc_menu: ServiceMenu,
291 pub dcp: Dcp,
293}
294
295#[derive(Deserialize, Debug, Eq, PartialEq)]
296#[serde(rename = "epp")]
297pub struct GreetingDocument {
298 #[serde(rename = "greeting")]
299 pub data: Greeting,
300}
301
302#[cfg(test)]
303mod tests {
304 use chrono::{TimeZone, Utc};
305
306 use super::{ExpiryType, GreetingDocument, HelloDocument, Relative};
307 use crate::tests::get_xml;
308 use crate::xml;
309
310 #[test]
311 fn hello() {
312 let xml = get_xml("request/hello.xml").unwrap();
313 let serialized = xml::serialize(&HelloDocument::default()).unwrap();
314
315 assert_eq!(xml, serialized);
316 }
317
318 #[test]
319 fn greeting() {
320 let xml = get_xml("response/greeting.xml").unwrap();
321 let object = xml::deserialize::<GreetingDocument>(xml.as_str()).unwrap();
322
323 assert_eq!(object.data.service_id, "ISPAPI EPP Server");
324 assert_eq!(
325 object.data.service_date,
326 Utc.with_ymd_and_hms(2021, 7, 25, 14, 51, 17).unwrap()
327 );
328 assert_eq!(object.data.svc_menu.options.version, "1.0".into());
329 assert_eq!(object.data.svc_menu.options.lang, "en".into());
330 assert_eq!(object.data.svc_menu.services.obj_uris.len(), 4);
331 assert_eq!(
332 object
333 .data
334 .svc_menu
335 .services
336 .svc_ext
337 .unwrap()
338 .ext_uris
339 .unwrap()
340 .len(),
341 5
342 );
343 assert_eq!(object.data.dcp.statement.len(), 2);
344 assert_eq!(
345 object.data.dcp.expiry.unwrap().ty,
346 ExpiryType::Relative(Relative {
347 relative: "P1M".into()
348 })
349 );
350 }
351}