1use std::{mem::size_of, time::Duration};
2
3use serde::{Serialize, Serializer};
4pub use transip::configuration_from_environment;
5use transip::Configuration;
6pub use transip::Error;
7use transip_command::{
8 DnsCommand, DomainCommand, EmailBoxCommand, EmailForwardCommand, InvoiceCommand, OnError,
9 ProductCommand, VpsCommand,
10};
11
12pub use transip_command::{ErrorKind, SubCommand, TransipCommand};
14
15pub struct Client {
16 inner: transip::Client,
17 onerror: transip_command::OnError,
18}
19
20impl Client {
21 pub fn exit_on_error(&self) -> bool {
22 self.onerror == OnError::Exit
23 }
24}
25
26trait Report {
27 fn report(self, s: impl Serializer) -> Result<(), transip::Error>;
28}
29
30impl<T: Serialize> Report for Result<T, transip::Error> {
31 fn report(self, s: impl Serializer) -> Result<(), transip::Error> {
32 self.map(|result| {
33 if size_of::<T>() > 0 {
34 result.serialize(s).unwrap();
35 }
36 })
37 }
38}
39
40impl TryFrom<Box<dyn Configuration>> for Client {
41 type Error = transip::Error;
42
43 fn try_from(configuration: Box<dyn Configuration>) -> Result<Self, Self::Error> {
44 transip::Client::try_from(configuration).map(|client| Client {
45 inner: client,
46 onerror: OnError::Print,
47 })
48 }
49}
50
51impl Client {
52 fn execute_dns(
53 &mut self,
54 command: &DnsCommand,
55 s: impl Serializer,
56 ) -> Result<(), transip::Error> {
57 use transip::api::dns::{DnsApi, DnsEntry};
58 match command {
59 DnsCommand::AcmeValidationDelete { domain } => self
60 .inner
61 .dns_entry_delete_all(domain, DnsEntry::is_acme_challenge)
62 .report(s),
63 DnsCommand::List { domain } => self.inner.dns_entry_list(domain).report(s),
64 DnsCommand::Delete(dns_entry) => {
65 let entry = DnsEntry {
66 name: dns_entry.name.clone(),
67 expire: dns_entry.ttl,
68 entry_type: format!("{:?}", dns_entry.r#type),
69 content: dns_entry.content.clone(),
70 };
71 self.inner
72 .dns_entry_delete(&dns_entry.domain, entry)
73 .report(s)
74 }
75 DnsCommand::Insert(dns_entry) => {
76 let entry = DnsEntry {
77 name: dns_entry.name.clone(),
78 expire: dns_entry.ttl,
79 entry_type: format!("{:?}", dns_entry.r#type),
80 content: dns_entry.content.clone(),
81 };
82 self.inner
83 .dns_entry_insert(&dns_entry.domain, entry)
84 .report(s)
85 }
86 DnsCommand::AcmeValidationSet { domain, challenge } => self
87 .inner
88 .dns_entry_delete_all(domain, DnsEntry::is_acme_challenge)
89 .and_then(|_| {
90 self.inner
91 .dns_entry_insert(domain, DnsEntry::new_acme_challenge(60, challenge))
92 })
93 .report(s),
94 #[cfg(feature = "propagation")]
95 DnsCommand::AcmeValidationCheck { domain, challenge } => {
96 acme_validation_propagation::wait(domain, challenge)
97 .map_err(|_| Error::AcmeChallege)
98 }
99 }
100 }
101
102 fn execute_domain(
103 &mut self,
104 command: &DomainCommand,
105 s: impl Serializer,
106 ) -> Result<(), transip::Error> {
107 use transip::api::domain::DomainApi;
108 match command {
109 DomainCommand::Item { domain } => self.inner.domain_item(domain).report(s),
110 DomainCommand::List => self.inner.domain_list().report(s),
111 }
112 }
113
114 fn execute_email_box(
115 &mut self,
116 command: &EmailBoxCommand,
117 s: impl Serializer,
118 ) -> Result<(), transip::Error> {
119 use transip::api::email::EmailApi;
120 match command {
121 EmailBoxCommand::Item { domain, id } => self.inner.mailbox_item(domain, id).report(s),
122 EmailBoxCommand::List { domain } => self.inner.mailbox_list(domain).report(s),
123 _ => Ok(()),
124 }
125 }
126
127 fn execute_email_forward(
128 &mut self,
129 command: &EmailForwardCommand,
130 s: impl Serializer,
131 ) -> Result<(), transip::Error> {
132 use transip::api::email::EmailApi;
133 match command {
134 EmailForwardCommand::Item { domain, id } => {
135 self.inner.mailforward_item(domain, id).report(s)
136 }
137 EmailForwardCommand::List { domain } => self.inner.mailforward_list(domain).report(s),
138 _ => Ok(()),
139 }
140 }
141
142 fn execute_invoice(
143 &mut self,
144 command: &InvoiceCommand,
145 s: impl Serializer,
146 ) -> Result<(), transip::Error> {
147 use transip::api::account::AccountApi;
148 match command {
149 InvoiceCommand::Item { number } => self.inner.invoice(number).report(s),
150 InvoiceCommand::Pdf { number } => self.inner.invoice_pdf(number).report(s),
151 InvoiceCommand::List => self.inner.invoice_list().report(s),
152 }
153 }
154
155 fn execute_product(
156 &mut self,
157 command: &ProductCommand,
158 s: impl Serializer,
159 ) -> Result<(), transip::Error> {
160 use transip::api::general::GeneralApi;
161 match command {
162 ProductCommand::Elements { name } => self.inner.product_elements(name).report(s),
163 ProductCommand::List => self.inner.products().report(s),
164 }
165 }
166
167 fn execute_vps(
168 &mut self,
169 command: &VpsCommand,
170 s: impl Serializer,
171 ) -> Result<(), transip::Error> {
172 use transip::api::vps::VpsApi;
173 match command {
174 VpsCommand::Item { name } => self.inner.vps(name).report(s),
175 VpsCommand::Lock { name } => self.inner.vps_set_is_locked(name, true).report(s),
176 VpsCommand::Reset { name } => self.inner.vps_reset(name).report(s),
177 VpsCommand::Start { name } => self.inner.vps_start(name).report(s),
178 VpsCommand::Stop { name } => self.inner.vps_stop(name).report(s),
179 VpsCommand::Unlock { name } => self.inner.vps_set_is_locked(name, false).report(s),
180 VpsCommand::List => self.inner.vps_list().report(s),
181 }
182 }
183
184 pub fn execute(
185 &mut self,
186 command: &SubCommand,
187 s: impl Serializer,
188 ) -> Result<(), transip::Error> {
189 use transip::api::general::GeneralApi;
190 match command {
191 SubCommand::AvailibilityZones => self.inner.availability_zones().report(s),
192 SubCommand::Comment { text: _ } => Ok(()),
193 SubCommand::Dns(command) => self.execute_dns(command, s),
194 SubCommand::Domain(command) => self.execute_domain(command, s),
195 SubCommand::EmailBox(command) => self.execute_email_box(command, s),
196 SubCommand::EmailForward(command) => self.execute_email_forward(command, s),
197 SubCommand::Invoice(command) => self.execute_invoice(command, s),
198 SubCommand::Onerror { on_error } => {
199 self.onerror = on_error.clone();
200 Ok(())
201 }
202 SubCommand::Ping => self.inner.api_test().report(s),
203 SubCommand::Product(command) => self.execute_product(command, s),
204 SubCommand::Sleep { number_of_seconds } => {
205 std::thread::sleep(Duration::from_secs(*number_of_seconds));
206 Ok(())
207 }
208 SubCommand::Vps(command) => self.execute_vps(command, s),
209 }
211 }
212}