use std::time::Duration;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum WechatIlinkError {
#[error("API error: {message} (http={http_status}, errcode={errcode})")]
Api {
message: String,
http_status: u16,
errcode: i32,
},
#[error("Rate limited: {message} (http={http_status}, errcode={errcode}, retry_after={}s)", retry_after.as_secs())]
RateLimited {
retry_after: Duration,
message: String,
http_status: u16,
errcode: i32,
},
#[error("Auth error: {0}")]
Auth(String),
#[error("No context_token for user {0}")]
NoContext(String),
#[error("Media error: {0}")]
Media(String),
#[error("Transport error: {0}")]
Transport(#[from] reqwest::Error),
#[error("JSON error: {0}")]
Json(#[from] serde_json::Error),
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
#[error("{0}")]
Other(String),
}
impl WechatIlinkError {
pub fn is_session_expired(&self) -> bool {
matches!(self, WechatIlinkError::Api { errcode: -14, .. })
}
pub fn is_rate_limited(&self) -> bool {
matches!(self, WechatIlinkError::RateLimited { .. })
}
pub fn retry_after(&self) -> Option<Duration> {
match self {
WechatIlinkError::RateLimited { retry_after, .. } => Some(*retry_after),
_ => None,
}
}
}
pub type Result<T> = std::result::Result<T, WechatIlinkError>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn session_expired_true() {
let err = WechatIlinkError::Api {
message: "session expired".to_string(),
http_status: 200,
errcode: -14,
};
assert!(err.is_session_expired());
}
#[test]
fn session_expired_false() {
let err = WechatIlinkError::Api {
message: "other error".to_string(),
http_status: 400,
errcode: -1,
};
assert!(!err.is_session_expired());
}
#[test]
fn non_api_not_session_expired() {
let err = WechatIlinkError::Auth("test".to_string());
assert!(!err.is_session_expired());
}
#[test]
fn error_display() {
let err = WechatIlinkError::Api {
message: "bad request".to_string(),
http_status: 400,
errcode: -1,
};
let msg = format!("{}", err);
assert!(msg.contains("bad request"));
assert!(msg.contains("400"));
assert!(msg.contains("-1"));
}
#[test]
fn no_context_error() {
let err = WechatIlinkError::NoContext("user123".to_string());
let msg = format!("{}", err);
assert!(msg.contains("user123"));
}
}