use serde::de::DeserializeOwned;
use crate::error::{ApiError, Error, ErrorCode};
#[derive(Debug, serde::Deserialize)]
pub(crate) struct SuccessEnvelope<T> {
#[serde(default)]
#[allow(dead_code)]
pub(crate) status: Option<String>,
#[serde(default)]
#[allow(dead_code)]
pub(crate) code: Option<u16>,
pub(crate) data: T,
}
#[derive(Debug, Default, serde::Deserialize)]
pub(crate) struct ErrorEnvelope {
#[serde(default)]
pub(crate) error_code: Option<String>,
#[serde(default)]
pub(crate) message: Option<String>,
}
pub(crate) async fn parse_response<T>(response: reqwest::Response) -> crate::Result<T>
where
T: DeserializeOwned,
{
let status = response.status();
let retry_after = response
.headers()
.get("X-Ratelimit-Reset")
.and_then(|v| v.to_str().ok())
.and_then(|s| s.parse::<u64>().ok());
let bytes = response.bytes().await.map_err(Error::from)?;
if status.is_success() {
tracing::debug!(?status, body_bytes = bytes.len(), "snippe response");
let envelope: SuccessEnvelope<T> =
serde_json::from_slice(&bytes).map_err(Error::Decode)?;
Ok(envelope.data)
} else {
let envelope: ErrorEnvelope =
serde_json::from_slice(&bytes).unwrap_or_default();
let raw_code = envelope.error_code.unwrap_or_default();
let error_code = ErrorCode::from_str(&raw_code);
let message = envelope.message.unwrap_or_else(|| {
String::from_utf8_lossy(&bytes).chars().take(500).collect()
});
tracing::warn!(?status, error_code = error_code.as_str(), %message, "snippe error response");
Err(Error::Api(ApiError {
status: status.as_u16(),
error_code,
message,
retry_after,
}))
}
}