Skip to main content

rullst_connect/
error.rs

1use thiserror::Error;
2
3/// Official errors of the Rullst Connect library
4#[derive(Error, Debug)]
5pub enum ConnectError {
6    #[error("HTTP request failed: {0}")]
7    Reqwest(String),
8
9    #[error("Failed to parse JSON: {0}")]
10    Json(String),
11
12    #[error("Failed to decode Base64: {0}")]
13    Base64(String),
14
15    #[error("JWT processing failed: {0}")]
16    Jwt(String),
17
18    #[error("System time error: {0}")]
19    Time(String),
20
21    #[error("Missing token or unexpected response: {0}")]
22    Token(String),
23
24    #[error("Provider API Error ({code}): {message}")]
25    ProviderApiError { code: String, message: String },
26
27    #[error("Provider specific error: {0}")]
28    Provider(String),
29
30    #[error("Invalid CSRF state: {0}")]
31    InvalidState(String),
32}
33
34impl From<reqwest::Error> for ConnectError {
35    fn from(err: reqwest::Error) -> Self {
36        ConnectError::Reqwest(err.to_string())
37    }
38}
39
40impl From<serde_json::Error> for ConnectError {
41    fn from(err: serde_json::Error) -> Self {
42        ConnectError::Json(err.to_string())
43    }
44}
45
46impl From<base64::DecodeError> for ConnectError {
47    fn from(err: base64::DecodeError) -> Self {
48        ConnectError::Base64(err.to_string())
49    }
50}
51
52impl From<jsonwebtoken::errors::Error> for ConnectError {
53    fn from(err: jsonwebtoken::errors::Error) -> Self {
54        ConnectError::Jwt(err.to_string())
55    }
56}
57
58impl From<std::time::SystemTimeError> for ConnectError {
59    fn from(err: std::time::SystemTimeError) -> Self {
60        ConnectError::Time(err.to_string())
61    }
62}
63
64#[cfg(test)]
65mod tests {
66    use super::*;
67
68    #[test]
69    #[cfg_attr(miri, ignore)]
70    fn test_reqwest_error_conversion() {
71        let err = reqwest::Client::new()
72            .get("htt p://invalid")
73            .build()
74            .unwrap_err();
75        let connect_err: ConnectError = err.into();
76        match connect_err {
77            ConnectError::Reqwest(_) => (),
78            _ => panic!("Expected ConnectError::Reqwest"),
79        }
80    }
81
82    #[test]
83    fn test_serde_json_error_conversion() {
84        let err: serde_json::Error =
85            serde_json::from_str::<serde_json::Value>("invalid json").unwrap_err();
86        let connect_err: ConnectError = err.into();
87        match connect_err {
88            ConnectError::Json(_) => (),
89            _ => panic!("Expected ConnectError::Json"),
90        }
91    }
92
93    #[test]
94    fn test_base64_error_conversion() {
95        use base64::Engine;
96        let err = base64::engine::general_purpose::STANDARD
97            .decode("invalid!base64")
98            .unwrap_err();
99        let connect_err: ConnectError = err.into();
100        match connect_err {
101            ConnectError::Base64(_) => (),
102            _ => panic!("Expected ConnectError::Base64"),
103        }
104    }
105
106    #[test]
107    fn test_jwt_error_conversion() {
108        let err = jsonwebtoken::decode_header("invalid.jwt.header").unwrap_err();
109        let connect_err: ConnectError = err.into();
110        match connect_err {
111            ConnectError::Jwt(_) => (),
112            _ => panic!("Expected ConnectError::Jwt"),
113        }
114    }
115
116    #[test]
117    fn test_time_error_conversion() {
118        let err = std::time::SystemTime::UNIX_EPOCH
119            .duration_since(std::time::SystemTime::now())
120            .unwrap_err();
121        let connect_err: ConnectError = err.into();
122        match connect_err {
123            ConnectError::Time(_) => (),
124            _ => panic!("Expected ConnectError::Time"),
125        }
126    }
127
128    #[test]
129    fn test_error_debug_and_display() {
130        let errors = vec![
131            ConnectError::Reqwest("test".to_string()),
132            ConnectError::Json("test".to_string()),
133            ConnectError::Base64("test".to_string()),
134            ConnectError::Jwt("test".to_string()),
135            ConnectError::Time("test".to_string()),
136            ConnectError::Token("test".to_string()),
137            ConnectError::ProviderApiError {
138                code: "400".to_string(),
139                message: "test".to_string(),
140            },
141            ConnectError::Provider("test".to_string()),
142            ConnectError::InvalidState("test".to_string()),
143        ];
144
145        for err in errors {
146            let _debug = format!("{:?}", err);
147            let _display = format!("{}", err);
148        }
149    }
150}