JavaScript is disabled, refresh for a better experience. ambee/giterated

ambee/giterated

Git repository hosting, collaboration, and discovery for the Fediverse.

Error handling refactor

This refactor aims to improve error handling throughout the project by refining the overarching error types and increasing usage of proper error handling. Replaced existing networked operation error with `NetworkOperationError`. `NetworkOperationError` does not forward any internal error details, which allows `OperationError` to grow into a better error type. `OperationError` now has support for storing real typed errors inside of it for better debugging. `IntoInternalError` is a trait which allows for easy conversion of error types into `OperationError::internal`.

Amber - ⁨2⁩ years ago

parent: tbd commit: ⁨e02c03d

⁨giterated-stack/src/lib.rs⁩ - ⁨20051⁩ bytes
Raw
1 mod handler;
2 mod meta;
3 pub use handler::{GiteratedStack, GiteratedStackState, *};
4 pub use meta::*;
5 pub mod state;
6 pub mod update;
7
8 use std::{any::Any, convert::Infallible, future::Future, ops::Deref, pin::Pin, sync::Arc};
9
10 use core::fmt::Debug;
11 use futures_util::FutureExt;
12 use giterated_models::{
13 error::{ExtractorError, IntoInternalError, OperationError, UnauthorizedError},
14 instance::{
15 AuthenticationTokenRequest, Instance, RegisterAccountRequest, RepositoryCreateRequest,
16 },
17 object::GiteratedObject,
18 object_backend::ObjectBackend,
19 operation::GiteratedOperation,
20 repository::{AccessList, Repository},
21 settings::{GetSetting, SetSetting},
22 user::User,
23 value::GetValue,
24 };
25
26 #[derive(Clone, Debug, Hash, Eq, PartialEq)]
27 struct ObjectOperationPair {
28 pub object_name: String,
29 pub operation_name: String,
30 }
31
32 #[derive(Clone, Debug, Hash, Eq, PartialEq)]
33 pub struct ObjectValuePair {
34 pub object_kind: String,
35 pub value_kind: String,
36 }
37
38 #[async_trait::async_trait(?Send)]
39 pub trait GiteratedOperationHandler<
40 L,
41 O: GiteratedObject,
42 D: GiteratedOperation<O>,
43 S: Send + Sync + Clone,
44 >
45 {
46 fn operation_name(&self) -> &str;
47 fn object_name(&self) -> &str;
48
49 async fn handle(
50 &self,
51 object: &O,
52 operation: D,
53 state: S,
54 operation_state: &StackOperationState,
55 ) -> Result<D::Success, OperationError<D::Failure>>;
56 }
57
58 #[async_trait::async_trait(?Send)]
59 impl<O, D, F, S> GiteratedOperationHandler<(), O, D, S> for F
60 where
61 F: FnMut(
62 &O,
63 D,
64 S,
65 )
66 -> Pin<Box<dyn Future<Output = Result<D::Success, OperationError<D::Failure>>>>>
67 + Send
68 + Sync
69 + Clone,
70 O: GiteratedObject + Send + Sync,
71 D: GiteratedOperation<O> + 'static,
72 <D as GiteratedOperation<O>>::Failure: Send,
73 S: Send + Sync + Clone + 'static,
74 {
75 fn operation_name(&self) -> &str {
76 D::operation_name()
77 }
78
79 fn object_name(&self) -> &str {
80 O::object_name()
81 }
82
83 async fn handle(
84 &self,
85 object: &O,
86 operation: D,
87 state: S,
88 _operation_state: &StackOperationState,
89 ) -> Result<D::Success, OperationError<D::Failure>> {
90 self.clone()(object, operation, state).await
91 }
92 }
93
94 #[async_trait::async_trait(?Send)]
95 impl<O, O1, D, F, S> GiteratedOperationHandler<(O1,), O, D, S> for F
96 where
97 F: FnMut(
98 &O,
99 D,
100 S,
101 O1,
102 )
103 -> Pin<Box<dyn Future<Output = Result<D::Success, OperationError<D::Failure>>>>>
104 + Send
105 + Sync
106 + Clone,
107 O: GiteratedObject + Send + Sync,
108 D: GiteratedOperation<O> + 'static + Send + Sync,
109 <D as GiteratedOperation<O>>::Failure: Send,
110 S: Send + Sync + Clone + 'static,
111 O1: FromOperationState<O, D>,
112 ExtractorError<<O1 as FromOperationState<O, D>>::Error>: Into<anyhow::Error>,
113 {
114 fn operation_name(&self) -> &str {
115 D::operation_name()
116 }
117
118 fn object_name(&self) -> &str {
119 O::object_name()
120 }
121
122 async fn handle(
123 &self,
124 object: &O,
125 operation: D,
126 state: S,
127 operation_state: &StackOperationState,
128 ) -> Result<D::Success, OperationError<D::Failure>> {
129 let o1 = O1::from_state(object, &operation, operation_state)
130 .await
131 .as_internal_error()?;
132 self.clone()(object, operation, state, o1).await
133 }
134 }
135
136 #[async_trait::async_trait(?Send)]
137 impl<O, O1, O2, D, F, S> GiteratedOperationHandler<(O1, O2), O, D, S> for F
138 where
139 F: FnMut(
140 &O,
141 D,
142 S,
143 O1,
144 O2,
145 )
146 -> Pin<Box<dyn Future<Output = Result<D::Success, OperationError<D::Failure>>>>>
147 + Send
148 + Sync
149 + Clone,
150 O: GiteratedObject + Send + Sync,
151 D: GiteratedOperation<O> + 'static + Send + Sync,
152 <D as GiteratedOperation<O>>::Failure: Send,
153 S: Send + Sync + Clone + 'static,
154 O1: FromOperationState<O, D>,
155 ExtractorError<<O1 as FromOperationState<O, D>>::Error>: Into<anyhow::Error>,
156 O2: FromOperationState<O, D>,
157 ExtractorError<<O2 as FromOperationState<O, D>>::Error>: Into<anyhow::Error>,
158 {
159 fn operation_name(&self) -> &str {
160 D::operation_name()
161 }
162
163 fn object_name(&self) -> &str {
164 O::object_name()
165 }
166
167 async fn handle(
168 &self,
169 object: &O,
170 operation: D,
171 state: S,
172 operation_state: &StackOperationState,
173 ) -> Result<D::Success, OperationError<D::Failure>> {
174 let o1 = O1::from_state(object, &operation, operation_state)
175 .await
176 .as_internal_error()?;
177 let o2 = O2::from_state(object, &operation, operation_state)
178 .await
179 .as_internal_error()?;
180 self.clone()(object, operation, state, o1, o2).await
181 }
182 }
183
184 #[async_trait::async_trait(?Send)]
185 impl<O, O1, O2, O3, D, F, S> GiteratedOperationHandler<(O1, O2, O3), O, D, S> for F
186 where
187 F: FnMut(
188 &O,
189 D,
190 S,
191 O1,
192 O2,
193 O3,
194 )
195 -> Pin<Box<dyn Future<Output = Result<D::Success, OperationError<D::Failure>>>>>
196 + Send
197 + Sync
198 + Clone,
199 O: GiteratedObject + Send + Sync,
200 D: GiteratedOperation<O> + 'static + Send + Sync,
201 <D as GiteratedOperation<O>>::Failure: Send,
202 S: Send + Sync + Clone + 'static,
203 O1: FromOperationState<O, D>,
204 ExtractorError<<O1 as FromOperationState<O, D>>::Error>: Into<anyhow::Error>,
205 O2: FromOperationState<O, D>,
206 ExtractorError<<O2 as FromOperationState<O, D>>::Error>: Into<anyhow::Error>,
207 O3: FromOperationState<O, D>,
208 ExtractorError<<O3 as FromOperationState<O, D>>::Error>: Into<anyhow::Error>,
209 {
210 fn operation_name(&self) -> &str {
211 D::operation_name()
212 }
213
214 fn object_name(&self) -> &str {
215 O::object_name()
216 }
217
218 async fn handle(
219 &self,
220 object: &O,
221 operation: D,
222 state: S,
223 operation_state: &StackOperationState,
224 ) -> Result<D::Success, OperationError<D::Failure>> {
225 let o1 = O1::from_state(object, &operation, operation_state)
226 .await
227 .as_internal_error()?;
228 let o2 = O2::from_state(object, &operation, operation_state)
229 .await
230 .as_internal_error()?;
231 let o3 = O3::from_state(object, &operation, operation_state)
232 .await
233 .as_internal_error()?;
234 self.clone()(object, operation, state, o1, o2, o3).await
235 }
236 }
237
238 pub struct OperationWrapper {
239 func: Box<
240 dyn Fn(
241 &(dyn Any + Send + Sync),
242 &(dyn Any + Send + Sync),
243 &(dyn Any + Send + Sync),
244 StackOperationState,
245 ) -> Pin<
246 Box<
247 dyn Future<
248 Output = Result<
249 Box<dyn Any + Send + Sync>,
250 OperationError<Box<dyn Any + Send + Sync>>,
251 >,
252 >,
253 >,
254 > + Send
255 + Sync,
256 >,
257 state: Box<dyn Any + Send + Sync>,
258 }
259
260 impl OperationWrapper {
261 pub fn new<
262 A,
263 O: GiteratedObject + Send + Sync + 'static,
264 D: GiteratedOperation<O> + 'static + Clone,
265 F: GiteratedOperationHandler<A, O, D, S> + 'static + Send + Sync + Clone,
266 S: GiteratedStackState + 'static,
267 >(
268 handler: F,
269 state: S,
270 ) -> Self
271 where
272 D::Failure: Send + Sync,
273 D::Success: Send + Sync,
274 {
275 Self {
276 func: Box::new(move |object, operation, state, operation_state| {
277 let handler = handler.clone();
278 let state = state.downcast_ref::<S>().unwrap().clone();
279 let object: &O = object.downcast_ref().unwrap();
280 let operation: &D = operation.downcast_ref().unwrap();
281 let object = object.clone();
282 let operation = operation.clone();
283 async move {
284 let result = handler
285 .handle(&object, operation, state, &operation_state)
286 .await;
287 result
288 .map(|success| Box::new(success) as _)
289 .map_err(|err| match err {
290 OperationError::Operation(err) => {
291 OperationError::Operation(Box::new(err) as _)
292 }
293 OperationError::Internal(internal) => {
294 OperationError::Internal(internal)
295 }
296 OperationError::Unhandled => OperationError::Unhandled,
297 })
298 }
299 .boxed_local()
300 }),
301 state: Box::new(state),
302 }
303 }
304
305 async fn handle(
306 &self,
307 object: &Box<dyn Any + Send + Sync>,
308 operation: &Box<dyn Any + Send + Sync>,
309 operation_state: &StackOperationState,
310 ) -> Result<Box<dyn Any + Send + Sync>, OperationError<Box<dyn Any + Send + Sync>>> {
311 (self.func)(
312 (*object).as_ref(),
313 (*operation).as_ref(),
314 self.state.as_ref(),
315 operation_state.clone(),
316 )
317 .await
318 }
319 }
320
321 #[async_trait::async_trait(?Send)]
322 pub trait FromOperationState<O: GiteratedObject, D: GiteratedOperation<O>>: Sized + Clone {
323 type Error: Into<anyhow::Error>;
324
325 async fn from_state(
326 object: &O,
327 operation: &D,
328 state: &StackOperationState,
329 ) -> Result<Self, ExtractorError<Self::Error>>;
330 }
331
332 #[async_trait::async_trait(?Send)]
333 impl<O: GiteratedObject, D: GiteratedOperation<O>> FromOperationState<O, D>
334 for Arc<GiteratedStack>
335 {
336 type Error = Infallible;
337
338 async fn from_state(
339 _object: &O,
340 _operation: &D,
341 state: &StackOperationState,
342 ) -> Result<Self, ExtractorError<Infallible>> {
343 Ok(state.runtime.clone())
344 }
345 }
346
347 #[async_trait::async_trait(?Send)]
348 impl<O: GiteratedObject, D: GiteratedOperation<O>> FromOperationState<O, D>
349 for StackOperationState
350 {
351 type Error = Infallible;
352
353 async fn from_state(
354 _object: &O,
355 _operation: &D,
356 state: &StackOperationState,
357 ) -> Result<StackOperationState, ExtractorError<Infallible>> {
358 Ok(state.clone())
359 }
360 }
361
362 #[derive(Debug, thiserror::Error)]
363 #[error("missing value")]
364 pub struct MissingValue;
365
366 #[async_trait::async_trait(?Send)]
367 impl<O: GiteratedObject, D: GiteratedOperation<O> + Send + Sync> FromOperationState<O, D>
368 for AuthenticatedUser
369 {
370 type Error = MissingValue;
371
372 async fn from_state(
373 _object: &O,
374 _operation: &D,
375 state: &StackOperationState,
376 ) -> Result<AuthenticatedUser, ExtractorError<MissingValue>> {
377 state
378 .user
379 .clone()
380 .ok_or_else(|| ExtractorError(MissingValue))
381 }
382 }
383
384 #[async_trait::async_trait(?Send)]
385 impl<O: GiteratedObject, D: GiteratedOperation<O> + Send + Sync> FromOperationState<O, D>
386 for AuthenticatedInstance
387 {
388 type Error = MissingValue;
389
390 async fn from_state(
391 _object: &O,
392 _operation: &D,
393 state: &StackOperationState,
394 ) -> Result<AuthenticatedInstance, ExtractorError<MissingValue>> {
395 state
396 .instance
397 .clone()
398 .ok_or_else(|| ExtractorError(MissingValue))
399 }
400 }
401
402 #[async_trait::async_trait(?Send)]
403 impl<
404 T: FromOperationState<O, D> + Send + Sync,
405 O: GiteratedObject + Sync,
406 D: GiteratedOperation<O> + Send + Sync,
407 > FromOperationState<O, D> for Option<T>
408 {
409 type Error = Infallible;
410
411 async fn from_state(
412 object: &O,
413 operation: &D,
414 state: &StackOperationState,
415 ) -> Result<Option<T>, ExtractorError<Infallible>> {
416 Ok(T::from_state(object, operation, state).await.ok())
417 }
418 }
419
420 #[derive(Clone)]
421 pub struct AuthorizedUser(AuthenticatedUser);
422
423 #[derive(Clone)]
424 pub struct AuthorizedInstance(AuthenticatedInstance);
425
426 #[async_trait::async_trait(?Send)]
427 pub trait AuthorizedOperation<O: GiteratedObject>: GiteratedOperation<O> {
428 type Error: Into<anyhow::Error>;
429
430 async fn authorize(
431 &self,
432 authorize_for: &O,
433 state: &StackOperationState,
434 ) -> Result<bool, ExtractorError<Self::Error>>;
435 }
436
437 #[async_trait::async_trait(?Send)]
438 impl<O: GiteratedObject + Send + Sync + Debug + 'static> AuthorizedOperation<O> for GetValue {
439 type Error = anyhow::Error;
440
441 async fn authorize(
442 &self,
443 authorize_for: &O,
444 operation_state: &StackOperationState,
445 ) -> Result<bool, ExtractorError<anyhow::Error>> {
446 Ok(operation_state
447 .runtime
448 .get_object::<O>(&authorize_for.to_string(), operation_state)
449 .await
450 .is_ok())
451 }
452 }
453
454 #[async_trait::async_trait(?Send)]
455 impl AuthorizedOperation<User> for SetSetting {
456 type Error = MissingValue;
457
458 async fn authorize(
459 &self,
460 authorize_for: &User,
461 operation_state: &StackOperationState,
462 ) -> Result<bool, ExtractorError<MissingValue>> {
463 let authenticated_user = operation_state.user.as_ref().ok_or_else(|| MissingValue)?;
464
465 Ok(authorize_for == authenticated_user.deref())
466 }
467 }
468
469 #[async_trait::async_trait(?Send)]
470 impl AuthorizedOperation<User> for GetSetting {
471 type Error = MissingValue;
472
473 async fn authorize(
474 &self,
475 authorize_for: &User,
476 operation_state: &StackOperationState,
477 ) -> Result<bool, ExtractorError<MissingValue>> {
478 let authenticated_user = operation_state.user.as_ref().ok_or_else(|| MissingValue)?;
479
480 Ok(authorize_for == authenticated_user.deref())
481 }
482 }
483
484 #[async_trait::async_trait(?Send)]
485 impl AuthorizedOperation<Repository> for SetSetting {
486 type Error = anyhow::Error;
487
488 async fn authorize(
489 &self,
490 authorize_for: &Repository,
491 operation_state: &StackOperationState,
492 ) -> Result<bool, ExtractorError<anyhow::Error>> {
493 let authenticated_user = operation_state
494 .user
495 .as_ref()
496 .ok_or_else(|| anyhow::Error::from(MissingValue))?;
497
498 let mut object = operation_state
499 .runtime
500 .get_object::<Repository>(&authorize_for.to_string(), operation_state)
501 .await
502 .map_err(|err| anyhow::Error::from(err))?;
503
504 let access_list = object
505 .get_setting::<AccessList>(operation_state)
506 .await
507 .map_err(|err| anyhow::Error::from(err))?;
508
509 if access_list
510 .0
511 .iter()
512 .find(|user| *user == authenticated_user.deref())
513 .is_some()
514 {
515 Ok(true)
516 } else {
517 Ok(false)
518 }
519 }
520 }
521
522 #[async_trait::async_trait(?Send)]
523 impl AuthorizedOperation<Repository> for GetSetting {
524 type Error = anyhow::Error;
525
526 async fn authorize(
527 &self,
528 authorize_for: &Repository,
529 operation_state: &StackOperationState,
530 ) -> Result<bool, ExtractorError<anyhow::Error>> {
531 let authenticated_user = operation_state
532 .user
533 .as_ref()
534 .ok_or_else(|| anyhow::Error::from(MissingValue))?;
535
536 let mut object = operation_state
537 .runtime
538 .get_object::<Repository>(&authorize_for.to_string(), operation_state)
539 .await
540 .map_err(|err| anyhow::Error::from(err))?;
541
542 let access_list = object
543 .get_setting::<AccessList>(operation_state)
544 .await
545 .map_err(|err| anyhow::Error::from(err))?;
546
547 if access_list
548 .0
549 .iter()
550 .find(|user| *user == authenticated_user.deref())
551 .is_some()
552 {
553 Ok(true)
554 } else {
555 Ok(false)
556 }
557 }
558 }
559
560 #[async_trait::async_trait(?Send)]
561 impl AuthorizedOperation<Instance> for RegisterAccountRequest {
562 type Error = Infallible;
563
564 async fn authorize(
565 &self,
566 authorize_for: &Instance,
567 state: &StackOperationState,
568 ) -> Result<bool, ExtractorError<Infallible>> {
569 if state.our_instance == *authorize_for {
570 Ok(true)
571 } else {
572 Ok(false)
573 }
574 }
575 }
576
577 #[async_trait::async_trait(?Send)]
578 impl AuthorizedOperation<Instance> for AuthenticationTokenRequest {
579 type Error = Infallible;
580
581 async fn authorize(
582 &self,
583 authorize_for: &Instance,
584 state: &StackOperationState,
585 ) -> Result<bool, ExtractorError<Infallible>> {
586 if state.our_instance == *authorize_for {
587 Ok(true)
588 } else {
589 Ok(false)
590 }
591 }
592 }
593
594 #[async_trait::async_trait(?Send)]
595 impl AuthorizedOperation<Instance> for RepositoryCreateRequest {
596 type Error = Infallible;
597
598 async fn authorize(
599 &self,
600 authorize_for: &Instance,
601 state: &StackOperationState,
602 ) -> Result<bool, ExtractorError<Infallible>> {
603 if state.our_instance == *authorize_for {
604 Ok(true)
605 } else {
606 Ok(false)
607 }
608 }
609 }
610
611 #[async_trait::async_trait(?Send)]
612 impl<A: AuthorizedOperation<User> + Send + Sync> FromOperationState<User, A> for AuthorizedUser {
613 type Error = UnauthorizedError;
614
615 async fn from_state(
616 object: &User,
617 operation: &A,
618 state: &StackOperationState,
619 ) -> Result<AuthorizedUser, ExtractorError<UnauthorizedError>> {
620 // TODO
621 let authenticated = AuthenticatedUser::from_state(object, operation, state)
622 .await
623 .map_err(|_| ExtractorError(UnauthorizedError))?;
624
625 match operation.authorize(object, state).await {
626 Ok(authorized) => {
627 assert!(authorized);
628 }
629 Err(_err) => return Err(ExtractorError(UnauthorizedError)),
630 };
631
632 Ok(AuthorizedUser(authenticated))
633 }
634 }
635
636 #[async_trait::async_trait(?Send)]
637 impl<A: AuthorizedOperation<Instance> + Send + Sync> FromOperationState<Instance, A>
638 for AuthorizedInstance
639 {
640 type Error = UnauthorizedError;
641
642 async fn from_state(
643 object: &Instance,
644 operation: &A,
645 state: &StackOperationState,
646 ) -> Result<AuthorizedInstance, ExtractorError<UnauthorizedError>> {
647 //TODO
648 let authenticated = AuthenticatedInstance::from_state(object, operation, state)
649 .await
650 .map_err(|_| ExtractorError(UnauthorizedError))?;
651
652 match operation.authorize(object, state).await {
653 Ok(authorized) => {
654 assert!(authorized);
655 }
656 Err(_err) => return Err(ExtractorError(UnauthorizedError)),
657 };
658
659 Ok(AuthorizedInstance(authenticated))
660 }
661 }
662
663 // #[async_trait::async_trait> FromOperationState for Option<T> {
664 // type Error = ();
665
666 // async fn from_state(state: &StackOperationState) -> Result<Option<T>, OperationError<()>> {
667 // Ok(T::from_state(]
668 // impl<T: FromOperationStatestate).await.ok())
669 // }
670 // }
671
672 #[derive(Clone)]
673 pub struct StackOperationState {
674 pub our_instance: Instance,
675 pub runtime: Arc<GiteratedStack>,
676 pub instance: Option<AuthenticatedInstance>,
677 pub user: Option<AuthenticatedUser>,
678 }
679
680 #[derive(Clone, Debug)]
681 pub struct AuthenticatedInstance(Instance);
682
683 impl AuthenticatedInstance {
684 pub fn new(instance: Instance) -> Self {
685 AuthenticatedInstance(instance)
686 }
687 }
688
689 impl Deref for AuthenticatedInstance {
690 type Target = Instance;
691
692 fn deref(&self) -> &Self::Target {
693 &self.0
694 }
695 }
696
697 #[derive(Clone, Debug)]
698 pub struct AuthenticatedUser(User);
699
700 impl AuthenticatedUser {
701 pub fn new(user: User) -> Self {
702 AuthenticatedUser(user)
703 }
704 }
705
706 impl Deref for AuthenticatedUser {
707 type Target = User;
708
709 fn deref(&self) -> &Self::Target {
710 &self.0
711 }
712 }
713