Skip to main content

zerokms_protocol/
error.rs

1use static_assertions::assert_impl_all;
2use std::error::Error as StdError;
3use thiserror::Error;
4
5pub trait RetryableError {
6    fn can_retry(&self) -> bool;
7}
8
9type ShareableError = Box<dyn StdError + Send + Sync>;
10
11#[derive(Debug)]
12pub enum ViturRequestErrorKind {
13    PrepareRequest,
14    SendRequest,
15    NotFound,
16    Conflict,
17    FailureResponse,
18    ParseResponse,
19    Unauthorized,
20    Forbidden,
21    Other,
22}
23
24impl std::fmt::Display for ViturRequestErrorKind {
25    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26        write!(
27            f,
28            "{}",
29            match self {
30                Self::PrepareRequest => "PrepareRequest",
31                Self::SendRequest => "SendRequest",
32                Self::NotFound => "NotFound",
33                Self::Conflict => "Conflict",
34                Self::FailureResponse => "FailureResponse",
35                Self::ParseResponse => "ParseResponse",
36                Self::Unauthorized => "Unauthorized",
37                Self::Forbidden => "Forbidden",
38                Self::Other => "Other",
39            }
40        )
41    }
42}
43
44#[derive(Debug, Error)]
45#[error("{kind}: {message}: {error}")]
46pub struct ViturRequestError {
47    pub kind: ViturRequestErrorKind,
48    pub message: &'static str,
49    pub error: ShareableError,
50    can_retry: bool,
51}
52
53// ViturRequestError should be able to be sent and shared between threads to be able to support
54// async as well as `anyhow!` for async.
55assert_impl_all!(ViturRequestError: Send, Sync);
56
57impl ViturRequestError {
58    #[inline]
59    pub fn new(
60        kind: ViturRequestErrorKind,
61        message: &'static str,
62        error: impl StdError + 'static + Send + Sync,
63    ) -> Self {
64        Self {
65            kind,
66            message,
67            error: Box::new(error),
68            can_retry: false,
69        }
70    }
71
72    #[inline]
73    pub fn prepare(message: &'static str, error: impl StdError + 'static + Send + Sync) -> Self {
74        Self {
75            kind: ViturRequestErrorKind::PrepareRequest,
76            message,
77            error: Box::new(error),
78            can_retry: false,
79        }
80    }
81
82    #[inline]
83    pub fn parse(message: &'static str, error: impl StdError + 'static + Send + Sync) -> Self {
84        Self {
85            kind: ViturRequestErrorKind::ParseResponse,
86            message,
87            error: Box::new(error),
88            can_retry: false,
89        }
90    }
91
92    #[inline]
93    pub fn send(message: &'static str, error: impl StdError + 'static + Send + Sync) -> Self {
94        Self {
95            kind: ViturRequestErrorKind::SendRequest,
96            message,
97            error: Box::new(error),
98            can_retry: false,
99        }
100    }
101
102    #[inline]
103    pub fn not_found(message: &'static str, error: impl StdError + 'static + Send + Sync) -> Self {
104        Self {
105            kind: ViturRequestErrorKind::NotFound,
106            message,
107            error: Box::new(error),
108            can_retry: false,
109        }
110    }
111
112    #[inline]
113    pub fn conflict(message: &'static str, error: impl StdError + 'static + Send + Sync) -> Self {
114        Self {
115            kind: ViturRequestErrorKind::Conflict,
116            message,
117            error: Box::new(error),
118            can_retry: false,
119        }
120    }
121
122    #[inline]
123    pub fn response(message: &'static str, error: impl StdError + 'static + Send + Sync) -> Self {
124        Self {
125            kind: ViturRequestErrorKind::FailureResponse,
126            message,
127            error: Box::new(error),
128            can_retry: false,
129        }
130    }
131
132    #[inline]
133    pub fn forbidden(message: &'static str, error: impl StdError + 'static + Send + Sync) -> Self {
134        Self {
135            kind: ViturRequestErrorKind::Forbidden,
136            message,
137            error: Box::new(error),
138            can_retry: false,
139        }
140    }
141
142    #[inline]
143    pub fn other(message: &'static str, error: impl StdError + 'static + Send + Sync) -> Self {
144        Self {
145            kind: ViturRequestErrorKind::Other,
146            message,
147            error: Box::new(error),
148            can_retry: false,
149        }
150    }
151
152    pub fn is_conflict(&self) -> bool {
153        matches!(self.kind, ViturRequestErrorKind::Conflict)
154    }
155
156    pub fn with_retryable(mut self, can_retry: bool) -> Self {
157        self.can_retry = can_retry;
158        self
159    }
160
161    pub fn retryable(self) -> Self {
162        self.with_retryable(true)
163    }
164}
165
166impl RetryableError for ViturRequestError {
167    fn can_retry(&self) -> bool {
168        self.can_retry
169    }
170}