use serde_json::Value;
#[derive(Debug, thiserror::Error)]
pub enum ClientError {
#[error("network: {0}")]
Transport(String),
#[error("authentication: {0}")]
Auth(String),
#[error("not found: {0}")]
NotFound(String),
#[error("invalid request: {0}")]
InvalidRequest(String),
#[error("server rejected (HTTP {status}): {}", extract_message(.body))]
Server { status: u16, body: Value },
#[error("configuration: {0}")]
Config(String),
#[error("websocket: {0}")]
Ws(String),
#[error("server does not speak delivery protocol v{client_version} (close 4426)")]
ProtocolVersion { client_version: u32 },
}
impl ClientError {
pub(crate) fn from_reqwest(err: reqwest::Error) -> Self {
ClientError::Transport(err.to_string())
}
pub fn from_status(status: u16, body: Value) -> Self {
match status {
401 | 403 => ClientError::Auth(extract_message(&body)),
404 => ClientError::NotFound(extract_message(&body)),
400 | 422 => ClientError::InvalidRequest(extract_message(&body)),
_ => ClientError::Server { status, body },
}
}
pub fn code(&self) -> Option<&str> {
match self {
ClientError::Server { body, .. } => body.get("code").and_then(|c| c.as_str()),
_ => None,
}
}
}
pub(crate) fn extract_message(body: &Value) -> String {
body.get("error")
.and_then(|m| m.as_str())
.map(String::from)
.unwrap_or_else(|| body.to_string())
}