instant_epp/
hello.rs

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// Request
9
10#[derive(Debug, PartialEq, ToXml)]
11#[xml(rename = "hello", ns(EPP_XMLNS))]
12pub(crate) struct Hello;
13
14// Response
15
16/// Type for data within the `<svcMenu>` section of an EPP greeting
17#[derive(Debug, Eq, PartialEq)]
18pub struct ServiceMenu {
19    pub options: Options<'static>,
20    pub services: Services<'static>,
21}
22
23/// Simplified service menu type for deserialization to `ServiceMenu` type from EPP greeting XML
24#[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    /// Deserializes the `<svcMenu>` data to the `ServiceMenu` type
41    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/// Type corresponding to `<all>` in the EPP greeting XML
72#[derive(Debug, Eq, FromXml, PartialEq)]
73#[xml(rename = "all", ns(EPP_XMLNS))]
74pub struct All;
75
76/// Type corresponding to `<none>` in the EPP greeting XML
77#[derive(Debug, Eq, FromXml, PartialEq)]
78#[xml(rename = "noAccess", ns(EPP_XMLNS))]
79pub struct NoAccess;
80
81/// Type corresponding to `<null>` in the EPP greeting XML
82#[derive(Debug, Eq, FromXml, PartialEq)]
83#[xml(rename = "null", ns(EPP_XMLNS))]
84pub struct Null;
85
86/// Type corresponding to `<personal>` in the EPP greeting XML
87#[derive(Debug, Eq, FromXml, PartialEq)]
88#[xml(rename = "personal", ns(EPP_XMLNS))]
89pub struct Personal;
90
91/// Type corresponding to `<personalAndOther>` in the EPP greeting XML
92#[derive(Debug, Eq, FromXml, PartialEq)]
93#[xml(rename = "personalAndOther", ns(EPP_XMLNS))]
94pub struct PersonalAndOther;
95
96/// Type corresponding to `<other>` in the EPP greeting XML
97#[derive(Debug, Eq, FromXml, PartialEq)]
98#[xml(rename = "other", ns(EPP_XMLNS))]
99pub struct Other;
100
101/// Type corresponding to possible `<retention>` type values
102#[derive(Debug, Eq, FromXml, PartialEq)]
103#[xml(forward)]
104pub enum AccessType {
105    /// Data for the `<all>` tag
106    All(All),
107    /// Data for the `<none>` tag
108    NoAccess(NoAccess),
109    /// Data for the `<null>` tag
110    Null(Null),
111    /// Data for the `<personal>` tag
112    Personal(Personal),
113    /// Data for the `<personalAndOther>` tag
114    PersonalAndOther(PersonalAndOther),
115    /// Data for the `<other>` tag
116    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/// Type corresponding to possible `<purpose>` type values
126#[derive(Debug, Eq, FromXml, PartialEq)]
127#[xml(forward)]
128pub enum PurposeType {
129    /// Data for the `<admin>` tag
130    Admin(Admin),
131    /// Data for the `<contact>` tag
132    Contact(Contact),
133    /// Data for the `<prov>` tag
134    Prov(Prov),
135    /// Data for the `<other>` tag
136    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/// Type corresponding to `<purpose>` in the EPP greeting XML
156#[derive(Debug, Eq, FromXml, PartialEq)]
157#[xml(rename = "purpose", ns(EPP_XMLNS))]
158pub struct Purpose {
159    pub purpose: Vec<PurposeType>,
160}
161
162/// Type corresponding to possible `<purpose>` type values
163#[derive(Debug, Eq, FromXml, PartialEq)]
164#[xml(forward)]
165pub enum RecipientType {
166    /// Data for the `<other>` tag
167    Other(Other),
168    /// Data for the `<ours>` tag
169    Ours(Ours),
170    /// Data for the `<public>` tag
171    Public(Public),
172    /// Data for the `<same>` tag
173    Same(Same),
174    /// Data for the `<unrelated>` tag
175    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/// Type corresponding to `<recipeint>` in the EPP greeting XML
195#[derive(Debug, Eq, FromXml, PartialEq)]
196#[xml(rename = "recipient", ns(EPP_XMLNS))]
197pub struct Recipient {
198    pub recipient: Vec<RecipientType>,
199}
200
201/// Type corresponding to `<business>` in the EPP greeting XML
202#[derive(Debug, Eq, FromXml, PartialEq)]
203#[xml(rename = "business", ns(EPP_XMLNS))]
204pub struct Business;
205
206/// Type corresponding to `<indefinite>` in the EPP greeting XML
207#[derive(Debug, Eq, FromXml, PartialEq)]
208#[xml(rename = "indefinite", ns(EPP_XMLNS))]
209pub struct Indefinite;
210
211/// Type corresponding to `<legal>` in the EPP greeting XML
212#[derive(Debug, Eq, FromXml, PartialEq)]
213#[xml(rename = "legal", ns(EPP_XMLNS))]
214pub struct Legal;
215
216/// Type corresponding to `<none>` in the EPP greeting XML
217#[derive(Debug, Eq, FromXml, PartialEq)]
218#[xml(rename = "none", ns(EPP_XMLNS))]
219pub struct No;
220
221/// Type corresponding to `<stated>` in the EPP greeting XML
222#[derive(Debug, Eq, FromXml, PartialEq)]
223#[xml(rename = "stated", ns(EPP_XMLNS))]
224pub struct Stated;
225
226/// Type corresponding to possible `<retention>` type values
227#[derive(Debug, Eq, FromXml, PartialEq)]
228#[xml(forward, rename = "retention", ns(EPP_XMLNS))]
229pub enum RetentionType {
230    /// Data for the `<business>` tag
231    Business(Business),
232    /// Data for the `<indefinite>` tag
233    Indefinite(Indefinite),
234    /// Data for the `<legal>` tag
235    Legal(Legal),
236    /// Data for the `<none>` tag
237    None(No),
238    /// Data for the `<stated>` tag
239    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/// Type corresponding to `<statement>` in the EPP greeting XML (pending more compliant implementation)
249#[derive(Debug, Eq, FromXml, PartialEq)]
250#[xml(rename = "statement", ns(EPP_XMLNS))]
251pub struct Statement {
252    /// Data for the `<purpose>` tag
253    pub purpose: Purpose,
254    /// Data for the `<recipient>` tag
255    pub recipient: Recipient,
256    /// Data for the `<retention>` tag
257    pub retention: Retention,
258}
259
260/// Type corresponding to `<absolute>` value in the EPP greeting XML
261#[derive(Debug, Eq, FromXml, PartialEq)]
262#[xml(rename = "absolute", ns(EPP_XMLNS))]
263pub struct Absolute(String);
264
265/// Type corresponding to `<relative>` value in the EPP greeting XML
266#[derive(Debug, Eq, FromXml, PartialEq)]
267#[xml(rename = "relative", ns(EPP_XMLNS))]
268pub struct Relative(String);
269
270/// Type corresponding to possible `<expiry>` type values
271#[derive(Debug, Eq, FromXml, PartialEq)]
272#[xml(forward)]
273pub enum ExpiryType {
274    /// Data for the `<absolute>` tag
275    Absolute(Absolute),
276    /// Data for the `<relative>` tag
277    Relative(Relative),
278}
279
280/// Type corresponding to possible `<expiry>` type values
281#[derive(Debug, Eq, FromXml, PartialEq)]
282#[xml(rename = "expiry", ns(EPP_XMLNS))]
283pub struct Expiry {
284    inner: ExpiryType,
285}
286
287/// Type corresponding to `<dcp>` in the EPP greeting XML
288#[derive(Debug, Eq, FromXml, PartialEq)]
289#[xml(rename = "dcp", ns(EPP_XMLNS))]
290pub struct Dcp {
291    /// Data for the `<access>` tag
292    pub access: Access,
293    /// Data for the `<statement>` tags
294    pub statement: Vec<Statement>,
295    /// Data for the `<expiry>` tag
296    pub expiry: Option<Expiry>,
297}
298
299/// Type corresponding to the `<greeting>` tag in the EPP greeting XML
300#[derive(Debug, Eq, FromXml, PartialEq)]
301#[xml(ns(EPP_XMLNS), rename = "greeting", rename_all = "lowercase")]
302pub struct Greeting {
303    /// The service ID
304    #[xml(rename = "svID")]
305    pub service_id: String,
306    /// The date from the EPP server
307    #[xml(rename = "svDate")]
308    pub service_date: DateTime<Utc>,
309    /// Data under the `<svcMenu>` element
310    pub svc_menu: ServiceMenu,
311    /// Data under the `<dcp>` element
312    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}