1use thiserror::Error;
2
3#[derive(Error, Debug)]
4pub enum VkError {
5 #[error("HTTP request failed: {0}")]
6 HttpError(#[from] reqwest::Error),
7
8 #[error("VK API error {code}: {message}")]
9 ApiError { code: i32, message: String },
10
11 #[error("Invalid response format: {0}")]
12 InvalidResponse(String),
13
14 #[error("JSON error: {0}")]
15 JsonError(#[from] serde_json::Error),
16
17 #[error("Missing required field: {0}")]
18 MissingField(String),
19
20 #[error("Rate limit exceeded")]
21 RateLimit,
22
23 #[error("Authentication error: {0}")]
24 AuthError(String),
25
26 #[error("Network error: {0}")]
27 NetworkError(String),
28
29 #[error("Timeout error: {0}")]
30 Timeout(String),
31
32 #[error("Configuration error: {0}")]
33 ConfigError(String),
34
35 #[error("Internal error: {0}")]
36 InternalError(String),
37
38 #[error("{0}")]
39 Custom(String),
40}
41
42impl VkError {
43 pub fn api_error<T: Into<i32>, M: Into<String>>(code: T, message: M) -> Self {
44 VkError::ApiError {
45 code: code.into(),
46 message: message.into(),
47 }
48 }
49
50 pub fn is_rate_limit(&self) -> bool {
51 matches!(self, VkError::RateLimit)
52 }
53
54 pub fn is_api_error(&self) -> bool {
55 matches!(self, VkError::ApiError { .. })
56 }
57
58 pub fn api_error_code(&self) -> Option<i32> {
59 match self {
60 VkError::ApiError { code, .. } => Some(*code),
61 _ => None,
62 }
63 }
64}
65
66pub type VkResult<T> = Result<T, VkError>;
67
68impl From<&str> for VkError {
69 fn from(s: &str) -> Self {
70 VkError::Custom(s.to_string())
71 }
72}
73
74impl From<String> for VkError {
75 fn from(s: String) -> Self {
76 VkError::Custom(s)
77 }
78}
79
80pub trait VkResponseExt {
81 fn extract_error(self) -> VkResult<serde_json::Value>;
82 fn has_error(&self) -> bool;
83}
84
85impl VkResponseExt for serde_json::Value {
86 fn extract_error(self) -> VkResult<serde_json::Value> {
87 if let Some(error) = self.get("error") {
88 let code = error["error_code"]
89 .as_i64()
90 .ok_or_else(|| VkError::InvalidResponse("Missing error_code".to_string()))?;
91
92 let message = error["error_msg"]
93 .as_str()
94 .ok_or_else(|| VkError::InvalidResponse("Missing error_msg".to_string()))?
95 .to_string();
96
97 Err(VkError::api_error(code as i32, message))
98 } else {
99 Ok(self)
100 }
101 }
102
103 fn has_error(&self) -> bool {
104 self.get("error").is_some()
105 }
106}