Skip to main content

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 type doesn't match what was expected.
44    #[error("Type mismatch: expected '{expected}', got '{actual}'")]
45    ResponseTypeMismatch {
46        /// The expected response data-type.
47        expected: String,
48        /// The actual response data-type.
49        actual: String,
50    },
51}
52impl HttpError {
53    fn status_text(status: u16) -> &'static str {
54        match status {
55            200 => "OK",
56            400 => "Bad Request",
57            401 => "Unauthorized",
58            403 => "Forbidden",
59            404 => "Not Found",
60            405 => "Method Not Allowed",
61            408 => "Not Acceptable",
62            429 => "Too Many Requests",
63            500 => "Internal Server Error",
64            503 => "Service Unavailable",
65            504 => "Gateway Timeout",
66            _ => "Unknown",
67        }
68    }
69
70    // Ignore Clippy hint since thiserror always provides it by reference.
71    #[allow(clippy::trivially_copy_pass_by_ref)]
72    fn format_http_error(status: &u16, message: &str) -> String {
73        if message.trim().is_empty() {
74            format!("HTTP {status} {}", Self::status_text(*status))
75        } else {
76            format!("HTTP {status}: {message}")
77        }
78    }
79}
80
81/// Result type alias for HTTP operations.
82pub type HttpResult<T> = Result<T, HttpError>;