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_REQUEST_TOO_LARGE: &str = "request_too_large";
24pub const ERROR_BAD_CHUNK: &str = "bad_chunk";
27pub const ERROR_CONNECTION_CLOSED: &str = "connection_closed";
29pub const ERROR_TIMEOUT: &str = "request_timeout";
33pub const ERROR_UNAVAILABLE: &str = "service_unavailable";
35
36pub const ERROR_UNAUTHENTICATED: &str = "unauthenticated";
38pub const ERROR_FORBIDDEN: &str = "forbidden";
40pub const ERROR_REVISION_CONFLICT: &str = "revision_conflict";
42pub const ERROR_RATE_LIMITED: &str = "rate_limited";
46
47#[derive(Debug, Clone, PartialEq, Eq)]
49pub struct ServiceError {
50 pub code: String,
51 pub message: String,
52 pub retryable: bool,
53}
54
55impl ServiceError {
56 pub fn new(code: impl Into<String>, message: impl Into<String>) -> Self {
57 Self {
58 code: code.into(),
59 message: message.into(),
60 retryable: false,
61 }
62 }
63 pub fn retryable(code: impl Into<String>, message: impl Into<String>) -> Self {
64 Self {
65 code: code.into(),
66 message: message.into(),
67 retryable: true,
68 }
69 }
70 pub fn handoff() -> Self {
71 Self::retryable(
72 ERROR_HOST_HANDOFF,
73 "host is handing the service to a higher-ranked host; retry",
74 )
75 }
76 pub fn to_value(&self) -> Value {
77 json!({"code": self.code, "message": self.message, "retryable": self.retryable})
78 }
79 pub fn from_value(v: &Value) -> Self {
80 Self {
81 code: v["code"].as_str().unwrap_or("error").into(),
82 message: v["message"].as_str().unwrap_or("").into(),
83 retryable: v["retryable"].as_bool().unwrap_or(false),
84 }
85 }
86}
87
88impl fmt::Display for ServiceError {
89 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
90 write!(f, "{}: {}", self.code, self.message)
91 }
92}
93impl std::error::Error for ServiceError {}
94impl From<std::io::Error> for ServiceError {
95 fn from(e: std::io::Error) -> Self {
96 Self::new("io", e.to_string())
97 }
98}
99impl From<HostIdentityError> for ServiceError {
100 fn from(e: HostIdentityError) -> Self {
102 Self::new(ERROR_HOST_IDENTITY, e.to_string())
103 }
104}
105
106#[derive(Debug, Clone, PartialEq, Eq)]
113#[non_exhaustive]
114pub enum ConnectError {
115 Service(ServiceError),
116 HostIdentity(HostIdentityError),
117}
118
119impl ConnectError {
120 pub fn into_service_error(self) -> ServiceError {
123 match self {
124 ConnectError::Service(e) => e,
125 ConnectError::HostIdentity(e) => e.into(),
126 }
127 }
128}
129
130impl fmt::Display for ConnectError {
131 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
132 match self {
133 ConnectError::Service(e) => fmt::Display::fmt(e, f),
134 ConnectError::HostIdentity(e) => fmt::Display::fmt(e, f),
135 }
136 }
137}
138
139impl std::error::Error for ConnectError {
140 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
141 match self {
142 ConnectError::Service(e) => Some(e),
143 ConnectError::HostIdentity(e) => Some(e),
144 }
145 }
146}
147
148impl From<ServiceError> for ConnectError {
149 fn from(e: ServiceError) -> Self {
150 ConnectError::Service(e)
151 }
152}
153
154impl From<HostIdentityError> for ConnectError {
155 fn from(e: HostIdentityError) -> Self {
156 ConnectError::HostIdentity(e)
157 }
158}