use reqwest::{Response, StatusCode};
use thiserror::Error;
#[derive(Error, Debug)]
pub enum IntelApiError {
#[error("Unsupported API version or feature: {0}")]
UnsupportedApiVersion(String),
#[error("Reqwest error: {0}")]
Reqwest(#[from] reqwest::Error),
#[error("URL parsing error: {0}")]
UrlParse(#[from] url::ParseError),
#[error("Serde JSON error: {0}")]
JsonError(#[from] serde_json::Error),
#[error("API Error: Status={status}, Request-ID={request_id}, Code={error_code:?}, Message={error_message:?}")]
ApiError {
status: StatusCode,
request_id: String,
error_code: Option<String>,
error_message: Option<String>,
},
#[error("Header missing or invalid: {0}")]
MissingOrInvalidHeader(&'static str),
#[error("Invalid Subscription Key format")]
InvalidSubscriptionKey,
#[error("Cannot provide conflicting parameters: {0}")]
ConflictingParameters(&'static str),
#[error("I/O Error: {0}")]
Io(#[from] std::io::Error),
#[error("Header value parse error for '{0}': {1}")]
HeaderValueParse(&'static str, String),
#[error("Invalid parameter value: {0}")]
InvalidParameter(&'static str),
#[error("Too many requests. Retry after {retry_after} seconds")]
TooManyRequests {
request_id: String,
retry_after: u64,
},
}
pub(crate) fn extract_api_error_details(
response: &Response,
) -> (String, Option<String>, Option<String>) {
let request_id = response
.headers()
.get("Request-ID")
.and_then(|v| v.to_str().ok())
.unwrap_or("Unknown")
.to_string();
let error_code = response
.headers()
.get("Error-Code")
.and_then(|v| v.to_str().ok())
.map(String::from);
let error_message = response
.headers()
.get("Error-Message")
.and_then(|v| v.to_str().ok())
.map(String::from);
(request_id, error_code, error_message)
}
pub(crate) async fn check_status(
response: Response,
expected_statuses: &[StatusCode],
) -> Result<Response, IntelApiError> {
let status = response.status();
if expected_statuses.contains(&status) {
Ok(response)
} else if status == StatusCode::TOO_MANY_REQUESTS {
let request_id = response
.headers()
.get("Request-ID")
.and_then(|v| v.to_str().ok())
.unwrap_or("Unknown")
.to_string();
let retry_after = response
.headers()
.get("Retry-After")
.and_then(|v| v.to_str().ok())
.and_then(|v| v.parse::<u64>().ok())
.unwrap_or(60);
Err(IntelApiError::TooManyRequests {
request_id,
retry_after,
})
} else {
let (request_id, error_code, error_message) = extract_api_error_details(&response);
Err(IntelApiError::ApiError {
status,
request_id,
error_code,
error_message,
})
}
}