instant_epp/extensions/
consolidate.rs

1//! Types for EPP consolidate request
2//!
3//! As described in [ConsoliDate mapping](https://www.verisign.com/assets/consolidate-mapping.txt)
4
5use std::fmt;
6
7use chrono::FixedOffset;
8use instant_xml::ToXml;
9
10use crate::common::NoExtension;
11use crate::domain::update::DomainUpdate;
12use crate::request::{Extension, Transaction};
13
14use super::namestore::NameStore;
15
16pub const XMLNS: &str = "http://www.verisign.com/epp/sync-1.0";
17
18impl Transaction<Update> for DomainUpdate<'_> {}
19
20impl Extension for Update {
21    type Response = NoExtension;
22}
23
24impl Transaction<UpdateWithNameStore<'_>> for DomainUpdate<'_> {}
25
26impl Extension for UpdateWithNameStore<'_> {
27    type Response = NameStore<'static>;
28}
29
30#[derive(PartialEq, Eq, Debug)]
31pub struct GMonthDay {
32    pub month: u8,
33    pub day: u8,
34    pub timezone: Option<FixedOffset>,
35}
36
37// Taken from https://github.com/lumeohq/xsd-parser-rs/blob/main/xsd-types/src/types/gmonthday.rs
38/// Represents a gMonthDay type <https://www.w3.org/TR/xmlschema-2/#gMonthDay>
39impl GMonthDay {
40    pub fn new(month: u8, day: u8, timezone: Option<FixedOffset>) -> Result<Self, String> {
41        if !(1..=12).contains(&month) {
42            return Err("Month value within GMonthDay should lie between 1 and 12".to_string());
43        }
44
45        if !(1..=31).contains(&day) {
46            return Err("Day value within GMonthDay should lie between 1 and 31".to_string());
47        }
48
49        const MONTH_MAX_LEN: [u8; 12] = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
50        if day > MONTH_MAX_LEN[month as usize - 1] {
51            return Err("Day value within GMonthDay is to big for specified month".to_string());
52        }
53
54        Ok(Self {
55            month,
56            day,
57            timezone,
58        })
59    }
60}
61
62impl fmt::Display for GMonthDay {
63    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
64        match self.timezone {
65            Some(tz) => write!(f, "--{:02}-{:02}{}", self.month, self.day, tz),
66            None => write!(f, "--{:02}-{:02}", self.month, self.day),
67        }
68    }
69}
70
71impl Update {
72    /// Create a new sync update request
73    pub fn new(expiration: GMonthDay) -> Self {
74        Self {
75            exp: expiration.to_string(),
76        }
77    }
78}
79
80impl<'a> UpdateWithNameStore<'a> {
81    /// Create a new sync update with namestore request
82    pub fn new(expiration: GMonthDay, subproduct: &'a str) -> Self {
83        Self {
84            sync: Update::new(expiration),
85            namestore: NameStore::new(subproduct),
86        }
87    }
88}
89
90#[derive(Debug, ToXml)]
91#[xml(transparent)]
92pub struct UpdateWithNameStore<'a> {
93    pub sync: Update,
94    pub namestore: NameStore<'a>,
95}
96
97/// Type for EPP XML `<consolidate>` extension
98#[derive(Debug, ToXml)]
99#[xml(rename = "update", ns(XMLNS))]
100pub struct Update {
101    /// The expiry date of the domain
102    #[xml(rename = "expMonthDay")]
103    pub exp: String,
104}
105
106#[cfg(test)]
107mod tests {
108    use super::{GMonthDay, Update};
109    use crate::domain::update::{DomainChangeInfo, DomainUpdate};
110    use crate::extensions::consolidate::UpdateWithNameStore;
111    use crate::tests::assert_serialized;
112
113    #[test]
114    fn command() {
115        let exp = GMonthDay::new(5, 31, None).unwrap();
116
117        let consolidate_ext = Update::new(exp);
118
119        let mut object = DomainUpdate::new("eppdev.com");
120
121        object.info(DomainChangeInfo {
122            registrant: None,
123            auth_info: None,
124        });
125
126        assert_serialized(
127            "request/extensions/consolidate.xml",
128            (&object, &consolidate_ext),
129        );
130    }
131
132    #[test]
133    fn command_with_namestore() {
134        let exp = GMonthDay::new(5, 31, None).unwrap();
135
136        let consolidate_ext = UpdateWithNameStore::new(exp, "com");
137
138        let mut object = DomainUpdate::new("eppdev.com");
139
140        object.info(DomainChangeInfo {
141            registrant: None,
142            auth_info: None,
143        });
144
145        assert_serialized(
146            "request/extensions/consolidate_namestore.xml",
147            (&object, &consolidate_ext),
148        );
149    }
150}