1pub const E_AUTH_MISSING: i64 = 893301;
10pub const E_QR_TIMEOUT: i64 = 893302;
12pub const E_CRYPTO: i64 = 893303;
14pub const E_STORAGE: i64 = 893304;
16pub const E_OTHER: i64 = 893999;
18
19#[derive(Debug)]
24pub enum AuthError {
25 MissingCredentials(String),
27
28 QrTimeout,
30
31 Crypto(String),
33
34 Storage(String),
36
37 Transport(wecomx_transport::Error),
39
40 Other(Box<dyn std::error::Error + Send + Sync>),
42}
43
44impl AuthError {
45 #[must_use]
50 pub fn code(&self) -> i64 {
51 match self {
52 AuthError::Transport(inner) => inner.code(),
53 AuthError::MissingCredentials(_) => E_AUTH_MISSING,
54 AuthError::QrTimeout => E_QR_TIMEOUT,
55 AuthError::Crypto(_) => E_CRYPTO,
56 AuthError::Storage(_) => E_STORAGE,
57 AuthError::Other(_) => E_OTHER,
58 }
59 }
60
61 #[must_use]
62 pub fn message(&self) -> String {
63 match self {
64 AuthError::Transport(inner) => inner.to_string(),
65 AuthError::MissingCredentials(message)
66 | AuthError::Crypto(message)
67 | AuthError::Storage(message) => message.clone(),
68 AuthError::QrTimeout => "扫码超时(5 分钟),请重试".to_string(),
69 AuthError::Other(e) => e.to_string(),
70 }
71 }
72}
73
74impl std::fmt::Display for AuthError {
75 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
76 let code = self.code();
77 match self {
78 AuthError::MissingCredentials(msg) => write!(f, "AuthError: {msg} [code={code}]"),
79 AuthError::QrTimeout => write!(f, "QrTimeout: {} [code={code}]", self.message()),
80 AuthError::Crypto(msg) => write!(f, "CryptoError: {msg} [code={code}]"),
81 AuthError::Storage(msg) => write!(f, "StorageError: {msg} [code={code}]"),
82 AuthError::Transport(inner) => write!(f, "{inner}"),
83 AuthError::Other(e) => write!(f, "UnknownError: {e} [code={code}]"),
84 }
85 }
86}
87
88impl std::error::Error for AuthError {
89 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
90 match self {
91 AuthError::Transport(inner) => Some(inner),
92 AuthError::Other(inner) => Some(inner.as_ref()),
93 _ => None,
94 }
95 }
96}
97
98impl From<wecomx_transport::Error> for AuthError {
101 fn from(e: wecomx_transport::Error) -> Self {
102 AuthError::Transport(e)
103 }
104}
105
106impl From<AuthError> for wecomx_transport::Error {
109 fn from(e: AuthError) -> Self {
110 match e {
111 AuthError::Transport(inner) => inner,
113 other => wecomx_transport::Error::Other(Box::new(other)),
114 }
115 }
116}
117
118#[cfg(test)]
119mod tests {
120 use super::*;
128
129 #[test]
133 fn code_maps_each_variant() {
134 assert_eq!(
135 AuthError::MissingCredentials("x".into()).code(),
136 E_AUTH_MISSING
137 );
138 assert_eq!(AuthError::QrTimeout.code(), E_QR_TIMEOUT);
139 assert_eq!(AuthError::Crypto("x".into()).code(), E_CRYPTO);
140 assert_eq!(AuthError::Storage("x".into()).code(), E_STORAGE);
141 assert_eq!(AuthError::Other("x".into()).code(), E_OTHER);
142 }
143
144 #[test]
148 fn code_transport_delegates() {
149 let e = AuthError::Transport(wecomx_transport::Error::Api {
150 message: "invalid".into(),
151 action: "/x".into(),
152 code: Some(853000),
153 body: Box::new(serde_json::Value::Null),
154 });
155 assert_eq!(e.code(), 853000);
156 }
157
158 #[test]
162 fn transport_error_roundtrip() {
163 let api = wecomx_transport::Error::Api {
164 message: "expired".into(),
165 action: "/x".into(),
166 code: Some(853004),
167 body: Box::new(serde_json::Value::Null),
168 };
169 let wrapped = AuthError::from(api);
170 assert!(matches!(wrapped, AuthError::Transport(_)));
171
172 let back: wecomx_transport::Error = wrapped.into();
173 assert!(matches!(
174 back,
175 wecomx_transport::Error::Api {
176 code: Some(853004),
177 ..
178 }
179 ));
180 }
181
182 #[test]
186 fn local_variant_boxes_into_transport_other() {
187 let e: wecomx_transport::Error = AuthError::MissingCredentials("need login".into()).into();
188 match e {
189 wecomx_transport::Error::Other(boxed) => {
190 let down = boxed.downcast_ref::<AuthError>();
191 assert!(
192 down.is_some_and(|e| matches!(e, AuthError::MissingCredentials(_))),
193 "expected AuthError::MissingCredentials"
194 );
195 }
196 other => panic!("expected Other, got {other:?}"),
197 }
198 }
199}