1use std::fmt;
6
7use serde::{
8 ser::{SerializeMap, Serializer},
9 Deserialize, Serialize,
10};
11
12pub struct ApiEmptyString;
14
15impl Serialize for ApiEmptyString {
16 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
17 serializer.serialize_str("")
18 }
19}
20
21pub struct ApiEmptyObject;
23
24impl Serialize for ApiEmptyObject {
25 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
26 serializer.serialize_map(Some(0))?.end()
27 }
28}
29
30#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
31pub struct ApiProblem {
32 #[serde(rename = "type")]
33 pub _type: String,
34
35 #[serde(skip_serializing_if = "Option::is_none")]
36 pub detail: Option<String>,
37
38 #[serde(skip_serializing_if = "Option::is_none")]
39 pub subproblems: Option<Vec<ApiSubproblem>>,
40}
41
42impl ApiProblem {
43 pub fn is_bad_nonce(&self) -> bool {
44 self._type == "badNonce"
45 }
46
47 pub fn is_jwt_verification_error(&self) -> bool {
48 (self._type == "urn:acme:error:malformed"
49 || self._type == "urn:ietf:params:acme:error:malformed")
50 && self
51 .detail
52 .as_ref()
53 .map(|s| s == "JWS verification error")
54 .unwrap_or(false)
55 }
56}
57
58impl fmt::Display for ApiProblem {
59 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60 if let Some(detail) = &self.detail {
61 write!(f, "{}: {detail}", self._type)
62 } else {
63 write!(f, "{}", self._type)
64 }
65 }
66}
67
68#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
69pub struct ApiSubproblem {
70 #[serde(rename = "type")]
71 pub _type: String,
72 pub detail: Option<String>,
73 pub identifier: Option<ApiIdentifier>,
74}
75
76#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
93#[serde(rename_all = "camelCase")]
94pub struct ApiDirectory {
95 pub new_nonce: String,
96 pub new_account: String,
97 pub new_order: String,
98
99 #[serde(skip_serializing_if = "Option::is_none")]
100 pub new_authz: Option<String>,
101
102 pub revoke_cert: String,
103 pub key_change: String,
104
105 #[serde(skip_serializing_if = "Option::is_none")]
106 pub meta: Option<ApiDirectoryMeta>,
107}
108
109#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
111#[serde(rename_all = "camelCase")]
112pub struct ApiDirectoryMeta {
113 #[serde(skip_serializing_if = "Option::is_none")]
114 pub terms_of_service: Option<String>,
115
116 #[serde(skip_serializing_if = "Option::is_none")]
117 pub website: Option<String>,
118
119 #[serde(skip_serializing_if = "Option::is_none")]
120 pub caa_identities: Option<Vec<String>>,
121
122 #[serde(skip_serializing_if = "Option::is_none")]
123 pub external_account_required: Option<bool>,
124}
125
126impl ApiDirectoryMeta {
127 pub fn external_account_required(&self) -> bool {
128 self.external_account_required.unwrap_or(false)
129 }
130}
131
132#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
144#[serde(rename_all = "camelCase")]
145pub struct ApiAccount {
146 #[serde(skip_serializing_if = "Option::is_none")]
147 pub status: Option<String>,
148
149 #[serde(skip_serializing_if = "Option::is_none")]
150 pub contact: Option<Vec<String>>,
151
152 #[serde(skip_serializing_if = "Option::is_none")]
153 pub external_account_binding: Option<String>,
154
155 #[serde(skip_serializing_if = "Option::is_none")]
156 pub terms_of_service_agreed: Option<bool>,
157
158 #[serde(skip_serializing_if = "Option::is_none")]
159 pub only_return_existing: Option<bool>,
160
161 #[serde(skip_serializing_if = "Option::is_none")]
162 pub orders: Option<String>,
163}
164
165impl ApiAccount {
166 pub fn is_status_valid(&self) -> bool {
167 self.status.as_ref().map(|s| s.as_ref()) == Some("valid")
168 }
169
170 pub fn is_status_deactivated(&self) -> bool {
171 self.status.as_ref().map(|s| s.as_ref()) == Some("deactivated")
172 }
173
174 pub fn is_status_revoked(&self) -> bool {
175 self.status.as_ref().map(|s| s.as_ref()) == Some("revoked")
176 }
177
178 pub fn terms_of_service_agreed(&self) -> bool {
179 self.terms_of_service_agreed.unwrap_or(false)
180 }
181}
182
183#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
199#[serde(rename_all = "camelCase")]
200pub struct ApiOrder {
201 #[serde(skip_serializing_if = "Option::is_none")]
202 pub status: Option<String>,
203
204 #[serde(skip_serializing_if = "Option::is_none")]
205 pub expires: Option<String>,
206
207 pub identifiers: Vec<ApiIdentifier>,
208 pub not_before: Option<String>,
209 pub not_after: Option<String>,
210 pub error: Option<ApiProblem>,
211 pub authorizations: Option<Vec<String>>,
212 pub finalize: String,
213 pub certificate: Option<String>,
214}
215
216impl ApiOrder {
217 pub fn is_status_pending(&self) -> bool {
219 self.status.as_ref().map(|s| s.as_ref()) == Some("pending")
220 }
221
222 pub fn is_status_ready(&self) -> bool {
224 self.status.as_ref().map(|s| s.as_ref()) == Some("ready")
225 }
226
227 pub fn is_status_processing(&self) -> bool {
229 self.status.as_ref().map(|s| s.as_ref()) == Some("processing")
230 }
231
232 pub fn is_status_valid(&self) -> bool {
234 self.status.as_ref().map(|s| s.as_ref()) == Some("valid")
235 }
236
237 pub fn is_status_invalid(&self) -> bool {
239 self.status.as_ref().map(|s| s.as_ref()) == Some("invalid")
240 }
241
242 pub fn domains(&self) -> Vec<&str> {
244 self.identifiers.iter().map(|i| i.value.as_ref()).collect()
245 }
246}
247
248#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
249pub struct ApiIdentifier {
250 #[serde(rename = "type")]
251 pub _type: String,
252 pub value: String,
253}
254
255impl ApiIdentifier {
256 pub fn is_type_dns(&self) -> bool {
257 self._type == "dns"
258 }
259}
260
261#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
306pub struct ApiAuth {
307 pub identifier: ApiIdentifier,
308 pub status: Option<String>,
309 pub expires: Option<String>,
310 pub challenges: Vec<ApiChallenge>,
311
312 pub wildcard: Option<bool>,
316}
317
318impl ApiAuth {
319 pub fn is_status_pending(&self) -> bool {
320 self.status.as_ref().map(|s| s.as_ref()) == Some("pending")
321 }
322
323 pub fn is_status_valid(&self) -> bool {
324 self.status.as_ref().map(|s| s.as_ref()) == Some("valid")
325 }
326
327 pub fn is_status_invalid(&self) -> bool {
328 self.status.as_ref().map(|s| s.as_ref()) == Some("invalid")
329 }
330
331 pub fn is_status_deactivated(&self) -> bool {
332 self.status.as_ref().map(|s| s.as_ref()) == Some("deactivated")
333 }
334
335 pub fn is_status_expired(&self) -> bool {
336 self.status.as_ref().map(|s| s.as_ref()) == Some("expired")
337 }
338
339 pub fn is_status_revoked(&self) -> bool {
340 self.status.as_ref().map(|s| s.as_ref()) == Some("revoked")
341 }
342
343 pub fn wildcard(&self) -> bool {
344 self.wildcard.unwrap_or(false)
345 }
346
347 pub fn http_challenge(&self) -> Option<&ApiChallenge> {
348 self.challenges.iter().find(|c| c._type == "http-01")
349 }
350
351 pub fn dns_challenge(&self) -> Option<&ApiChallenge> {
352 self.challenges.iter().find(|c| c._type == "dns-01")
353 }
354
355 pub fn tls_alpn_challenge(&self) -> Option<&ApiChallenge> {
356 self.challenges.iter().find(|c| c._type == "tls-alpn-01")
357 }
358}
359
360#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
361pub struct ApiChallenge {
362 pub url: String,
363 #[serde(rename = "type")]
364 pub _type: String,
365 pub status: String,
366 pub token: String,
367 pub validated: Option<String>,
368 pub error: Option<ApiProblem>,
369}
370
371impl ApiChallenge {
378 pub fn is_status_pending(&self) -> bool {
379 &self.status == "pending"
380 }
381
382 pub fn is_status_processing(&self) -> bool {
383 &self.status == "processing"
384 }
385
386 pub fn is_status_valid(&self) -> bool {
387 &self.status == "valid"
388 }
389
390 pub fn is_status_invalid(&self) -> bool {
391 &self.status == "invalid"
392 }
393}
394
395#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
396pub struct ApiFinalize {
397 pub csr: String,
398}
399
400#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
401pub struct ApiRevocation {
402 pub certificate: String,
403 pub reason: usize,
404}
405
406#[cfg(test)]
407mod tests {
408 use super::*;
409
410 #[test]
411 fn test_api_empty_string() {
412 let x = serde_json::to_string(&ApiEmptyString).unwrap();
413 assert_eq!("\"\"", x);
414 }
415
416 #[test]
417 fn test_api_empty_object() {
418 let x = serde_json::to_string(&ApiEmptyObject).unwrap();
419 assert_eq!("{}", x);
420 }
421}