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}