Skip to main content

confirm_email/error/
mod.rs

1use aes_gcm::Error as AesGcmError;
2use argon2::password_hash::Error as PwhError;
3use base64::DecodeError as Base64DecodeError;
4use chrono::{DateTime, Utc};
5use serde_json::Error as JsonError;
6use std::string::FromUtf8Error;
7use std::{error::Error as StdError, fmt};
8
9/// Defines possible errors
10#[derive(Debug)]
11pub enum Error {
12    /// The Token is expired at the date time
13    Expired(DateTime<Utc>),
14
15    /// JSON serialization failed
16    JsonSerialize(JsonError),
17    /// JSON deserialization failed
18    JsonDeserialize(JsonError),
19
20    /// The requested expiration was invalid (<= 1s)
21    InvalidExpirationSeconds(i64),
22
23    /// Expiration timestamp was out of range for `Utc.timestamp_opt`
24    ExpirationOutOfRange(i64),
25
26    /// Something went wrong with password hashing (salt‐encode or hash_password)
27    PasswordHash(PwhError),
28
29    /// We got a PHC string back, but it had no `.hash` component
30    MissingHash,
31
32    /// AES‐GCM encryption/decryption error
33    AesGcm(AesGcmError),
34
35    /// Base64 decoding failed
36    Base64Decode(Base64DecodeError),
37
38    /// UTF‑8 decoding (after decryption) failed
39    Utf8(FromUtf8Error),
40
41    /// Encrypted blob was too short to contain salt and nonce
42    InvalidDataLength(usize),
43}
44
45impl fmt::Display for Error {
46    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
47        match self {
48            Error::Expired(dt) => write!(f, "token expired at `{dt:#?}`"),
49
50            Error::JsonSerialize(e) => write!(f, "failed to serialize payload to JSON: {e}"),
51
52            Error::JsonDeserialize(e) => write!(f, "failed to parse JSON payload: {e}"),
53
54            Error::InvalidExpirationSeconds(secs) => write!(
55                f,
56                "invalid expiration {secs}s: must be greater than 1 second"
57            ),
58
59            Error::ExpirationOutOfRange(ts) => write!(
60                f,
61                "expiration timestamp {ts} is out of range for a UTC DateTime"
62            ),
63
64            Error::PasswordHash(e) => write!(f, "password hashing failed: {e}"),
65
66            Error::MissingHash => write!(f, "failed to extract raw hash bytes from PHC string"),
67
68            Error::AesGcm(e) => write!(f, "AES‑GCM error: {e}"),
69
70            Error::Base64Decode(e) => write!(f, "Base64 decode error: {e:#?}"),
71
72            Error::Utf8(e) => write!(f, "UTF‑8 conversion error: {e:#?}"),
73
74            Error::InvalidDataLength(got) => {
75                let needed = 16 + 12;
76                write!(
77                    f,
78                    "invalid encrypted data length: expected at least {needed} bytes but got {got}"
79                )
80            }
81        }
82    }
83}
84
85impl StdError for Error {
86    fn source(&self) -> Option<&(dyn StdError + 'static)> {
87        match self {
88            Error::PasswordHash(e) => Some(e),
89            Error::AesGcm(e) => Some(e),
90            Error::Base64Decode(e) => Some(e),
91            Error::Utf8(e) => Some(e),
92            _ => None,
93        }
94    }
95}
96
97// allow `?` on password-hash results
98impl From<PwhError> for Error {
99    fn from(e: PwhError) -> Error {
100        Error::PasswordHash(e)
101    }
102}
103
104impl From<AesGcmError> for Error {
105    fn from(e: AesGcmError) -> Error {
106        Error::AesGcm(e)
107    }
108}
109
110impl From<Base64DecodeError> for Error {
111    fn from(e: Base64DecodeError) -> Error {
112        Error::Base64Decode(e)
113    }
114}
115
116impl From<FromUtf8Error> for Error {
117    fn from(e: FromUtf8Error) -> Error {
118        Error::Utf8(e)
119    }
120}
121impl From<JsonError> for Error {
122    fn from(e: JsonError) -> Self {
123        Error::JsonSerialize(e)
124    }
125}