crdb_core/
error.rs

1use crate::{BinPtr, EventId, ObjectId, QueryId, SessionToken, TypeId};
2use anyhow::anyhow;
3use web_time::SystemTime;
4
5pub type Result<T> = std::result::Result<T, Error>;
6
7#[derive(Debug, thiserror::Error)]
8#[non_exhaustive]
9pub enum Error {
10    #[error("Missing binary pointers: {0:?}")]
11    MissingBinaries(Vec<BinPtr>),
12
13    #[error("{0:?} already exists with a different value set")]
14    ObjectAlreadyExists(ObjectId),
15
16    #[error("Event {0:?} already exists with a different value set")]
17    EventAlreadyExists(EventId),
18
19    #[error("{0:?} does not exist in database")]
20    ObjectDoesNotExist(ObjectId),
21
22    #[error("{0:?} does not exist in database")]
23    QueryDoesNotExist(QueryId),
24
25    #[error("{0:?} does not exist in datapase")]
26    TypeDoesNotExist(TypeId),
27
28    #[error("{0:?} is not the hash of the provided value")]
29    BinaryHashMismatch(BinPtr),
30
31    #[error("Null byte in provided string")]
32    NullByteInString,
33
34    #[error("Invalid token: {0:?}")]
35    InvalidToken(SessionToken),
36
37    #[error("Invalid number provided")]
38    InvalidNumber,
39
40    #[error("Invalid time")]
41    InvalidTime(SystemTime),
42
43    #[error(
44        "{event_id:?} is too early to be submitted on {object_id:?} created at {created_at:?}"
45    )]
46    EventTooEarly {
47        event_id: EventId,
48        object_id: ObjectId,
49        created_at: EventId,
50    },
51
52    #[error("{object_id:?} has type {real_type_id:?} and not expected type {expected_type_id:?}")]
53    WrongType {
54        object_id: ObjectId,
55        expected_type_id: TypeId,
56        real_type_id: TypeId,
57    },
58
59    #[error("Lost connection while request was in progress")]
60    ConnectionLoss,
61
62    #[error("Client violated the protocol")]
63    ProtocolViolation,
64
65    #[error("User is not allowed to perform this action")]
66    Forbidden,
67
68    #[error(transparent)]
69    Other(anyhow::Error),
70}
71
72#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, thiserror::Error)]
73#[non_exhaustive]
74pub enum SerializableError {
75    #[error("Missing binary pointers: {0:?}")]
76    MissingBinaries(Vec<BinPtr>),
77
78    #[error("{0:?} already exists with a different value set")]
79    ObjectAlreadyExists(ObjectId),
80
81    #[error("Event {0:?} already exists with a different value set")]
82    EventAlreadyExists(EventId),
83
84    #[error("{0:?} does not exist in database")]
85    ObjectDoesNotExist(ObjectId),
86
87    #[error("{0:?} does not exist in database")]
88    QueryDoesNotExist(QueryId),
89
90    #[error("{0:?} does not exist in datapase")]
91    TypeDoesNotExist(TypeId),
92
93    #[error("{0:?} is not the hash of the provided value")]
94    BinaryHashMismatch(BinPtr),
95
96    #[error("Null byte in provided string")]
97    NullByteInString,
98
99    #[error("Invalid token: {0:?}")]
100    InvalidToken(SessionToken),
101
102    #[error("Invalid number provided")]
103    InvalidNumber,
104
105    #[error("Invalid time")]
106    InvalidTime(SystemTime),
107
108    #[error(
109        "{event_id:?} is too early to be submitted on {object_id:?} created at {created_at:?}"
110    )]
111    EventTooEarly {
112        event_id: EventId,
113        object_id: ObjectId,
114        created_at: EventId,
115    },
116
117    #[error("{object_id:?} has type {real_type_id:?} and not expected type {expected_type_id:?}")]
118    WrongType {
119        object_id: ObjectId,
120        expected_type_id: TypeId,
121        real_type_id: TypeId,
122    },
123
124    #[error("Lost connection while request was in progress")]
125    ConnectionLoss,
126
127    #[error("Client violated the protocol")]
128    ProtocolViolation,
129
130    #[error("User is not allowed to perform this action")]
131    Forbidden,
132
133    #[error("Internal server error")]
134    InternalServerError,
135}
136
137impl From<Error> for SerializableError {
138    fn from(err: Error) -> SerializableError {
139        match err {
140            Error::MissingBinaries(e) => SerializableError::MissingBinaries(e),
141            Error::ObjectAlreadyExists(e) => SerializableError::ObjectAlreadyExists(e),
142            Error::EventAlreadyExists(e) => SerializableError::EventAlreadyExists(e),
143            Error::ObjectDoesNotExist(e) => SerializableError::ObjectDoesNotExist(e),
144            Error::QueryDoesNotExist(e) => SerializableError::QueryDoesNotExist(e),
145            Error::TypeDoesNotExist(e) => SerializableError::TypeDoesNotExist(e),
146            Error::BinaryHashMismatch(e) => SerializableError::BinaryHashMismatch(e),
147            Error::NullByteInString => SerializableError::NullByteInString,
148            Error::InvalidToken(e) => SerializableError::InvalidToken(e),
149            Error::InvalidNumber => SerializableError::InvalidNumber,
150            Error::InvalidTime(t) => SerializableError::InvalidTime(t),
151            Error::EventTooEarly {
152                event_id,
153                object_id,
154                created_at,
155            } => SerializableError::EventTooEarly {
156                event_id,
157                object_id,
158                created_at,
159            },
160            Error::WrongType {
161                object_id,
162                expected_type_id,
163                real_type_id,
164            } => SerializableError::WrongType {
165                object_id,
166                expected_type_id,
167                real_type_id,
168            },
169            Error::ConnectionLoss => SerializableError::ConnectionLoss,
170            Error::ProtocolViolation => SerializableError::ProtocolViolation,
171            Error::Forbidden => SerializableError::Forbidden,
172            Error::Other(err) => {
173                tracing::error!(?err, "returning internal server error to client");
174                SerializableError::InternalServerError
175            }
176        }
177    }
178}
179
180impl From<SerializableError> for Error {
181    fn from(err: SerializableError) -> Error {
182        match err {
183            SerializableError::MissingBinaries(e) => Error::MissingBinaries(e),
184            SerializableError::ObjectAlreadyExists(e) => Error::ObjectAlreadyExists(e),
185            SerializableError::EventAlreadyExists(e) => Error::EventAlreadyExists(e),
186            SerializableError::ObjectDoesNotExist(e) => Error::ObjectDoesNotExist(e),
187            SerializableError::QueryDoesNotExist(e) => Error::QueryDoesNotExist(e),
188            SerializableError::TypeDoesNotExist(e) => Error::TypeDoesNotExist(e),
189            SerializableError::BinaryHashMismatch(e) => Error::BinaryHashMismatch(e),
190            SerializableError::NullByteInString => Error::NullByteInString,
191            SerializableError::InvalidToken(e) => Error::InvalidToken(e),
192            SerializableError::InvalidNumber => Error::InvalidNumber,
193            SerializableError::InvalidTime(t) => Error::InvalidTime(t),
194            SerializableError::EventTooEarly {
195                event_id,
196                object_id,
197                created_at,
198            } => Error::EventTooEarly {
199                event_id,
200                object_id,
201                created_at,
202            },
203            SerializableError::WrongType {
204                object_id,
205                expected_type_id,
206                real_type_id,
207            } => Error::WrongType {
208                object_id,
209                expected_type_id,
210                real_type_id,
211            },
212            SerializableError::ConnectionLoss => Error::ConnectionLoss,
213            SerializableError::ProtocolViolation => Error::ProtocolViolation,
214            SerializableError::Forbidden => Error::Forbidden,
215            SerializableError::InternalServerError => {
216                Error::Other(anyhow!("Internal server error"))
217            }
218        }
219    }
220}
221
222pub trait ResultExt: Sized {
223    type Ok;
224
225    fn wrap_with_context(self, f: impl FnOnce() -> String) -> Result<Self::Ok>;
226
227    fn wrap_context(self, s: &str) -> Result<Self::Ok> {
228        self.wrap_with_context(|| s.to_string())
229    }
230}
231
232impl<T> ResultExt for Result<T> {
233    type Ok = T;
234
235    fn wrap_with_context(self, f: impl FnOnce() -> String) -> Result<T> {
236        match self {
237            Err(Error::Other(e)) => Err(Error::Other(e.context(f()))),
238            r => r,
239        }
240    }
241}
242
243impl<T> ResultExt for anyhow::Result<T> {
244    type Ok = T;
245
246    fn wrap_with_context(self, f: impl FnOnce() -> String) -> Result<T> {
247        match self {
248            Err(e) => Err(Error::Other(e.context(f()))),
249            Ok(r) => Ok(r),
250        }
251    }
252}
253
254#[cfg(any(feature = "sqlx-sqlite", feature = "server"))]
255impl<T> ResultExt for sqlx::Result<T> {
256    type Ok = T;
257
258    fn wrap_with_context(self, f: impl FnOnce() -> String) -> Result<T> {
259        match self {
260            Err(sqlx::Error::Database(err)) => match err.code().as_deref() {
261                Some("22P05" | "22021") => Err(Error::NullByteInString),
262                Some("22P03") => Err(Error::InvalidNumber),
263                _ => Err(Error::Other(
264                    anyhow::Error::from(sqlx::Error::Database(err)).context(f()),
265                )),
266            },
267            Err(e) => Err(Error::Other(anyhow::Error::from(e).context(f()))),
268            Ok(r) => Ok(r),
269        }
270    }
271}
272
273#[cfg(feature = "indexed-db")]
274impl<T> ResultExt for std::result::Result<T, web_sys::DomException> {
275    type Ok = T;
276
277    fn wrap_with_context(self, f: impl FnOnce() -> String) -> Result<T> {
278        match self {
279            Err(e) => Err(Error::Other(
280                anyhow::anyhow!("{}: {}", e.name(), e.message()).context(f()),
281            )),
282            Ok(r) => Ok(r),
283        }
284    }
285}
286
287#[cfg(feature = "indexed-db")]
288impl<T> ResultExt for std::result::Result<T, web_sys::wasm_bindgen::JsValue> {
289    type Ok = T;
290
291    fn wrap_with_context(self, f: impl FnOnce() -> String) -> Result<T> {
292        use web_sys::wasm_bindgen::JsCast;
293        match self {
294            Err(err) => {
295                if err.has_type::<web_sys::DomException>() {
296                    return Err(err.dyn_into::<web_sys::DomException>().unwrap())
297                        .wrap_with_context(f);
298                }
299                Err(crate::Error::Other(
300                    anyhow::anyhow!("error with unknown type: {err:?}").context(f()),
301                ))
302            }
303            Ok(r) => Ok(r),
304        }
305    }
306}
307
308#[cfg(feature = "indexed-db")]
309impl<T> ResultExt for std::result::Result<T, serde_wasm_bindgen::Error> {
310    type Ok = T;
311
312    fn wrap_with_context(self, f: impl FnOnce() -> String) -> Result<T> {
313        match self {
314            Err(e) => Err(Error::Other(anyhow::anyhow!("{}", e).context(f()))),
315            Ok(r) => Ok(r),
316        }
317    }
318}
319
320#[cfg(feature = "indexed-db")]
321impl<T> ResultExt for std::result::Result<T, indexed_db::Error<crate::Error>> {
322    type Ok = T;
323
324    fn wrap_with_context(self, f: impl FnOnce() -> String) -> Result<T> {
325        match self {
326            Err(indexed_db::Error::User(e)) => Err(e).wrap_with_context(f),
327            Err(e) => Err(Error::Other(anyhow::Error::from(e).context(f()))),
328            Ok(r) => Ok(r),
329        }
330    }
331}
332
333#[cfg(feature = "server")]
334impl<T> ResultExt for std::result::Result<T, axum::Error> {
335    type Ok = T;
336
337    fn wrap_with_context(self, f: impl FnOnce() -> String) -> Result<T> {
338        match self {
339            Err(e) => Err(Error::Other(anyhow::Error::from(e).context(f()))),
340            Ok(r) => Ok(r),
341        }
342    }
343}
344
345impl<T> ResultExt for serde_json::Result<T> {
346    type Ok = T;
347
348    fn wrap_with_context(self, f: impl FnOnce() -> String) -> Result<T> {
349        match self {
350            Err(e) => Err(Error::Other(anyhow::Error::from(e).context(f()))),
351            Ok(r) => Ok(r),
352        }
353    }
354}
355
356impl<T> ResultExt for std::result::Result<T, SerializableError> {
357    type Ok = T;
358
359    fn wrap_with_context(self, f: impl FnOnce() -> String) -> Result<T> {
360        match self {
361            Ok(r) => Ok(r),
362            Err(err) => Err(Error::from(err)).wrap_with_context(f),
363        }
364    }
365}