1use thiserror::Error;
2
3#[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}