epp_client/
hello.rs

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// Request
9
10#[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// Response
30
31/// Type for data within the <svcMenu> section of an EPP greeting
32#[derive(Debug, Eq, PartialEq)]
33pub struct ServiceMenu {
34    pub options: Options<'static>,
35    pub services: Services<'static>,
36}
37
38/// Simplified service menu type for deserialization to `ServiceMenu` type from EPP greeting XML
39#[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    /// Deserializes the <svcMenu> data to the `ServiceMenu` type
51    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/// Type corresponding to <all> in the EPP greeting XML
73#[derive(Deserialize, Debug, Eq, PartialEq)]
74pub struct All;
75
76/// Type corresponding to <none> in the EPP greeting XML
77#[derive(Deserialize, Debug, Eq, PartialEq)]
78pub struct NoAccess;
79
80/// Type corresponding to <null> in the EPP greeting XML
81#[derive(Deserialize, Debug, Eq, PartialEq)]
82pub struct Null;
83
84/// Type corresponding to <personal> in the EPP greeting XML
85#[derive(Deserialize, Debug, Eq, PartialEq)]
86pub struct Personal;
87
88/// Type corresponding to <personalAndOther> in the EPP greeting XML
89#[derive(Deserialize, Debug, Eq, PartialEq)]
90pub struct PersonalAndOther;
91
92/// Type corresponding to <other> in the EPP greeting XML
93#[derive(Deserialize, Debug, Eq, PartialEq)]
94pub struct Other;
95
96/// Type corresponding to possible <retention> type values
97#[derive(Deserialize, Debug, Eq, PartialEq)]
98pub enum AccessType {
99    /// Data for the <all> tag
100    #[serde(rename = "all")]
101    All(All),
102    /// Data for the <none> tag
103    #[serde(rename = "none")]
104    NoAccess(NoAccess),
105    /// Data for the <null> tag
106    #[serde(rename = "null")]
107    Null(Null),
108    /// Data for the <personal> tag
109    #[serde(rename = "personal")]
110    Personal(Personal),
111    /// Data for the <personalAndOther> tag
112    #[serde(rename = "personalAndOther")]
113    PersonalAndOther(PersonalAndOther),
114    /// Data for the <other> tag
115    #[serde(rename = "other")]
116    Other(Other),
117}
118
119/// Type corresponding to <access> in the EPP greeting XML
120#[derive(Deserialize, Debug, Eq, PartialEq)]
121pub struct Access {
122    #[serde(flatten)]
123    pub ty: AccessType,
124}
125
126/// Type corresponding to possible <purpose> type values
127#[derive(Deserialize, Debug, Eq, PartialEq)]
128pub enum PurposeType {
129    /// Data for the <admin> tag
130    #[serde(rename = "admin")]
131    Admin,
132    /// Data for the <contact> tag
133    #[serde(rename = "contact")]
134    Contact,
135    /// Data for the <prov> tag
136    #[serde(rename = "prov")]
137    Prov,
138    /// Data for the <other> tag
139    #[serde(rename = "other")]
140    OtherPurpose,
141}
142
143/// Type corresponding to <purpose> in the EPP greeting XML
144#[derive(Deserialize, Debug, Eq, PartialEq)]
145pub struct Purpose {
146    #[serde(rename = "$value")]
147    pub purpose: Vec<PurposeType>,
148}
149
150/// Type corresponding to possible <purpose> type values
151#[derive(Deserialize, Debug, Eq, PartialEq)]
152pub enum RecipientType {
153    /// Data for the <other> tag
154    #[serde(rename = "other")]
155    Other,
156    /// Data for the <ours> tag
157    #[serde(rename = "ours")]
158    Ours,
159    /// Data for the <public> tag
160    #[serde(rename = "public")]
161    Public,
162    /// Data for the <same> tag
163    #[serde(rename = "same")]
164    Same,
165    /// Data for the <unrelated> tag
166    #[serde(rename = "unrelated")]
167    Unrelated,
168}
169
170/// Type corresponding to <recipeint> in the EPP greeting XML
171#[derive(Deserialize, Debug, Eq, PartialEq)]
172pub struct Recipient {
173    #[serde(rename = "$value")]
174    pub recipient: Vec<RecipientType>,
175}
176
177/// Type corresponding to <business> in the EPP greeting XML
178#[derive(Deserialize, Debug, Eq, PartialEq)]
179pub struct Business;
180
181/// Type corresponding to <indefinite> in the EPP greeting XML
182#[derive(Deserialize, Debug, Eq, PartialEq)]
183pub struct Indefinite;
184
185/// Type corresponding to <legal> in the EPP greeting XML
186#[derive(Deserialize, Debug, Eq, PartialEq)]
187pub struct Legal;
188
189/// Type corresponding to <none> in the EPP greeting XML
190#[derive(Deserialize, Debug, Eq, PartialEq)]
191pub struct No;
192
193/// Type corresponding to <stated> in the EPP greeting XML
194#[derive(Deserialize, Debug, Eq, PartialEq)]
195pub struct Stated;
196
197/// Type corresponding to possible <retention> type values
198#[derive(Deserialize, Debug, Eq, PartialEq)]
199pub enum RetentionType {
200    /// Data for the <business> tag
201    #[serde(rename = "business")]
202    Business(Business),
203    /// Data for the <indefinite> tag
204    #[serde(rename = "indefinite")]
205    Indefinite(Indefinite),
206    /// Data for the <legal> tag
207    #[serde(rename = "legal")]
208    Legal(Legal),
209    /// Data for the <none> tag
210    #[serde(rename = "none")]
211    No(No),
212    /// Data for the <stated> tag
213    #[serde(rename = "stated")]
214    Stated(Stated),
215}
216
217/// Type corresponding to <retention> in the EPP greeting XML
218#[derive(Deserialize, Debug, Eq, PartialEq)]
219pub struct Retention {
220    #[serde(flatten)]
221    pub ty: RetentionType,
222}
223
224/// Type corresponding to <statement> in the EPP greeting XML (pending more compliant implementation)
225#[derive(Deserialize, Debug, Eq, PartialEq)]
226pub struct Statement {
227    /// Data for the <purpose> tag
228    pub purpose: Purpose,
229    /// Data for the <recipient> tag
230    pub recipient: Recipient,
231    /// Data for the <retention> tag
232    pub retention: Retention,
233}
234
235/// Type corresponding to <absolute> value in the EPP greeting XML
236#[derive(Deserialize, Debug, Eq, PartialEq)]
237pub struct Absolute {
238    #[serde(rename = "$value")]
239    pub absolute: StringValue<'static>,
240}
241
242/// Type corresponding to <relative> value in the EPP greeting XML
243#[derive(Deserialize, Debug, Eq, PartialEq)]
244pub struct Relative {
245    #[serde(rename = "$value")]
246    pub relative: StringValue<'static>,
247}
248
249/// Type corresponding to possible <expiry> type values
250#[derive(Deserialize, Debug, Eq, PartialEq)]
251pub enum ExpiryType {
252    /// Data for the <absolute> tag
253    #[serde(rename = "absolute")]
254    Absolute(Absolute),
255    /// Data for the <relative> tag
256    #[serde(rename = "relative")]
257    Relative(Relative),
258}
259
260/// Type corresponding to <expiry> in the EPP greeting XML
261#[derive(Deserialize, Debug, Eq, PartialEq)]
262pub struct Expiry {
263    #[serde(flatten)]
264    pub ty: ExpiryType,
265}
266
267/// Type corresponding to <dcp> in the EPP greeting XML
268#[derive(Deserialize, Debug, Eq, PartialEq)]
269pub struct Dcp {
270    /// Data for the <access> tag
271    pub access: Access,
272    /// Data for the <statement> tags
273    pub statement: Vec<Statement>,
274    /// Data for the <expiry> tag
275    pub expiry: Option<Expiry>,
276}
277
278#[derive(Deserialize, Debug, Eq, PartialEq)]
279#[serde(rename_all = "lowercase")]
280/// Type corresponding to the <greeting> tag in the EPP greeting XML
281pub struct Greeting {
282    /// The service ID
283    #[serde(rename = "svID")]
284    pub service_id: String,
285    /// The date from the EPP server
286    #[serde(rename = "svDate")]
287    pub service_date: DateTime<Utc>,
288    /// Data under the <svcMenu> element
289    #[serde(rename = "svcMenu")]
290    pub svc_menu: ServiceMenu,
291    /// Data under the <dcp> element
292    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}