use serde::de::DeserializeOwned;
use serde::{Deserialize, Deserializer};
use super::Error;
#[derive(Debug, Deserialize)]
pub struct ApiResponse<T> {
#[serde(rename = "@Status")]
pub status: String,
#[serde(rename = "Errors", default)]
pub errors: ApiErrors,
#[serde(rename = "CommandResponse")]
pub command_response: Option<T>,
}
#[derive(Debug, Default, Deserialize)]
pub struct ApiErrors {
#[serde(rename = "Error", default)]
pub errors: Vec<ApiError>,
}
#[derive(Debug, Deserialize)]
pub struct ApiError {
#[serde(rename = "@Number", default)]
pub number: String,
#[serde(rename = "$text", default)]
pub message: String,
}
pub fn parse<T: DeserializeOwned>(body: &str) -> Result<T, Error> {
let resp: ApiResponse<T> =
quick_xml::de::from_str(body).map_err(|e| Error::Parse(format!("malformed XML: {e}")))?;
if resp.status.eq_ignore_ascii_case("ok") {
resp.command_response
.ok_or_else(|| Error::Parse("Status=OK but no CommandResponse element".into()))
} else {
let (code, mut message) = resp
.errors
.errors
.first()
.map(|e| (e.number.clone(), e.message.trim().to_owned()))
.unwrap_or_else(|| ("unknown".into(), "no error detail in response".into()));
if code == "1011150" {
message.push_str(
" — Namecheap rejected the request IP: check the client_ip config value, \
your current outbound IPv4, and the Namecheap whitelist",
);
}
Err(Error::Api { code, message })
}
}
pub fn de_bool<'de, D: Deserializer<'de>>(d: D) -> Result<bool, D::Error> {
let s = String::deserialize(d)?;
match s.to_ascii_lowercase().as_str() {
"true" | "1" => Ok(true),
"false" | "0" => Ok(false),
other => Err(serde::de::Error::custom(format!(
"expected boolean, got {other:?}"
))),
}
}