instant_epp/extensions/
consolidate.rs1use 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
37impl 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 pub fn new(expiration: GMonthDay) -> Self {
74 Self {
75 exp: expiration.to_string(),
76 }
77 }
78}
79
80impl<'a> UpdateWithNameStore<'a> {
81 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#[derive(Debug, ToXml)]
99#[xml(rename = "update", ns(XMLNS))]
100pub struct Update {
101 #[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}