use crate::i18n::Lang;
use crate::tr_in;
use serde_json::Value;
#[derive(Debug)]
pub struct HttpError {
pub status: u16,
pub message: String,
}
impl std::fmt::Display for HttpError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.message)
}
}
impl std::error::Error for HttpError {}
#[derive(Debug)]
pub struct Refused(pub String);
impl std::fmt::Display for Refused {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.0)
}
}
impl std::error::Error for Refused {}
pub fn http_error_in(lang: Lang, name: &str, status: u16, body: &str) -> String {
let tail = said(body).map(|m| format!(" — {m}")).unwrap_or_default();
match status {
401 => tr_in!(
lang,
"{name} refuse ces identifiants (401){tail}",
"{name} refuses these credentials (401){tail}"
),
403 => tr_in!(
lang,
"{name} refuse l'accès (403){tail}",
"{name} denies access (403){tail}"
),
404 => tr_in!(
lang,
"{name} : introuvable (404){tail}",
"{name}: not found (404){tail}"
),
429 => tr_in!(
lang,
"{name} limite les requêtes (429) : réessaie dans un moment",
"{name} limits the requests (429): try again in a moment"
),
_ => tr_in!(
lang,
"{name} : erreur {status}{tail}",
"{name}: error {status}{tail}"
),
}
}
pub fn said(body: &str) -> Option<String> {
let body = body.trim();
let msg = match serde_json::from_str::<Value>(body) {
Ok(Value::String(s)) => s,
Ok(v) => v["errorMessages"][0]
.as_str()
.map(str::to_string)
.or_else(|| {
v["errors"]
.as_object()
.and_then(|o| o.values().find_map(|e| e.as_str().map(str::to_string)))
})
.or_else(|| v["errors"][0]["message"].as_str().map(str::to_string))
.or_else(|| v["message"].as_str().map(str::to_string))
.or_else(|| v["error"].as_str().map(str::to_string))
.or_else(|| v["error"]["message"].as_str().map(str::to_string))
.or_else(|| v["err"].as_str().map(str::to_string))?,
Err(_) if body.starts_with('<') => return None,
Err(_) => body.lines().next()?.to_string(),
};
let msg = msg.trim();
(!msg.is_empty()).then(|| first_line(msg))
}
pub fn first_line(e: &str) -> String {
let line = e
.lines()
.map(str::trim)
.find(|l| !l.is_empty())
.map(str::to_string)
.unwrap_or_else(|| crate::tr!("erreur inconnue", "unknown error"));
truncate(&line, 200)
}
pub fn truncate(s: &str, max: usize) -> String {
if s.len() <= max {
return s.to_string();
}
let mut end = max;
while !s.is_char_boundary(end) {
end -= 1;
}
format!("{}…", &s[..end])
}