1use std::cmp::Ordering;
5use std::fmt::Debug;
6use std::marker::PhantomData;
7
8use async_trait::async_trait;
9use kurrentdb::{
10 AppendToStreamOptions, Client as EventDb, Error, EventData, ReadStreamOptions, StreamPosition,
11 StreamState, SubscribeToPersistentSubscriptionOptions,
12};
13use serde::de::DeserializeOwned;
14use serde::{Deserialize, Serialize};
15
16use crate::cache_db::CacheDb;
17use crate::helper::create_subscription;
18use crate::metadata::{CompleteEvent, Metadata};
19use crate::model_key::ModelKey;
20use crate::{Dto, EventSourceError, EventSourceStateError};
21use crate::{State, Stream};
22
23#[derive(Clone)]
27#[allow(clippy::module_name_repetitions)]
28pub struct DtoRepository<D, C>
29where
30 D: Dto,
31 C: CacheDb<D>,
32{
33 event_db: EventDb,
34 cache_db: C,
35 repository_kind: RepositoryKind,
36 dto: PhantomData<D>,
37}
38
39#[derive(Clone)]
43#[allow(clippy::module_name_repetitions)]
44pub struct StateRepository<S, C>
45where
46 S: State,
47 C: CacheDb<S>,
48{
49 event_db: EventDb,
50 state_db: C,
51 repository_kind: RepositoryKind,
52 state: PhantomData<S>,
53}
54
55#[derive(Default, Serialize, Deserialize, Debug, Clone)]
57pub struct ModelWithPosition<M> {
58 position: Option<u64>,
59 model: M,
60}
61
62impl<M> ModelWithPosition<M>
63where
64 M: Dto,
65{
66 pub const fn state(&self) -> &M {
68 &self.model
69 }
70
71 pub fn play_event(&mut self, event: &M::Event, position: Option<u64>) {
73 self.model.play_event(event);
74
75 self.position = position;
76 }
77
78 pub const fn position(&self) -> Option<u64> {
81 self.position
82 }
83}
84
85#[derive(Clone)]
88pub enum RepositoryKind {
89 State,
91 Dto(&'static str),
93}
94
95impl RepositoryKind {
96 const fn to_cache_prefix(&self) -> Option<&'static str> {
97 match self {
98 Self::State => None,
99 Self::Dto(p) => Some(p),
100 }
101 }
102}
103
104#[allow(missing_docs)]
105#[async_trait]
106pub trait DtoRepositoryConstructor<D, C>: Clone + Send
108where
109 D: Dto,
110 C: CacheDb<D>,
111{
112 fn new(event_db: EventDb, cache_db: C, repository_kind: RepositoryKind) -> Self;
117}
118
119#[allow(missing_docs)]
120#[async_trait]
121pub trait StateRepositoryConstructor<D, C>: Clone + Send
123where
124 D: Dto,
125 C: CacheDb<D>,
126{
127 fn new(event_db: EventDb, cache_db: C) -> Self;
131}
132
133#[allow(missing_docs)]
134#[async_trait]
135pub trait Repository<D, C>: Clone + Send
137where
138 D: Dto,
139 C: CacheDb<D>,
140{
141 fn event_db(&self) -> &EventDb;
143
144 fn cache_db(&self) -> &C;
146
147 fn repository_kind(&self) -> &RepositoryKind;
149
150 async fn get_model(&self, key: &ModelKey) -> Result<ModelWithPosition<D>, EventSourceError>
151 where
152 D: Dto + DeserializeOwned,
153 {
154 let value = self
155 .cache_db()
156 .get(self.repository_kind().to_cache_prefix(), key)
157 .map_err(EventSourceError::CacheDbError)?;
158
159 self.complete_from_es(key, &value).await
160 }
161
162 async fn complete_from_es(
163 &self,
164 key: &ModelKey,
165 value: &ModelWithPosition<D>,
166 ) -> Result<ModelWithPosition<D>, EventSourceError> {
167 let mut dto: D = value.model.clone();
168 let mut position = value.position;
169
170 let options = ReadStreamOptions::default();
171 let options = if let Some(position) = value.position {
172 options.position(StreamPosition::Position(position + 1))
173 } else {
174 options.position(StreamPosition::Start)
175 };
176
177 let mut stream = self
178 .event_db()
179 .read_stream(key.format(), &options)
180 .await
181 .map_err(EventSourceError::EventStore)?;
182
183 while let Ok(Some(json_event)) = stream.next().await {
184 let original_event = json_event.get_original_event();
185
186 let metadata: Metadata =
187 serde_json::from_slice(original_event.custom_metadata.as_ref())
188 .map_err(EventSourceError::Serde)?;
189
190 if metadata.is_event() {
191 let event = original_event
192 .as_json::<D::Event>()
193 .map_err(EventSourceError::Serde)?;
194
195 dto.play_event(&event);
196 }
197
198 position = Some(original_event.revision);
199 }
200
201 let result = ModelWithPosition {
202 position,
203 model: dto,
204 };
205
206 Ok(result)
207 }
208
209 async fn cache_dto(&self, stream: &Stream, group_name: &str) -> Result<(), EventSourceError> {
210 create_subscription(self.event_db(), stream, group_name)
211 .await
212 .map_err(EventSourceError::EventStore)?;
213
214 let options = SubscribeToPersistentSubscriptionOptions::default().buffer_size(1);
215
216 let mut sub = self
217 .event_db()
218 .subscribe_to_persistent_subscription(stream.to_string(), group_name, &options)
219 .await
220 .map_err(EventSourceError::EventStore)?;
221
222 loop {
223 let rcv_event = sub.next().await.map_err(EventSourceError::EventStore)?;
224
225 let Some(event) = rcv_event.event.as_ref() else {
226 continue;
227 };
228
229 let model_key: ModelKey = event
230 .stream_id()
231 .try_into()
232 .map_err(EventSourceError::ModelKey)?;
233
234 let mut model = self
235 .cache_db()
236 .get(self.repository_kind().to_cache_prefix(), &model_key)
237 .map_err(EventSourceError::CacheDbError)?;
238
239 let ordering = if event.revision == 0 {
240 if model.position.is_some() {
241 Ordering::Greater
242 } else {
243 Ordering::Equal
244 }
245 } else {
246 model
247 .position
248 .map_or(Ordering::Less, |pos| pos.cmp(&(event.revision)))
249 };
250
251 match ordering {
252 Ordering::Less => {
253 model = self.complete_from_es(&model_key, &model).await?;
254
255 self.cache_db()
256 .set(&model_key, model, self.repository_kind().to_cache_prefix())
257 .map_err(EventSourceError::CacheDbError)?;
258 }
259 Ordering::Equal | Ordering::Greater => {}
260 }
261
262 sub.ack(&rcv_event)
263 .await
264 .map_err(EventSourceError::EventStore)?;
265 }
266 }
267
268 fn split_event_id(str: &str) -> Result<(&str, &str), EventSourceError> {
272 let mut iter = str.split('@');
273
274 if let (Some(index), Some(stream_id)) = (iter.next(), iter.next()) {
275 return Ok((index, stream_id));
276 }
277
278 Err(EventSourceError::Position(format!(
279 "{str} isnt in the format index@stream_id"
280 )))
281 }
282}
283
284impl<D, C> DtoRepositoryConstructor<D, C> for DtoRepository<D, C>
285where
286 D: Dto,
287 C: CacheDb<D>,
288{
289 fn new(event_db: EventDb, cache_db: C, repository_kind: RepositoryKind) -> Self {
290 Self {
291 event_db,
292 cache_db,
293 repository_kind,
294 dto: PhantomData,
295 }
296 }
297}
298
299impl<D, C> Repository<D, C> for DtoRepository<D, C>
300where
301 D: Dto,
302 C: CacheDb<D>,
303{
304 fn event_db(&self) -> &EventDb {
305 &self.event_db
306 }
307 fn cache_db(&self) -> &C {
308 &self.cache_db
309 }
310
311 fn repository_kind(&self) -> &RepositoryKind {
312 &self.repository_kind
313 }
314}
315
316impl<S, C> StateRepositoryConstructor<S, C> for StateRepository<S, C>
317where
318 S: State,
319 C: CacheDb<S>,
320{
321 fn new(event_db: EventDb, state_db: C) -> Self {
322 Self {
323 event_db,
324 state_db,
325 repository_kind: RepositoryKind::State,
326 state: PhantomData,
327 }
328 }
329}
330
331impl<S, C> Repository<S, C> for StateRepository<S, C>
332where
333 S: State,
334 C: CacheDb<S>,
335{
336 fn event_db(&self) -> &EventDb {
337 &self.event_db
338 }
339 fn cache_db(&self) -> &C {
340 &self.state_db
341 }
342
343 fn repository_kind(&self) -> &RepositoryKind {
344 &self.repository_kind
345 }
346}
347
348#[derive(Eq, PartialEq)]
350pub enum AddedEvent {
351 Success,
353
354 NeedRetry,
356}
357
358impl<S, C> StateRepository<S, C>
359where
360 S: State,
361 C: CacheDb<S>,
362{
363 pub async fn add_command(
367 &self,
368 key: &ModelKey,
369 command: S::Command,
370 previous_metadata: Option<&Metadata>,
371 ) -> Result<S, EventSourceStateError>
372 where
373 S: State,
374 {
375 let mut model: S;
376 let events: Vec<S::Event>;
377
378 loop {
379 let (l_model, l_events, retry) = self
380 .try_append(key, command.clone(), previous_metadata)
381 .await?;
382 if retry == AddedEvent::NeedRetry {
383 continue;
384 }
385
386 model = l_model;
387 events = l_events;
388
389 break;
390 }
391
392 for event in &events {
393 model.play_event(event);
394 }
395
396 Ok(model)
397 }
398
399 async fn try_append(
400 &self,
401 key: &ModelKey,
402 command: S::Command,
403 previous_metadata: Option<&Metadata>,
404 ) -> Result<(S, Vec<S::Event>, AddedEvent), EventSourceStateError>
405 where
406 S: State + Sync,
407 {
408 let model: ModelWithPosition<S> = self
409 .get_model(key)
410 .await
411 .map_err(EventSourceStateError::EventSourceError)?;
412
413 let state = model.model;
414
415 let events = state
416 .try_command(command.clone())
417 .map_err(|e| EventSourceStateError::State(format!("{e}")))?;
418
419 let options = model.position.map_or_else(
420 || AppendToStreamOptions::default().stream_state(StreamState::NoStream),
421 |position| {
422 AppendToStreamOptions::default().stream_state(StreamState::StreamRevision(position))
423 },
424 );
425
426 let command_metadata = CompleteEvent::from_command(&command, previous_metadata)
427 .map_err(|e| EventSourceStateError::EventSourceError(EventSourceError::Serde(e)))?;
428
429 let mut events_data = vec![command_metadata.clone()];
430
431 let mut previous_metadata = command_metadata.metadata().to_owned();
432
433 let res_events = events.clone();
434
435 for event in events {
436 let event_metadata = CompleteEvent::from_event(&event, &previous_metadata)
437 .map_err(|e| EventSourceStateError::EventSourceError(EventSourceError::Serde(e)))?;
438
439 events_data.push(event_metadata.clone());
440 event_metadata.metadata().clone_into(&mut previous_metadata);
441 }
442
443 let retry = self
444 .try_append_event_data(key, &options, events_data)
445 .await?;
446
447 Ok((state, res_events, retry))
448 }
449
450 async fn try_append_event_data(
451 &self,
452 key: &ModelKey,
453 options: &AppendToStreamOptions,
454 events_with_data: Vec<CompleteEvent>,
455 ) -> Result<AddedEvent, EventSourceStateError>
456 where
457 S: State,
458 {
459 let mut err = Ok(());
460 let events: Vec<EventData> = events_with_data
461 .into_iter()
462 .filter_map(|e| match e.full_event_data() {
463 Ok(event) => Some(event),
464 Err(e) => {
465 err = Err(EventSourceError::Serde(e));
466 None
467 }
468 })
469 .collect();
470 err.map_err(EventSourceStateError::EventSourceError)?;
471
472 let appended = self
473 .event_db
474 .append_to_stream(key.format(), options, events)
475 .await;
476
477 match appended {
478 Ok(_) => Ok(AddedEvent::Success),
479 Err(Error::WrongExpectedVersion { .. }) => Ok(AddedEvent::NeedRetry),
480 Err(e) => Err(EventSourceStateError::EventSourceError(
481 EventSourceError::EventStore(e),
482 )),
483 }
484 }
485}