sms_client/http/
error.rs

1//! HTTP interface related errors.
2
3/// An error originating from the SMS `HttpClient`.
4#[derive(thiserror::Error, Debug)]
5pub enum HttpError {
6    /// Network request failed (connection issues, timeouts, etc.)
7    #[error("Reqwest failure: {0}")]
8    RequestError(#[from] reqwest::Error),
9
10    /// Failed to parse the provided URL.
11    #[error("Invalid URL: {0}")]
12    UrlParseError(#[from] url::ParseError),
13
14    /// Failed to parse JSON response from the API.
15    #[error("JSON parsing failed: {0}")]
16    JsonError(#[from] serde_json::Error),
17
18    /// System IO error
19    #[error("IO error: {0}")]
20    IOError(#[from] std::io::Error),
21
22    /// HTTP request returned a non-success status code.
23    #[error("{}", HttpError::format_http_error(status, message))]
24    HttpStatus {
25        /// HTTP status returned in response.
26        status: u16,
27        /// Full response body as text.
28        message: String,
29    },
30
31    /// API returned success=false with an error message.
32    #[error("{0}")]
33    ApiError(String),
34
35    /// TLS configuration error
36    #[error("TLS error: {0}")]
37    TLSError(String),
38
39    /// API response missing the expected 'response' field.
40    #[error("Missing 'response' field in API response")]
41    MissingResponseField,
42
43    /// Modem response missing the expected 'type' field.
44    #[error("Missing 'type' field in API response")]
45    MissingTypeField,
46
47    /// Modem response missing the expected 'data' field.
48    #[error("Missing 'data' field in API response")]
49    MissingDataField,
50
51    /// Modem response type doesn't match what was expected.
52    #[error("Type mismatch: expected '{expected}', got '{actual}'")]
53    ResponseTypeMismatch {
54        /// The expected response data-type.
55        expected: String,
56        /// The actual response data-type.
57        actual: String,
58    },
59}
60impl HttpError {
61    fn status_text(status: u16) -> &'static str {
62        match status {
63            200 => "OK",
64            400 => "Bad Request",
65            401 => "Unauthorized",
66            403 => "Forbidden",
67            404 => "Not Found",
68            405 => "Method Not Allowed",
69            408 => "Not Acceptable",
70            429 => "Too Many Requests",
71            500 => "Internal Server Error",
72            503 => "Service Unavailable",
73            504 => "Gateway Timeout",
74            _ => "Unknown",
75        }
76    }
77
78    // Ignore Clippy hint since thiserror always provides it by reference.
79    #[allow(clippy::trivially_copy_pass_by_ref)]
80    fn format_http_error(status: &u16, message: &str) -> String {
81        if message.trim().is_empty() {
82            format!("HTTP {status} {}", Self::status_text(*status))
83        } else {
84            format!("HTTP {status}: {message}")
85        }
86    }
87}
88
89/// Result type alias for HTTP operations.
90pub type HttpResult<T> = Result<T, HttpError>;