use std::str::FromStr;
use once_cell::sync::Lazy;
use regex::Regex;
use reqwest::Response;
use serde::{de::DeserializeOwned, Deserialize};
use strum::EnumString;
use tracing::{trace, warn};
#[derive(Debug, Deserialize, PartialEq, EnumString)]
pub enum Exception {
UniqueFileException,
BadCredentialsException,
CorruptUploadOpenApiException,
NoSuchFileException,
NoSuchPathException,
InvalidArgumentException,
IncompleteUploadOpenApiException,
RequestedRangeNotSatisfiedException,
}
#[derive(Debug, Deserialize)]
pub struct JsonErrorBody {
pub code: Option<u16>,
pub message: Option<String>,
pub cause: Option<String>,
pub error_id: Option<MaybeUnknown<Exception>>,
#[serde(rename(deserialize = "x-id"))]
pub x_id: Option<String>,
}
#[derive(Debug, Deserialize)]
pub struct JavaErrorMessage(pub String);
impl JavaErrorMessage {
#[must_use]
pub fn exception_opt(&self) -> Option<Exception> {
static RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"^(?:\w+\.)*(?P<except>\w+)").unwrap());
let captures = RE.captures(&self.0)?;
let exception_str = captures.name("except")?.as_str();
match Exception::from_str(exception_str) {
Ok(exception) => Some(exception),
Err(err) => {
warn!(
"parse upstream exception failed: {:?} (from {})",
err, &self.0
);
None
}
}
}
}
#[derive(Debug, Deserialize)]
pub struct XmlErrorBody {
pub code: u16,
pub message: Option<JavaErrorMessage>,
pub reason: String,
pub cause: Option<String>,
pub hostname: Option<String>,
#[serde(rename(deserialize = "x-id"))]
pub x_id: Option<String>,
}
impl XmlErrorBody {
#[must_use]
pub fn exception_opt(&self) -> Option<Exception> {
self.message.as_ref()?.exception_opt()
}
}
pub(crate) async fn read_json<T: DeserializeOwned>(
res: Response,
) -> reqwest::Result<Result<T, JsonErrorBody>> {
if res.status().is_success() {
res.json().await.map(Ok)
} else {
res.json().await.map(Err)
}
}
pub(crate) async fn read_xml<T: DeserializeOwned>(res: Response) -> crate::Result<T> {
let status = res.status();
let xml = res.text().await?;
trace!("{}", xml);
if status.is_success() {
let data = serde_xml_rs::from_str(&xml)?;
Ok(data)
} else {
let e: XmlErrorBody = serde_xml_rs::from_str(&xml)?;
Err(e.into())
}
}
#[derive(Debug, Deserialize)]
#[serde(untagged)]
pub enum MaybeUnknown<T> {
Known(T),
Unknown(String),
}