tencentcloud_sms/
error.rs

1use crate::api::send_sms::response::SendStatus;
2use std::time::Duration;
3use thiserror::Error;
4
5#[derive(Debug, Error)]
6pub enum SmsError {
7    #[error("HTTP请求失败: {0}")]
8    Http(#[from] reqwest::Error),
9
10    #[error("JSON序列化失败: {0}")]
11    Serialization(#[from] serde_json::Error),
12
13    #[error("认证失败 [{code}]: {message} (request_id: {request_id})")]
14    Authentication {
15        code: String,
16        message: String,
17        request_id: String,
18    },
19
20    #[error("参数错误 [{code}]: {message} (request_id: {request_id})")]
21    InvalidParameter {
22        code: String,
23        message: String,
24        request_id: String,
25    },
26
27    #[error(
28        "频率限制 [{code}]: {message}, 建议重试延迟: {retry_after:?} (request_id: {request_id})"
29    )]
30    RateLimited {
31        code: String,
32        message: String,
33        retry_after: Option<Duration>,
34        request_id: String,
35    },
36
37    #[error("余额不足: {message} (request_id: {request_id})")]
38    InsufficientBalance {
39        code: String,
40        message: String,
41        request_id: String,
42    },
43
44    #[error("业务错误 [{code}]: {message} (request_id: {request_id})")]
45    Business {
46        code: String,
47        message: String,
48        request_id: String,
49    },
50
51    #[error("内部错误 [{code}]: {message} (request_id: {request_id})")]
52    Internal {
53        code: String,
54        message: String,
55        request_id: String,
56    },
57
58    #[error("部分号码发送失败: 成功 {} 个, 失败 {} 个", succeeded.len(), failed.len())]
59    PartialFailure {
60        succeeded: Vec<SendStatus>,
61        failed: Vec<SendStatus>,
62    },
63
64    #[error("配置错误: {0}")]
65    Configuration(String),
66
67    #[error("API 响应异常: {0}")]
68    UnexpectedResponse(String),
69}
70
71impl SmsError {
72    /// 判断错误是否可重试
73    pub fn is_retryable(&self) -> bool {
74        matches!(
75            self,
76            Self::Http(_) | Self::RateLimited { .. } | Self::Internal { .. }
77        )
78    }
79
80    /// 获取建议的重试延迟时间
81    pub fn retry_after(&self) -> Option<Duration> {
82        match self {
83            Self::RateLimited { retry_after, .. } => *retry_after,
84            Self::Internal { .. } => Some(Duration::from_millis(100)),
85            _ => None,
86        }
87    }
88
89    /// 获取请求ID(如果存在)
90    pub fn request_id(&self) -> Option<&str> {
91        match self {
92            Self::Authentication { request_id, .. }
93            | Self::InvalidParameter { request_id, .. }
94            | Self::RateLimited { request_id, .. }
95            | Self::InsufficientBalance { request_id, .. }
96            | Self::Business { request_id, .. }
97            | Self::Internal { request_id, .. } => Some(request_id),
98            _ => None,
99        }
100    }
101
102    /// 判断是否为部分失败错误
103    ///
104    /// # 示例
105    /// ```ignore
106    /// match client.send_sms(request).await {
107    ///     Ok(results) => { /* 全部成功 */ }
108    ///     Err(e) if e.is_partial_failure() => {
109    ///         // 部分失败,可以获取成功和失败的数据
110    ///         let (succeeded, failed) = e.partial_results().unwrap();
111    ///         println!("成功: {}, 失败: {}", succeeded.len(), failed.len());
112    ///     }
113    ///     Err(e) => { /* 其他错误 */ }
114    /// }
115    /// ```
116    pub fn is_partial_failure(&self) -> bool {
117        matches!(self, Self::PartialFailure { .. })
118    }
119
120    /// 获取部分失败的详细数据
121    ///
122    /// 返回 `(成功列表, 失败列表)` 的引用,仅当错误为 `PartialFailure` 时返回 `Some`
123    ///
124    /// # 示例
125    /// ```ignore
126    /// if let Some((succeeded, failed)) = error.partial_results() {
127    ///     for status in succeeded {
128    ///         println!("✓ {} 发送成功", status.phone_number);
129    ///     }
130    ///     for status in failed {
131    ///         println!("✗ {} 发送失败: {}", status.phone_number, status.message);
132    ///     }
133    /// }
134    /// ```
135    pub fn partial_results(&self) -> Option<(&[SendStatus], &[SendStatus])> {
136        match self {
137            Self::PartialFailure { succeeded, failed } => Some((succeeded, failed)),
138            _ => None,
139        }
140    }
141
142    /// 消费错误并获取部分失败的详细数据(拥有所有权)
143    ///
144    /// 返回 `(成功列表, 失败列表)`,仅当错误为 `PartialFailure` 时返回 `Some`
145    ///
146    /// # 示例
147    /// ```ignore
148    /// match client.send_sms(request).await {
149    ///     Err(e) => {
150    ///         if let Some((succeeded, failed)) = e.into_partial_results() {
151    ///             // 现在拥有了 Vec<SendStatus> 的所有权
152    ///             process_results(succeeded, failed);
153    ///         }
154    ///     }
155    ///     Ok(results) => { /* ... */ }
156    /// }
157    /// ```
158    pub fn into_partial_results(self) -> Option<(Vec<SendStatus>, Vec<SendStatus>)> {
159        match self {
160            Self::PartialFailure { succeeded, failed } => Some((succeeded, failed)),
161            _ => None,
162        }
163    }
164}
165
166pub type SmsResult<T> = Result<T, SmsError>;