use thiserror::Error;
#[derive(Debug, Error)]
#[error("{message}")]
pub struct HttpResponseError {
pub code: u16,
pub message: String,
pub error_reference: Option<String>,
}
#[derive(Debug, Error)]
#[error("Exceeded maximum retry count of {tries}. Last message: {message}")]
pub struct MaxHttpRetriesExceededError {
pub code: u16,
pub tries: u32,
pub message: String,
pub error_reference: Option<String>,
}
#[derive(Debug, Error, Clone, PartialEq, Eq)]
pub enum InvalidHttpRequestError {
#[error("Invalid Http method {method}.")]
InvalidMethod {
method: String,
},
#[error("Cannot set a body without also setting body_type.")]
MissingBodyType,
#[error("Cannot use {method} without specifying data.")]
MissingBody {
method: String,
},
}
#[derive(Debug, Error)]
pub enum HttpError {
#[error(transparent)]
Response(#[from] HttpResponseError),
#[error(transparent)]
MaxRetries(#[from] MaxHttpRetriesExceededError),
#[error(transparent)]
InvalidRequest(#[from] InvalidHttpRequestError),
#[error("Network error: {0}")]
Network(#[from] reqwest::Error),
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_http_response_error_includes_status_code_in_message() {
let error = HttpResponseError {
code: 404,
message: r#"{"error":"Not Found"}"#.to_string(),
error_reference: None,
};
assert_eq!(error.to_string(), r#"{"error":"Not Found"}"#);
}
#[test]
fn test_http_response_error_includes_request_id() {
let error = HttpResponseError {
code: 500,
message: r#"{"error":"Internal Server Error","error_reference":"If you report this error, please include this id: abc-123."}"#.to_string(),
error_reference: Some("abc-123".to_string()),
};
assert_eq!(error.error_reference, Some("abc-123".to_string()));
assert!(error.to_string().contains("abc-123"));
}
#[test]
fn test_max_retries_error_includes_retry_count() {
let error = MaxHttpRetriesExceededError {
code: 429,
tries: 3,
message: r#"{"error":"Rate limited"}"#.to_string(),
error_reference: None,
};
let message = error.to_string();
assert!(message.contains("3"));
assert!(message.contains("Exceeded maximum retry count"));
}
#[test]
fn test_invalid_request_error_missing_body() {
let error = InvalidHttpRequestError::MissingBody {
method: "post".to_string(),
};
assert_eq!(
error.to_string(),
"Cannot use post without specifying data."
);
}
#[test]
fn test_invalid_request_error_missing_body_type() {
let error = InvalidHttpRequestError::MissingBodyType;
assert_eq!(
error.to_string(),
"Cannot set a body without also setting body_type."
);
}
#[test]
fn test_error_types_implement_std_error() {
let http_error: &dyn std::error::Error = &HttpResponseError {
code: 400,
message: "test".to_string(),
error_reference: None,
};
let _ = http_error;
let max_retries_error: &dyn std::error::Error = &MaxHttpRetriesExceededError {
code: 429,
tries: 3,
message: "test".to_string(),
error_reference: None,
};
let _ = max_retries_error;
let invalid_error: &dyn std::error::Error = &InvalidHttpRequestError::MissingBodyType;
let _ = invalid_error;
}
}