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