1use crate::host_identity::HostIdentityError;
2use serde_json::{json, Value};
3use std::fmt;
4
5pub const ERROR_HOST_HANDOFF: &str = "host_handoff";
8pub const ERROR_HOST_IDENTITY: &str = "host_identity";
12pub const ERROR_SUITE_UNRESOLVED: &str = "suite_unresolved";
15pub const ERROR_NOT_ALLOWED: &str = "request_not_allowed";
17pub const ERROR_RESPONSE_TOO_LARGE: &str = "response_too_large";
20pub const ERROR_CONNECTION_CLOSED: &str = "connection_closed";
22pub const ERROR_TIMEOUT: &str = "request_timeout";
26pub const ERROR_UNAVAILABLE: &str = "service_unavailable";
28
29pub const ERROR_UNAUTHENTICATED: &str = "unauthenticated";
31pub const ERROR_FORBIDDEN: &str = "forbidden";
33pub const ERROR_REVISION_CONFLICT: &str = "revision_conflict";
35
36#[derive(Debug, Clone, PartialEq, Eq)]
38pub struct ServiceError {
39 pub code: String,
40 pub message: String,
41 pub retryable: bool,
42}
43
44impl ServiceError {
45 pub fn new(code: impl Into<String>, message: impl Into<String>) -> Self {
46 Self {
47 code: code.into(),
48 message: message.into(),
49 retryable: false,
50 }
51 }
52 pub fn retryable(code: impl Into<String>, message: impl Into<String>) -> Self {
53 Self {
54 code: code.into(),
55 message: message.into(),
56 retryable: true,
57 }
58 }
59 pub fn handoff() -> Self {
60 Self::retryable(
61 ERROR_HOST_HANDOFF,
62 "host is handing the service to a higher-ranked host; retry",
63 )
64 }
65 pub fn to_value(&self) -> Value {
66 json!({"code": self.code, "message": self.message, "retryable": self.retryable})
67 }
68 pub fn from_value(v: &Value) -> Self {
69 Self {
70 code: v["code"].as_str().unwrap_or("error").into(),
71 message: v["message"].as_str().unwrap_or("").into(),
72 retryable: v["retryable"].as_bool().unwrap_or(false),
73 }
74 }
75}
76
77impl fmt::Display for ServiceError {
78 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79 write!(f, "{}: {}", self.code, self.message)
80 }
81}
82impl std::error::Error for ServiceError {}
83impl From<std::io::Error> for ServiceError {
84 fn from(e: std::io::Error) -> Self {
85 Self::new("io", e.to_string())
86 }
87}
88impl From<HostIdentityError> for ServiceError {
89 fn from(e: HostIdentityError) -> Self {
91 Self::new(ERROR_HOST_IDENTITY, e.to_string())
92 }
93}
94
95#[derive(Debug, Clone, PartialEq, Eq)]
102#[non_exhaustive]
103pub enum ConnectError {
104 Service(ServiceError),
105 HostIdentity(HostIdentityError),
106}
107
108impl ConnectError {
109 pub fn into_service_error(self) -> ServiceError {
112 match self {
113 ConnectError::Service(e) => e,
114 ConnectError::HostIdentity(e) => e.into(),
115 }
116 }
117}
118
119impl fmt::Display for ConnectError {
120 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
121 match self {
122 ConnectError::Service(e) => fmt::Display::fmt(e, f),
123 ConnectError::HostIdentity(e) => fmt::Display::fmt(e, f),
124 }
125 }
126}
127
128impl std::error::Error for ConnectError {
129 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
130 match self {
131 ConnectError::Service(e) => Some(e),
132 ConnectError::HostIdentity(e) => Some(e),
133 }
134 }
135}
136
137impl From<ServiceError> for ConnectError {
138 fn from(e: ServiceError) -> Self {
139 ConnectError::Service(e)
140 }
141}
142
143impl From<HostIdentityError> for ConnectError {
144 fn from(e: HostIdentityError) -> Self {
145 ConnectError::HostIdentity(e)
146 }
147}