1use std::fmt::Debug;
2
3use chrono::{DateTime, Utc};
4use instant_xml::{Deserializer, FromXml, ToXml};
5
6use crate::common::{Options, ServiceExtension, Services, EPP_XMLNS};
7
8#[derive(Debug, PartialEq, ToXml)]
11#[xml(rename = "hello", ns(EPP_XMLNS))]
12pub(crate) struct Hello;
13
14#[derive(Debug, Eq, PartialEq)]
18pub struct ServiceMenu {
19 pub options: Options<'static>,
20 pub services: Services<'static>,
21}
22
23#[derive(Debug, FromXml, PartialEq)]
25#[xml(ns(EPP_XMLNS), rename = "svcMenu")]
26struct FlattenedServiceMenu {
27 version: String,
28 lang: String,
29 #[xml(rename = "objURI")]
30 obj_uris: Vec<String>,
31 #[xml(rename = "svcExtension")]
32 svc_ext: Option<ServiceExtension<'static>>,
33}
34
35impl<'xml> FromXml<'xml> for ServiceMenu {
36 fn matches(id: instant_xml::Id<'_>, field: Option<instant_xml::Id<'_>>) -> bool {
37 FlattenedServiceMenu::matches(id, field)
38 }
39
40 fn deserialize<'cx>(
42 into: &mut Self::Accumulator,
43 field: &'static str,
44 deserializer: &mut Deserializer<'cx, 'xml>,
45 ) -> Result<(), instant_xml::Error> {
46 let mut value = None;
47 FlattenedServiceMenu::deserialize(&mut value, field, deserializer)?;
48 let flattened = match value {
49 Some(value) => value,
50 None => return Ok(()),
51 };
52
53 *into = Some(Self {
54 options: Options {
55 version: flattened.version.into(),
56 lang: flattened.lang.into(),
57 },
58 services: Services {
59 obj_uris: flattened.obj_uris.into_iter().map(|s| s.into()).collect(),
60 svc_ext: flattened.svc_ext,
61 },
62 });
63
64 Ok(())
65 }
66
67 type Accumulator = Option<Self>;
68 const KIND: instant_xml::Kind = FlattenedServiceMenu::KIND;
69}
70
71#[derive(Debug, Eq, FromXml, PartialEq)]
73#[xml(rename = "all", ns(EPP_XMLNS))]
74pub struct All;
75
76#[derive(Debug, Eq, FromXml, PartialEq)]
78#[xml(rename = "noAccess", ns(EPP_XMLNS))]
79pub struct NoAccess;
80
81#[derive(Debug, Eq, FromXml, PartialEq)]
83#[xml(rename = "null", ns(EPP_XMLNS))]
84pub struct Null;
85
86#[derive(Debug, Eq, FromXml, PartialEq)]
88#[xml(rename = "personal", ns(EPP_XMLNS))]
89pub struct Personal;
90
91#[derive(Debug, Eq, FromXml, PartialEq)]
93#[xml(rename = "personalAndOther", ns(EPP_XMLNS))]
94pub struct PersonalAndOther;
95
96#[derive(Debug, Eq, FromXml, PartialEq)]
98#[xml(rename = "other", ns(EPP_XMLNS))]
99pub struct Other;
100
101#[derive(Debug, Eq, FromXml, PartialEq)]
103#[xml(forward)]
104pub enum AccessType {
105 All(All),
107 NoAccess(NoAccess),
109 Null(Null),
111 Personal(Personal),
113 PersonalAndOther(PersonalAndOther),
115 Other(Other),
117}
118
119#[derive(Debug, Eq, FromXml, PartialEq)]
120#[xml(rename = "access", ns(EPP_XMLNS))]
121pub struct Access {
122 inner: AccessType,
123}
124
125#[derive(Debug, Eq, FromXml, PartialEq)]
127#[xml(forward)]
128pub enum PurposeType {
129 Admin(Admin),
131 Contact(Contact),
133 Prov(Prov),
135 OtherPurpose(OtherPurpose),
137}
138
139#[derive(Debug, Eq, FromXml, PartialEq)]
140#[xml(rename = "admin", ns(EPP_XMLNS))]
141pub struct Admin;
142
143#[derive(Debug, Eq, FromXml, PartialEq)]
144#[xml(rename = "contact", ns(EPP_XMLNS))]
145pub struct Contact;
146
147#[derive(Debug, Eq, FromXml, PartialEq)]
148#[xml(rename = "prov", ns(EPP_XMLNS))]
149pub struct Prov;
150
151#[derive(Debug, Eq, FromXml, PartialEq)]
152#[xml(rename = "otherPurpose", ns(EPP_XMLNS))]
153pub struct OtherPurpose;
154
155#[derive(Debug, Eq, FromXml, PartialEq)]
157#[xml(rename = "purpose", ns(EPP_XMLNS))]
158pub struct Purpose {
159 pub purpose: Vec<PurposeType>,
160}
161
162#[derive(Debug, Eq, FromXml, PartialEq)]
164#[xml(forward)]
165pub enum RecipientType {
166 Other(Other),
168 Ours(Ours),
170 Public(Public),
172 Same(Same),
174 Unrelated(Unrelated),
176}
177
178#[derive(Debug, Eq, FromXml, PartialEq)]
179#[xml(rename = "ours", ns(EPP_XMLNS))]
180pub struct Ours;
181
182#[derive(Debug, Eq, FromXml, PartialEq)]
183#[xml(rename = "public", ns(EPP_XMLNS))]
184pub struct Public;
185
186#[derive(Debug, Eq, FromXml, PartialEq)]
187#[xml(rename = "unrelated", ns(EPP_XMLNS))]
188pub struct Unrelated;
189
190#[derive(Debug, Eq, FromXml, PartialEq)]
191#[xml(rename = "same", ns(EPP_XMLNS))]
192pub struct Same;
193
194#[derive(Debug, Eq, FromXml, PartialEq)]
196#[xml(rename = "recipient", ns(EPP_XMLNS))]
197pub struct Recipient {
198 pub recipient: Vec<RecipientType>,
199}
200
201#[derive(Debug, Eq, FromXml, PartialEq)]
203#[xml(rename = "business", ns(EPP_XMLNS))]
204pub struct Business;
205
206#[derive(Debug, Eq, FromXml, PartialEq)]
208#[xml(rename = "indefinite", ns(EPP_XMLNS))]
209pub struct Indefinite;
210
211#[derive(Debug, Eq, FromXml, PartialEq)]
213#[xml(rename = "legal", ns(EPP_XMLNS))]
214pub struct Legal;
215
216#[derive(Debug, Eq, FromXml, PartialEq)]
218#[xml(rename = "none", ns(EPP_XMLNS))]
219pub struct No;
220
221#[derive(Debug, Eq, FromXml, PartialEq)]
223#[xml(rename = "stated", ns(EPP_XMLNS))]
224pub struct Stated;
225
226#[derive(Debug, Eq, FromXml, PartialEq)]
228#[xml(forward, rename = "retention", ns(EPP_XMLNS))]
229pub enum RetentionType {
230 Business(Business),
232 Indefinite(Indefinite),
234 Legal(Legal),
236 None(No),
238 Stated(Stated),
240}
241
242#[derive(Debug, Eq, FromXml, PartialEq)]
243#[xml(rename = "retention", ns(EPP_XMLNS))]
244pub struct Retention {
245 inner: RetentionType,
246}
247
248#[derive(Debug, Eq, FromXml, PartialEq)]
250#[xml(rename = "statement", ns(EPP_XMLNS))]
251pub struct Statement {
252 pub purpose: Purpose,
254 pub recipient: Recipient,
256 pub retention: Retention,
258}
259
260#[derive(Debug, Eq, FromXml, PartialEq)]
262#[xml(rename = "absolute", ns(EPP_XMLNS))]
263pub struct Absolute(String);
264
265#[derive(Debug, Eq, FromXml, PartialEq)]
267#[xml(rename = "relative", ns(EPP_XMLNS))]
268pub struct Relative(String);
269
270#[derive(Debug, Eq, FromXml, PartialEq)]
272#[xml(forward)]
273pub enum ExpiryType {
274 Absolute(Absolute),
276 Relative(Relative),
278}
279
280#[derive(Debug, Eq, FromXml, PartialEq)]
282#[xml(rename = "expiry", ns(EPP_XMLNS))]
283pub struct Expiry {
284 inner: ExpiryType,
285}
286
287#[derive(Debug, Eq, FromXml, PartialEq)]
289#[xml(rename = "dcp", ns(EPP_XMLNS))]
290pub struct Dcp {
291 pub access: Access,
293 pub statement: Vec<Statement>,
295 pub expiry: Option<Expiry>,
297}
298
299#[derive(Debug, Eq, FromXml, PartialEq)]
301#[xml(ns(EPP_XMLNS), rename = "greeting", rename_all = "lowercase")]
302pub struct Greeting {
303 #[xml(rename = "svID")]
305 pub service_id: String,
306 #[xml(rename = "svDate")]
308 pub service_date: DateTime<Utc>,
309 pub svc_menu: ServiceMenu,
311 pub dcp: Dcp,
313}
314
315#[cfg(test)]
316mod tests {
317 use chrono::{TimeZone, Utc};
318
319 use super::{ExpiryType, Greeting, Hello, Relative};
320 use crate::tests::get_xml;
321 use crate::xml;
322
323 #[test]
324 fn hello() {
325 let xml = get_xml("request/hello.xml").unwrap();
326 let serialized = xml::serialize(Hello).unwrap();
327
328 assert_eq!(xml, serialized);
329 }
330
331 #[test]
332 fn greeting() {
333 let xml = get_xml("response/greeting.xml").unwrap();
334 let object = xml::deserialize::<Greeting>(xml.as_str()).unwrap();
335
336 assert_eq!(object.service_id, "ISPAPI EPP Server");
337 assert_eq!(
338 object.service_date,
339 Utc.with_ymd_and_hms(2021, 7, 25, 14, 51, 17).unwrap()
340 );
341 assert_eq!(object.svc_menu.options.version, "1.0");
342 assert_eq!(object.svc_menu.options.lang, "en");
343 assert_eq!(object.svc_menu.services.obj_uris.len(), 4);
344 assert_eq!(object.svc_menu.services.svc_ext.unwrap().ext_uris.len(), 5);
345 assert_eq!(object.dcp.statement.len(), 2);
346 assert_eq!(
347 object.dcp.expiry.unwrap().inner,
348 ExpiryType::Relative(Relative("P1M".into()))
349 );
350 }
351}