1use serde::{Deserialize, Deserializer, Serialize};
4use std::collections::HashSet;
5use std::fmt;
6use std::io;
7use thiserror::Error;
8
9#[non_exhaustive]
13#[derive(Error, Debug)]
14pub enum Error {
15 #[error("{0:?}")]
17 Codes(HashSet<Code>),
18 #[error("{0}")]
20 Reqwest(#[from] reqwest::Error),
21 #[error("{0}")]
23 Io(#[from] io::Error),
24 #[error("{0}")]
26 Json(#[from] serde_json::Error),
27 #[error("{0}")]
29 UrlEncoded(#[from] serde_urlencoded::ser::Error),
30 #[error("{0}")]
32 Uuid(#[from] uuid::Error),
33 #[error("{0}")]
35 Url(#[from] url::ParseError),
36}
37
38#[non_exhaustive]
41#[derive(PartialEq, Eq, Hash, Clone, Debug)]
42pub enum Code {
43 MissingSecret,
45 InvalidSecret,
47 MissingUserIp,
49 InvalidUserIp,
51 MissingSiteKey,
53 InvalidSiteKey,
55 MissingResponse,
57 InvalidResponse,
59 BadRequest,
61 InvalidAlreadySeen,
63 SiteSecretMismatch,
65 InvalidSecretExtWrongLen,
67 InvalidSecretExtNotHex,
69 SecretVersionUnknown,
77 Unknown(String),
79}
80
81impl<'de> Deserialize<'de> for Code {
82 fn deserialize<D>(de: D) -> Result<Self, D::Error>
85 where
86 D: Deserializer<'de>,
87 {
88 let code = String::deserialize(de)?;
89 Ok(match &*code {
90 "missing-input-secret" => Code::MissingSecret,
91 "invalid-input-secret" => Code::InvalidSecret,
92 "missing-input-response" => Code::MissingResponse,
93 "invalid-input-response" => Code::InvalidResponse,
94 "bad-request" => Code::BadRequest,
95 "invalid-or-already-seen-response" => Code::InvalidAlreadySeen,
96 "sitekey-secret-mismatch" => Code::SiteSecretMismatch,
97 _ => Code::Unknown(code),
98 })
99 }
100}
101
102impl Serialize for Code {
103 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
104 where
105 S: serde::Serializer,
106 {
107 serializer.serialize_str(match self {
108 Code::MissingSecret => "missing-input-secret",
109 Code::InvalidSecret => "invalid-input-secret",
110 Code::MissingUserIp => "missing-input-user-ip",
111 Code::InvalidUserIp => "invalid-input-user-ip",
112 Code::MissingSiteKey => "missing-input-sitekey",
113 Code::InvalidSiteKey => "invalid-input-sitekey",
114 Code::MissingResponse => "missing-input-response",
115 Code::InvalidResponse => "invalid-input-response",
116 Code::BadRequest => "bad-request",
117 Code::InvalidAlreadySeen => "invalid-or-already-seen-response",
118 Code::SiteSecretMismatch => "sitekey-secret-mismatch",
119 Code::SecretVersionUnknown => "secret-version-unknown",
120 Code::InvalidSecretExtNotHex => "invalid-secret-ext-not-hex",
121 Code::InvalidSecretExtWrongLen => "invalid-secret-ext-wrong-len",
122 Code::Unknown(s) => s.as_str(),
123 })
124 }
125}
126
127impl fmt::Display for Code {
128 #[allow(unused_variables)]
129 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
130 match self {
131 Code::MissingSecret => write!(f, "Secret key is missing."),
132 Code::InvalidSecret => write!(f, "Secret key is invalid or malformed."),
133 Code::MissingUserIp => write!(f, "User IP string is missing."),
134 Code::InvalidUserIp => write!(f, "User IP string is invalid."),
135 Code::MissingSiteKey => write!(f, "Site Key string is missing."),
136 Code::InvalidSiteKey => write!(f, "Site Key string is invalid."),
137 Code::InvalidSecretExtWrongLen => {
138 write!(f, "Secret key is not the correct length.")
139 }
140 Code::InvalidSecretExtNotHex => write!(f, "Secret key is not a hex string."),
141 Code::MissingResponse => {
142 write!(f, "The response parameter (verification token) is missing.")
143 }
144 Code::InvalidResponse => write!(
145 f,
146 "The response parameter (verification token) is invalid or malformed."
147 ),
148 Code::BadRequest => write!(f, "The request is invalid or malformed."),
149 Code::InvalidAlreadySeen => write!(
150 f,
151 "The response parameter has already been checked, or has another issue."
152 ),
153 Code::SiteSecretMismatch => {
154 write!(f, "The sitekey is not registered with the provided secret.")
155 }
156 Code::SecretVersionUnknown => {
157 write!(f, "The version of the site secret is not recognise.")
158 }
159 Code::Unknown(e) => write!(f, "Unkown error: {e}"),
160 }
161 }
162}
163
164#[cfg(test)]
165mod tests {
166 use super::*;
167 use serde_test::{assert_ser_tokens, Token};
168
169 #[test]
170 fn test_serialize_missing_secret() {
171 let code = Code::MissingSecret;
172 assert_ser_tokens(&code, &[Token::Str("missing-input-secret")]);
173 }
174
175 #[test]
176 fn test_serialize_invalid_secret() {
177 let code = Code::InvalidSecret;
178 assert_ser_tokens(&code, &[Token::Str("invalid-input-secret")]);
179 }
180
181 #[test]
182 fn test_serialize_missing_user_ip() {
183 let code = Code::MissingUserIp;
184 assert_ser_tokens(&code, &[Token::Str("missing-input-user-ip")]);
185 }
186
187 #[test]
188 fn test_serialize_invalid_user_ip() {
189 let code = Code::InvalidUserIp;
190 assert_ser_tokens(&code, &[Token::Str("invalid-input-user-ip")]);
191 }
192
193 #[test]
194 fn test_serialize_missing_site_key() {
195 let code = Code::MissingSiteKey;
196 assert_ser_tokens(&code, &[Token::Str("missing-input-sitekey")]);
197 }
198
199 #[test]
200 fn test_serialize_invalid_site_key() {
201 let code = Code::InvalidSiteKey;
202 assert_ser_tokens(&code, &[Token::Str("invalid-input-sitekey")]);
203 }
204
205 #[test]
206 fn test_serialize_missing_response() {
207 let code = Code::MissingResponse;
208 assert_ser_tokens(&code, &[Token::Str("missing-input-response")]);
209 }
210
211 #[test]
212 fn test_serialize_invalid_response() {
213 let code = Code::InvalidResponse;
214 assert_ser_tokens(&code, &[Token::Str("invalid-input-response")]);
215 }
216
217 #[test]
218 fn test_serialize_bad_request() {
219 let code = Code::BadRequest;
220 assert_ser_tokens(&code, &[Token::Str("bad-request")]);
221 }
222
223 #[test]
224 fn test_serialize_invalid_already_seen() {
225 let code = Code::InvalidAlreadySeen;
226 assert_ser_tokens(&code, &[Token::Str("invalid-or-already-seen-response")]);
227 }
228
229 #[test]
230 fn test_serialize_site_secret_mismatch() {
231 let code = Code::SiteSecretMismatch;
232 assert_ser_tokens(&code, &[Token::Str("sitekey-secret-mismatch")]);
233 }
234
235 #[test]
236 fn test_serialize_secret_version_unknown() {
237 let code = Code::SecretVersionUnknown;
238 assert_ser_tokens(&code, &[Token::Str("secret-version-unknown")]);
239 }
240
241 #[test]
242 fn test_serialize_secret_ext_not_hex() {
243 let code = Code::InvalidSecretExtNotHex;
244 assert_ser_tokens(&code, &[Token::Str("invalid-secret-ext-not-hex")]);
245 }
246
247 #[test]
248 fn test_serialize_unknown_variant() {
249 let code = Code::Unknown("unexpected-error".to_string());
250 assert_ser_tokens(&code, &[Token::Str("unexpected-error")]);
251 }
252
253 #[test]
254 fn test_serialize_invalid_secret_ext_wrong_len() {
255 let code = Code::InvalidSecretExtWrongLen;
256 assert_ser_tokens(&code, &[Token::Str("invalid-secret-ext-wrong-len")]);
257 }
258
259 #[test]
260 fn test_fmt_missing_secret() {
261 let code = Code::MissingSecret;
262 let formatted = format!("{}", code);
263 assert_eq!(formatted, "Secret key is missing.");
264 }
265
266 #[test]
267 fn test_fmt_invalid_secret() {
268 let code = Code::InvalidSecret;
269 let formatted = format!("{}", code);
270 assert_eq!(formatted, "Secret key is invalid or malformed.");
271 }
272
273 #[test]
274 fn test_fmt_missing_user_ip() {
275 let code = Code::MissingUserIp;
276 let formatted = format!("{}", code);
277 assert_eq!(formatted, "User IP string is missing.");
278 }
279
280 #[test]
281 fn test_fmt_invalid_user_ip() {
282 let code = Code::InvalidUserIp;
283 let formatted = format!("{}", code);
284 assert_eq!(formatted, "User IP string is invalid.");
285 }
286
287 #[test]
288 fn test_fmt_missing_site_key() {
289 let code = Code::MissingSiteKey;
290 let formatted = format!("{}", code);
291 assert_eq!(formatted, "Site Key string is missing.");
292 }
293
294 #[test]
295 fn test_fmt_invalid_site_key() {
296 let code = Code::InvalidSiteKey;
297 let formatted = format!("{}", code);
298 assert_eq!(formatted, "Site Key string is invalid.");
299 }
300
301 #[test]
302 fn test_fmt_invlid_secret_ext_wrong_len() {
303 let code = Code::InvalidSecretExtWrongLen;
304 let formatted = format!("{}", code);
305 assert_eq!(formatted, "Secret key is not the correct length.");
306 }
307
308 #[test]
309 fn test_fmt_invalid_secret_ext_no_hex() {
310 let code = Code::InvalidSecretExtNotHex;
311 let formatted = format!("{}", code);
312 assert_eq!(formatted, "Secret key is not a hex string.");
313 }
314
315 #[test]
316 fn test_fmt_missing_response() {
317 let code = Code::MissingResponse;
318 let formatted = format!("{}", code);
319 assert_eq!(
320 formatted,
321 "The response parameter (verification token) is missing."
322 );
323 }
324
325 #[test]
326 fn test_fmt_invalid_response() {
327 let code = Code::InvalidResponse;
328 let formatted = format!("{}", code);
329 assert_eq!(
330 formatted,
331 "The response parameter (verification token) is invalid or malformed."
332 );
333 }
334
335 #[test]
336 fn test_fmt_bad_request() {
337 let code = Code::BadRequest;
338 let formatted = format!("{}", code);
339 assert_eq!(formatted, "The request is invalid or malformed.");
340 }
341
342 #[test]
343 fn test_fmt_invalid_already_seen() {
344 let code = Code::InvalidAlreadySeen;
345 let formatted = format!("{}", code);
346 assert_eq!(
347 formatted,
348 "The response parameter has already been checked, or has another issue."
349 );
350 }
351
352 #[test]
353 fn test_fmt_site_secret_mismatch() {
354 let code = Code::SiteSecretMismatch;
355 let formatted = format!("{}", code);
356 assert_eq!(
357 formatted,
358 "The sitekey is not registered with the provided secret."
359 );
360 }
361
362 #[test]
363 fn test_fmt_secret_version_unknown() {
364 let code = Code::SecretVersionUnknown;
365 let formatted = format!("{}", code);
366 assert_eq!(
367 formatted,
368 "The version of the site secret is not recognise."
369 );
370 }
371
372 #[test]
373 fn test_fmt_invalid_secret_ext_not_hex() {
374 let code = Code::InvalidSecretExtNotHex;
375 let formatted = format!("{}", code);
376 assert_eq!(formatted, "Secret key is not a hex string.");
377 }
378
379 #[test]
380 fn test_fmt_unknown_error() {
381 let error_message = "Some unknown error occurred.";
382 let code = Code::Unknown(error_message.to_string());
383 let formatted = format!("{}", code);
384 assert_eq!(formatted, format!("Unkown error: {}", error_message));
385 }
386}