wacloudapi/
error.rs

1//! Error types for the WhatsApp Cloud API SDK
2
3use serde::{Deserialize, Serialize};
4use thiserror::Error;
5
6/// Result type alias for WhatsApp Cloud API operations
7pub type Result<T> = std::result::Result<T, Error>;
8
9/// Errors that can occur when using the WhatsApp Cloud API
10#[derive(Error, Debug)]
11pub enum Error {
12    /// HTTP request failed
13    #[error("HTTP request failed: {0}")]
14    Request(#[from] reqwest::Error),
15
16    /// JSON serialization/deserialization error
17    #[error("JSON error: {0}")]
18    Json(#[from] serde_json::Error),
19
20    /// URL parsing error
21    #[error("URL parse error: {0}")]
22    UrlParse(#[from] url::ParseError),
23
24    /// API error returned by WhatsApp Cloud API
25    #[error("API error: {message} (code: {code})")]
26    Api {
27        code: i32,
28        message: String,
29        error_subcode: Option<i32>,
30        error_data: Option<ApiErrorData>,
31    },
32
33    /// Rate limit exceeded
34    #[error("Rate limit exceeded. Retry after {retry_after:?} seconds")]
35    RateLimited { retry_after: Option<u64> },
36
37    /// Invalid access token
38    #[error("Invalid or expired access token")]
39    InvalidToken,
40
41    /// Media upload failed
42    #[error("Media upload failed: {0}")]
43    MediaUpload(String),
44
45    /// Invalid phone number format
46    #[error("Invalid phone number format: {0}")]
47    InvalidPhoneNumber(String),
48
49    /// Message not sent
50    #[error("Message not sent: {0}")]
51    MessageNotSent(String),
52
53    /// IO error
54    #[error("IO error: {0}")]
55    Io(#[from] std::io::Error),
56}
57
58/// Additional error data from the API
59#[derive(Debug, Clone, Serialize, Deserialize)]
60pub struct ApiErrorData {
61    /// Messaging product (always "whatsapp")
62    pub messaging_product: Option<String>,
63    /// Details about the error
64    pub details: Option<String>,
65}
66
67/// Error response from the WhatsApp Cloud API
68#[derive(Debug, Clone, Serialize, Deserialize)]
69pub struct ApiErrorResponse {
70    /// Error object
71    pub error: ApiError,
72}
73
74/// Error object from the API
75#[derive(Debug, Clone, Serialize, Deserialize)]
76pub struct ApiError {
77    /// Error message
78    pub message: String,
79    /// Error type
80    #[serde(rename = "type")]
81    pub error_type: Option<String>,
82    /// Error code
83    pub code: i32,
84    /// Error subcode
85    pub error_subcode: Option<i32>,
86    /// Error user title
87    pub error_user_title: Option<String>,
88    /// Error user message
89    pub error_user_msg: Option<String>,
90    /// Facebook trace ID
91    pub fbtrace_id: Option<String>,
92    /// Additional error data
93    pub error_data: Option<ApiErrorData>,
94}
95
96impl From<ApiErrorResponse> for Error {
97    fn from(response: ApiErrorResponse) -> Self {
98        let err = response.error;
99
100        // Check for specific error codes
101        match err.code {
102            190 => Error::InvalidToken,
103            4 | 17 | 32 | 613 => Error::RateLimited { retry_after: None },
104            _ => Error::Api {
105                code: err.code,
106                message: err.message,
107                error_subcode: err.error_subcode,
108                error_data: err.error_data,
109            },
110        }
111    }
112}