1use std::convert::Infallible;
6use std::str::FromStr;
7use std::{fmt, io};
8
9use async_trait::async_trait;
10use destream::{de, en};
11
12pub type TCResult<T> = Result<T, TCError>;
14
15#[derive(Clone)]
16struct ErrorData {
17 message: String,
18 stack: Vec<String>,
19}
20
21struct DataVisitor;
22
23#[async_trait]
24impl de::Visitor for DataVisitor {
25 type Value = ErrorData;
26
27 fn expecting() -> &'static str {
28 "an error message and optional stacktrace"
29 }
30
31 async fn visit_map<A: de::MapAccess>(self, mut access: A) -> Result<Self::Value, A::Error> {
32 let message = if let Some(key) = access.next_key::<String>(()).await? {
33 if key == "message" {
34 access.next_value(()).await
35 } else {
36 Err(de::Error::invalid_value(key, "message"))
37 }
38 } else {
39 Err(de::Error::invalid_length(0, Self::expecting()))
40 }?;
41
42 let stack = if let Some(key) = access.next_key::<String>(()).await? {
43 if key == "stack" {
44 access.next_value(()).await
45 } else {
46 Err(de::Error::invalid_value(key, "stack"))
47 }
48 } else {
49 Ok(Default::default())
50 }?;
51
52 Ok(ErrorData { message, stack })
53 }
54
55 fn visit_string<E: de::Error>(self, message: String) -> Result<Self::Value, E> {
56 Ok(ErrorData {
57 message,
58 stack: vec![],
59 })
60 }
61}
62
63#[async_trait]
64impl de::FromStream for ErrorData {
65 type Context = ();
66
67 async fn from_stream<D: de::Decoder>(_: (), decoder: &mut D) -> Result<Self, D::Error> {
68 decoder.decode_any(DataVisitor).await
69 }
70}
71
72impl<'en> en::IntoStream<'en> for ErrorData {
73 fn into_stream<E: en::Encoder<'en>>(self, encoder: E) -> Result<E::Ok, E::Error> {
74 if self.stack.is_empty() {
75 return en::IntoStream::into_stream(self.message, encoder);
76 }
77
78 use en::EncodeMap;
79
80 let mut map = encoder.encode_map(Some(2))?;
81 map.encode_entry("message", self.message)?;
82 map.encode_entry("stack", self.stack)?;
83 map.end()
84 }
85}
86
87impl<'en> en::ToStream<'en> for ErrorData {
88 fn to_stream<E: en::Encoder<'en>>(&'en self, encoder: E) -> Result<E::Ok, E::Error> {
89 if self.stack.is_empty() {
90 return en::ToStream::to_stream(&self.message, encoder);
91 }
92
93 use en::EncodeMap;
94
95 let mut map = encoder.encode_map(Some(2))?;
96 map.encode_entry("message", &self.message)?;
97 map.end()
98 }
99}
100
101impl<T> From<T> for ErrorData
102where
103 T: fmt::Display,
104{
105 fn from(message: T) -> Self {
106 Self {
107 message: message.to_string(),
108 stack: vec![],
109 }
110 }
111}
112
113#[derive(Clone, Copy, Eq, PartialEq)]
115pub enum ErrorKind {
116 BadGateway,
117 BadRequest,
118 Conflict,
119 Forbidden,
120 Internal,
121 MethodNotAllowed,
122 NotFound,
123 NotImplemented,
124 Timeout,
125 Unauthorized,
126 Unavailable,
127}
128
129impl ErrorKind {
130 pub fn is_conflict(&self) -> bool {
131 *self == Self::Conflict
132 }
133
134 pub fn is_retriable(&self) -> bool {
135 [Self::Timeout, Self::Unavailable]
136 .into_iter()
137 .any(|code| *self == code)
138 }
139}
140
141impl FromStr for ErrorKind {
142 type Err = TCError;
143
144 fn from_str(s: &str) -> Result<Self, Self::Err> {
145 match s {
146 "bad_gateway" => Ok(Self::BadGateway),
147 "bad_request" => Ok(Self::BadRequest),
148 "conflict" => Ok(Self::Conflict),
149 "forbidden" => Ok(Self::Forbidden),
150 "internal_error" => Ok(Self::Internal),
151 "method_not_allowed" => Ok(Self::MethodNotAllowed),
152 "not_found" => Ok(Self::NotFound),
153 "not_implemented" => Ok(Self::NotImplemented),
154 "request_timeout" => Ok(Self::Timeout),
155 "unauthorized" => Ok(Self::Unauthorized),
156 "temporarily_unavailable" => Ok(Self::Unavailable),
157 other => Err(bad_request!("unrecognized error code: {other}")),
158 }
159 }
160}
161
162#[async_trait]
163impl de::FromStream for ErrorKind {
164 type Context = ();
165
166 async fn from_stream<D: de::Decoder>(cxt: (), decoder: &mut D) -> Result<Self, D::Error> {
167 let code = String::from_stream(cxt, decoder).await?;
168 code.parse()
169 .map_err(|_| de::Error::invalid_value(code, "an error code"))
170 }
171}
172
173impl<'en> en::IntoStream<'en> for ErrorKind {
174 fn into_stream<E: en::Encoder<'en>>(self, encoder: E) -> Result<E::Ok, E::Error> {
175 self.to_string().into_stream(encoder)
176 }
177}
178
179impl fmt::Debug for ErrorKind {
180 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
181 write!(f, "{}", self.to_string().replace("_", " "))
182 }
183}
184
185impl fmt::Display for ErrorKind {
186 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
187 f.write_str(match self {
188 Self::BadGateway => "bad_gateway",
189 Self::BadRequest => "bad_request",
190 Self::Conflict => "conflict",
191 Self::Forbidden => "forbidden",
192 Self::Internal => "internal_error",
193 Self::MethodNotAllowed => "method_not_allowed",
194 Self::NotFound => "not_found",
195 Self::NotImplemented => "not_implemented",
196 Self::Timeout => "request_timeout",
197 Self::Unauthorized => "unauthorized",
198 Self::Unavailable => "temporarily_unavailable",
199 })
200 }
201}
202
203#[derive(Clone)]
205pub struct TCError {
206 kind: ErrorKind,
207 data: ErrorData,
208}
209
210impl TCError {
211 pub fn new<I: fmt::Display>(code: ErrorKind, message: I) -> Self {
213 #[cfg(debug_assertions)]
214 match code {
215 ErrorKind::Internal | ErrorKind::MethodNotAllowed | ErrorKind::NotImplemented => {
216 panic!("{code}: {message}")
217 }
218 other => log::warn!("{other}: {message}"),
219 }
220
221 Self {
222 kind: code,
223 data: message.into(),
224 }
225 }
226
227 pub fn with_stack<I, S, SI>(code: ErrorKind, message: I, stack: S) -> Self
229 where
230 I: fmt::Display,
231 SI: fmt::Display,
232 S: IntoIterator<Item = SI>,
233 {
234 let stack = stack.into_iter().map(|msg| msg.to_string()).collect();
235
236 #[cfg(debug_assertions)]
237 match code {
238 ErrorKind::Internal | ErrorKind::MethodNotAllowed | ErrorKind::NotImplemented => {
239 panic!("{code}: {message} (cause: {stack:?})")
240 }
241 other => log::warn!("{other}: {message} (cause: {stack:?})"),
242 }
243
244 Self {
245 kind: code,
246 data: ErrorData {
247 message: message.to_string(),
248 stack,
249 },
250 }
251 }
252
253 pub fn bad_request<I: fmt::Display>(info: I) -> Self {
255 Self::new(ErrorKind::BadGateway, info)
256 }
257
258 pub fn bad_gateway<I: fmt::Display>(locator: I) -> Self {
260 Self::new(ErrorKind::BadGateway, locator)
261 }
262
263 pub fn conflict<I: fmt::Display>(info: I) -> Self {
265 #[cfg(debug_assertions)]
266 panic!("conflict: {info}");
267
268 #[cfg(not(debug_assertions))]
269 Self::new(ErrorKind::Conflict, info)
270 }
271
272 pub fn internal<I: fmt::Display>(info: I) -> Self {
274 #[cfg(debug_assertions)]
275 panic!("internal error: {info}");
276
277 #[cfg(not(debug_assertions))]
278 Self::new(ErrorKind::Internal, info)
279 }
280
281 pub fn method_not_allowed<M: fmt::Debug, P: fmt::Display>(method: M, path: P) -> Self {
283 let message = format!("endpoint {} does not support {:?}", path, method);
284
285 #[cfg(debug_assertions)]
286 panic!("{message}");
287
288 #[cfg(not(debug_assertions))]
289 Self::new(ErrorKind::MethodNotAllowed, message)
290 }
291
292 pub fn not_found<I: fmt::Display>(locator: I) -> Self {
294 Self::new(ErrorKind::NotFound, locator)
295 }
296
297 pub fn unauthorized<I: fmt::Display>(info: I) -> Self {
299 Self::new(ErrorKind::Unauthorized, info)
300 }
301
302 pub fn unexpected<V: fmt::Debug>(value: V, expected: &str) -> Self {
304 Self::bad_request(format!("invalid value {value:?}: expected {expected}"))
305 }
306
307 pub fn unsupported<I: fmt::Display>(info: I) -> Self {
309 Self::bad_request(info)
310 }
311
312 pub fn code(&self) -> ErrorKind {
314 self.kind
315 }
316
317 pub fn message(&self) -> &str {
319 &self.data.message
320 }
321
322 pub fn consume<I: fmt::Debug>(mut self, cause: I) -> Self {
324 self.data.stack.push(format!("{:?}", cause));
325 self
326 }
327}
328
329impl std::error::Error for TCError {}
330
331impl From<pathlink::ParseError> for TCError {
332 fn from(err: pathlink::ParseError) -> Self {
333 Self::bad_request(err)
334 }
335}
336
337#[cfg(feature = "ha-ndarray")]
338impl From<ha_ndarray::Error> for TCError {
339 fn from(err: ha_ndarray::Error) -> Self {
340 Self::new(ErrorKind::Internal, err)
341 }
342}
343
344#[cfg(feature = "rjwt")]
345impl From<rjwt::Error> for TCError {
346 fn from(err: rjwt::Error) -> Self {
347 #[cfg(debug_assertions)]
348 panic!("rjwt error: {err}");
349
350 #[cfg(not(debug_assertions))]
351 match err.into_inner() {
352 (rjwt::ErrorKind::Auth | rjwt::ErrorKind::Time, msg) => Self::unauthorized(msg),
353 (rjwt::ErrorKind::Base64 | rjwt::ErrorKind::Format | rjwt::ErrorKind::Json, msg) => {
354 Self::bad_request(msg)
355 }
356 (rjwt::ErrorKind::Fetch, msg) => Self::bad_gateway(msg),
357 }
358 }
359}
360
361#[cfg(feature = "txn_lock")]
362impl From<txn_lock::Error> for TCError {
363 fn from(err: txn_lock::Error) -> Self {
364 Self::conflict(err)
365 }
366}
367
368#[cfg(feature = "txfs")]
369impl From<txfs::Error> for TCError {
370 fn from(cause: txfs::Error) -> Self {
371 match cause {
372 txfs::Error::Conflict(cause) => Self::conflict(cause),
373 txfs::Error::IO(cause) => Self::from(cause),
374 txfs::Error::NotFound(cause) => Self::not_found(cause),
375 txfs::Error::Parse(cause) => Self::from(cause),
376 }
377 }
378}
379
380impl From<io::Error> for TCError {
381 fn from(cause: io::Error) -> Self {
382 match cause.kind() {
383 io::ErrorKind::AlreadyExists => {
384 #[cfg(debug_assertions)]
385 panic!("tried to create an entry that already exists: {}", cause);
386
387 #[cfg(not(debug_assertions))]
388 bad_request!("tried to create an entry that already exists").consume(cause)
389 }
390 io::ErrorKind::InvalidInput => bad_request!("{}", cause),
391 io::ErrorKind::NotFound => TCError::not_found(cause),
392 io::ErrorKind::PermissionDenied => {
393 bad_gateway!("host filesystem permission denied").consume(cause)
394 }
395 io::ErrorKind::WouldBlock => {
396 conflict!("synchronous filesystem access failed").consume(cause)
397 }
398 kind => internal!("host filesystem error: {:?}", kind).consume(cause),
399 }
400 }
401}
402
403impl From<Infallible> for TCError {
404 fn from(_: Infallible) -> Self {
405 internal!("an unanticipated error occurred--please file a bug report")
406 }
407}
408
409struct ErrorVisitor;
410
411#[async_trait]
412impl de::Visitor for ErrorVisitor {
413 type Value = TCError;
414
415 fn expecting() -> &'static str {
416 "an error code, message, and optional stacktrace"
417 }
418
419 async fn visit_map<A: de::MapAccess>(self, mut access: A) -> Result<Self::Value, A::Error> {
420 if let Some(kind) = access.next_key(()).await? {
421 let data = access.next_value(()).await?;
422 Ok(TCError { kind, data })
423 } else {
424 Err(de::Error::invalid_length(0, Self::expecting()))
425 }
426 }
427}
428
429#[async_trait]
430impl de::FromStream for TCError {
431 type Context = ();
432
433 async fn from_stream<D: de::Decoder>(_: (), decoder: &mut D) -> Result<Self, D::Error> {
434 decoder.decode_map(ErrorVisitor).await
435 }
436}
437
438impl<'en> en::IntoStream<'en> for TCError {
439 fn into_stream<E: en::Encoder<'en>>(self, encoder: E) -> Result<E::Ok, E::Error> {
440 use en::EncodeMap;
441 let mut map = encoder.encode_map(Some(1))?;
442 map.encode_entry(self.kind, self.data)?;
443 map.end()
444 }
445}
446
447impl<'en> en::ToStream<'en> for TCError {
448 fn to_stream<E: en::Encoder<'en>>(&'en self, encoder: E) -> Result<E::Ok, E::Error> {
449 use en::EncodeMap;
450 let mut map = encoder.encode_map(Some(1))?;
451 map.encode_entry(self.kind, &self.data)?;
452 map.end()
453 }
454}
455
456impl fmt::Debug for TCError {
457 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
458 fmt::Display::fmt(self, f)
459 }
460}
461
462impl fmt::Display for TCError {
463 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
464 write!(f, "{}: {}", self.kind, self.data.message)
465 }
466}
467
468#[macro_export]
470macro_rules! bad_gateway {
471 ($($t:tt)*) => {{
472 $crate::TCError::new($crate::ErrorKind::BadGateway, format!($($t)*))
473 }}
474}
475
476#[macro_export]
478macro_rules! bad_request {
479 ($($t:tt)*) => {{
480 $crate::TCError::bad_request(format!($($t)*))
481 }}
482}
483
484#[macro_export]
486macro_rules! conflict {
487 ($($t:tt)*) => {{
488 $crate::TCError::conflict(format!($($t)*))
489 }}
490}
491
492#[macro_export]
494macro_rules! forbidden {
495 ($($t:tt)*) => {{
496 $crate::TCError::new($crate::ErrorKind::Unauthorized, format!($($t)*))
497 }}
498}
499
500#[macro_export]
502macro_rules! not_found {
503 ($($t:tt)*) => {{
504 $crate::TCError::not_found(format!($($t)*))
505 }}
506}
507
508#[macro_export]
510macro_rules! not_implemented {
511 ($($t:tt)*) => {{
512 $crate::TCError::new($crate::ErrorKind::NotImplemented, format!($($t)*))
513 }}
514}
515
516#[macro_export]
518macro_rules! timeout {
519 ($($t:tt)*) => {{
520 $crate::TCError::new($crate::ErrorKind::Timeout, format!($($t)*))
521 }}
522}
523
524#[macro_export]
526macro_rules! internal {
527 ($($t:tt)*) => {{
528 $crate::TCError::internal(format!($($t)*))
529 }}
530}
531
532#[macro_export]
534macro_rules! unauthorized {
535 ($($t:tt)*) => {{
536 $crate::TCError::unauthorized(format!($($t)*))
537 }}
538}
539
540#[macro_export]
542macro_rules! unavailable {
543 ($($t:tt)*) => {{
544 $crate::TCError::new($crate::ErrorKind::Unavailable, format!($($t)*))
545 }}
546}