nitinol_projection/
resolver.rs

1use crate::errors::ProjectionError;
2use crate::projection::Projection;
3use async_trait::async_trait;
4use futures_util::FutureExt;
5use nitinol_core::event::Event;
6use nitinol_resolver::resolver::ResolveHandler;
7use std::panic::AssertUnwindSafe;
8
9pub(crate) const HANDLER_TYPE: &str = "projection";
10
11pub struct Project;
12
13#[async_trait]
14impl<E: Event, T> ResolveHandler<E, T> for Project
15where
16    T: Projection<E>,
17{
18    const HANDLER_TYPE: &'static str = HANDLER_TYPE;
19    type Error = ProjectionError;
20
21    async fn apply(entity: &mut Option<T>, event: E) -> Result<(), Self::Error> {
22        let Some(entity) = entity else {
23            let first = match AssertUnwindSafe(T::first(event))
24                .catch_unwind()
25                .await
26                .map_err(|_| ProjectionError::FirstFormation)?
27            {
28                Ok(a) => a,
29                Err(e) => {
30                    tracing::error!("First formation failed: {:?}", e);
31                    return Err(ProjectionError::ApplyEvent {
32                        backtrace: format!("{:?}", e),
33                    });
34                }
35            };
36            *entity = Some(first);
37            return Ok(());
38        };
39
40        if let Err(e) = T::apply(entity, event).await {
41            tracing::error!("Projection failed: {:?}", e);
42            return Err(ProjectionError::ApplyEvent {
43                backtrace: format!("{:?}", e),
44            });
45        }
46
47        Ok(())
48    }
49}