eventcore_postgres/projections/
projector.rs1use std::convert::Infallible;
2use std::error::Error;
3use std::future::Future;
4
5use eventcore_types::{AttemptNumber, DeliveryPosition, PersistedEventEnvelope, ProjectorName};
6use serde::de::DeserializeOwned;
7use sqlx::{Postgres, Transaction};
8
9use super::error::BoxedProjectionError;
10
11pub trait AfterCommit: Send + 'static {
13 type Error: Error + Send + Sync + 'static;
15
16 fn execute(self) -> impl Future<Output = Result<(), Self::Error>> + Send;
18}
19
20#[derive(Debug, Clone, Copy, Default)]
22pub struct NoopAfterCommit;
23
24impl AfterCommit for NoopAfterCommit {
25 type Error = Infallible;
26
27 async fn execute(self) -> Result<(), Self::Error> {
28 Ok(())
29 }
30}
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq)]
34#[non_exhaustive]
35pub enum ProjectionFailureDecision {
36 Retry,
38 Skip,
40 Stop,
42 Fatal,
44}
45
46#[derive(Debug)]
48pub struct ProjectionFailureContext<'a, E> {
49 position: DeliveryPosition,
50 error: &'a E,
51 attempt: AttemptNumber,
52}
53
54impl<'a, E> ProjectionFailureContext<'a, E> {
55 pub fn new(position: DeliveryPosition, error: &'a E, attempt: AttemptNumber) -> Self {
57 Self {
58 position,
59 error,
60 attempt,
61 }
62 }
63
64 pub fn position(&self) -> DeliveryPosition {
66 self.position
67 }
68
69 pub fn error(&self) -> &'a E {
71 self.error
72 }
73
74 pub fn attempt(&self) -> AttemptNumber {
76 self.attempt
77 }
78}
79
80pub trait PostgresProjector: Send {
82 type Event: DeserializeOwned + Send + Sync;
84 type Error: Error + Send + Sync + 'static;
86 type AfterCommit: AfterCommit;
88
89 fn name(&self) -> &ProjectorName;
91
92 fn decode(
100 &self,
101 envelope: &PersistedEventEnvelope,
102 ) -> Result<Self::Event, BoxedProjectionError> {
103 serde_json::from_str(envelope.payload().get())
104 .map_err(|error| -> BoxedProjectionError { Box::new(error) })
105 }
106
107 fn apply<'a, 'c>(
109 &'a mut self,
110 event: &'a Self::Event,
111 position: DeliveryPosition,
112 tx: &'a mut Transaction<'c, Postgres>,
113 ) -> impl Future<Output = Result<Self::AfterCommit, Self::Error>> + Send + 'a
114 where
115 'c: 'a;
116
117 fn on_error(
119 &mut self,
120 failure: ProjectionFailureContext<'_, Self::Error>,
121 ) -> ProjectionFailureDecision {
122 let _ = failure;
123 ProjectionFailureDecision::Fatal
124 }
125}
126
127pub trait PostgresProjectionReset: Send {
129 type Error: Error + Send + Sync + 'static;
131
132 fn reset<'a, 'c>(
134 &'a mut self,
135 tx: &'a mut Transaction<'c, Postgres>,
136 ) -> impl Future<Output = Result<(), Self::Error>> + Send + 'a
137 where
138 'c: 'a;
139}