1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
use bcrypt::BcryptError;
use dotenv;
use rocket::{
    http::Status,
    response::{self, Responder, Response},
    Request,
};
use sqlx::error::{DatabaseError, ErrorKind};

/// Represents errors that can occur within cryptographic operations.
#[derive(Debug)]
pub enum CryptoError {
    /// An error arising from RSA key pair generation or manipulation.
    ///
    /// This variant is used to encapsulate errors from the `rsa` crate,
    /// such as those occurring during the generation of a new RSA key pair
    /// or when manipulating existing keys.
    KeyPairError(rsa::errors::Error),

    /// An error related to system time operations.
    ///
    /// This variant is used when encountering issues with retrieving or
    /// manipulating system time, which may be needed for setting key
    /// expiration times or other time-sensitive cryptographic operations.
    SystemTimeError(std::time::SystemTimeError),

    /// Indicates a failure in JWT creation.
    ///
    /// This error is used when the JWT creation process fails, which might
    /// be due to issues with the payload, signing process, or other aspects
    /// of token generation.
    TokenCreationError,

    /// An error related to environment variable operations.
    ///
    /// This variant is used when encountering issues with retrieving
    /// environment variables, such as missing or malformed values that
    /// are expected to configure or drive cryptographic operations.
    EnvVarError(dotenv::Error),

    /// An error arising from parsing integer values.
    ///
    /// This variant is used when an error occurs while parsing string
    /// representations of integers into their respective numeric types.
    /// It is typically encountered when converting configuration values
    /// or parameters from text to numbers.
    ParseIntError(std::num::ParseIntError),

    /// Indicates a generic database error.
    ///
    /// This variant represents a generic error related to database
    /// operations within cryptographic contexts.
    DatabaseError,
}

/// A structured error type for hashing operations, encapsulating details about the error.
#[derive(Debug)]
pub struct HashError {
    /// A human-readable message describing the error.
    pub message: String,
    /// An optional `BcryptError` providing more specific details if the error is related to bcrypt operations.
    bcrypt_error: Option<BcryptError>,
}

impl HashError {
    /// Constructs a new `HashError`.
    ///
    /// # Arguments
    /// * `message` - A message describing the error.
    /// * `bcrypt_error` - An optional `BcryptError` related to the hashing operation.
    ///
    /// # Returns
    /// Returns an instance of `HashError`.
    pub fn new(message: &str, bcrypt_error: Option<BcryptError>) -> Self {
        Self {
            message: message.to_string(),
            bcrypt_error,
        }
    }
}

impl std::fmt::Display for HashError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.message)
    }
}

impl std::error::Error for HashError {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        self.bcrypt_error
            .as_ref()
            .map(|e| e as &dyn std::error::Error)
    }
}

impl DatabaseError for HashError {
    fn message(&self) -> &str {
        &self.message
    }

    fn kind(&self) -> ErrorKind {
        ErrorKind::Other
    }

    fn as_error(&self) -> &(dyn std::error::Error + Send + Sync + 'static) {
        self
    }

    fn as_error_mut(&mut self) -> &mut (dyn std::error::Error + Send + Sync + 'static) {
        self
    }

    fn into_error(self: Box<Self>) -> Box<(dyn std::error::Error + Send + Sync + 'static)> {
        self
    }
}

/// Allows conversion from `rsa::errors::Error` to `CryptoError`.
impl From<rsa::errors::Error> for CryptoError {
    fn from(err: rsa::errors::Error) -> CryptoError {
        CryptoError::KeyPairError(err)
    }
}

/// Allows conversion from `dotenv::Error` to `CryptoError`.
impl From<dotenv::Error> for CryptoError {
    fn from(err: dotenv::Error) -> CryptoError {
        CryptoError::EnvVarError(err)
    }
}

/// Allows conversion from `std::num::ParseIntError` to `CryptoError`.
impl From<std::num::ParseIntError> for CryptoError {
    fn from(err: std::num::ParseIntError) -> CryptoError {
        CryptoError::ParseIntError(err)
    }
}

/// Allows conversion from `jsonwebtoken::errors::Error` to `CryptoError`.
impl From<jsonwebtoken::errors::Error> for CryptoError {
    fn from(_err: jsonwebtoken::errors::Error) -> Self {
        CryptoError::TokenCreationError
    }
}

/// Enables conversion from `std::time::SystemTimeError` to `CryptoError`.
impl From<std::time::SystemTimeError> for CryptoError {
    fn from(err: std::time::SystemTimeError) -> CryptoError {
        CryptoError::SystemTimeError(err)
    }
}

/// Implementation of the `Responder` trait for `CryptoError`.
/// This allows `CryptoError` instances to be directly used in Rocket handler responses.
impl<'r> Responder<'r, 'static> for CryptoError {
    /// Converts a `CryptoError` into a Rocket response.
    ///
    /// # Returns
    ///
    /// A Rocket response indicating an error occurred.
    fn respond_to(self, _: &'r Request<'_>) -> response::Result<'static> {
        match self {
            CryptoError::KeyPairError(_) => Response::build().status(Status::BadRequest).ok(),
            CryptoError::TokenCreationError
            | CryptoError::DatabaseError
            | CryptoError::SystemTimeError(_)
            | CryptoError::ParseIntError(_)
            | CryptoError::EnvVarError(_) => {
                Response::build().status(Status::InternalServerError).ok()
            }
        }
    }
}