use crate::host_identity::HostIdentityError;
use serde_json::{json, Value};
use std::fmt;
pub const ERROR_HOST_HANDOFF: &str = "host_handoff";
pub const ERROR_HOST_IDENTITY: &str = "host_identity";
pub const ERROR_SUITE_UNRESOLVED: &str = "suite_unresolved";
pub const ERROR_NOT_ALLOWED: &str = "request_not_allowed";
pub const ERROR_RESPONSE_TOO_LARGE: &str = "response_too_large";
pub const ERROR_REQUEST_TOO_LARGE: &str = "request_too_large";
pub const ERROR_BAD_CHUNK: &str = "bad_chunk";
pub const ERROR_CONNECTION_CLOSED: &str = "connection_closed";
pub const ERROR_TIMEOUT: &str = "request_timeout";
pub const ERROR_UNAVAILABLE: &str = "service_unavailable";
pub const ERROR_UNAUTHENTICATED: &str = "unauthenticated";
pub const ERROR_FORBIDDEN: &str = "forbidden";
pub const ERROR_REVISION_CONFLICT: &str = "revision_conflict";
pub const ERROR_RATE_LIMITED: &str = "rate_limited";
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ServiceError {
pub code: String,
pub message: String,
pub retryable: bool,
}
impl ServiceError {
pub fn new(code: impl Into<String>, message: impl Into<String>) -> Self {
Self {
code: code.into(),
message: message.into(),
retryable: false,
}
}
pub fn retryable(code: impl Into<String>, message: impl Into<String>) -> Self {
Self {
code: code.into(),
message: message.into(),
retryable: true,
}
}
pub fn handoff() -> Self {
Self::retryable(
ERROR_HOST_HANDOFF,
"host is handing the service to a higher-ranked host; retry",
)
}
pub fn to_value(&self) -> Value {
json!({"code": self.code, "message": self.message, "retryable": self.retryable})
}
pub fn from_value(v: &Value) -> Self {
Self {
code: v["code"].as_str().unwrap_or("error").into(),
message: v["message"].as_str().unwrap_or("").into(),
retryable: v["retryable"].as_bool().unwrap_or(false),
}
}
}
impl fmt::Display for ServiceError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}: {}", self.code, self.message)
}
}
impl std::error::Error for ServiceError {}
impl From<std::io::Error> for ServiceError {
fn from(e: std::io::Error) -> Self {
Self::new("io", e.to_string())
}
}
impl From<HostIdentityError> for ServiceError {
fn from(e: HostIdentityError) -> Self {
Self::new(ERROR_HOST_IDENTITY, e.to_string())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum ConnectError {
Service(ServiceError),
HostIdentity(HostIdentityError),
}
impl ConnectError {
pub fn into_service_error(self) -> ServiceError {
match self {
ConnectError::Service(e) => e,
ConnectError::HostIdentity(e) => e.into(),
}
}
}
impl fmt::Display for ConnectError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ConnectError::Service(e) => fmt::Display::fmt(e, f),
ConnectError::HostIdentity(e) => fmt::Display::fmt(e, f),
}
}
}
impl std::error::Error for ConnectError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
ConnectError::Service(e) => Some(e),
ConnectError::HostIdentity(e) => Some(e),
}
}
}
impl From<ServiceError> for ConnectError {
fn from(e: ServiceError) -> Self {
ConnectError::Service(e)
}
}
impl From<HostIdentityError> for ConnectError {
fn from(e: HostIdentityError) -> Self {
ConnectError::HostIdentity(e)
}
}