use async_trait::async_trait;
use crate::{
errors::DomainError,
events::DomainEvent,
models::{
Movie, MovieFilter, MovieProfile, MovieSummary, PersistedWatchEvent, WatchEvent,
collections::{PageParams, Paginated},
},
ports::{MovieCommand, MovieProfileRepository, MovieQuery},
value_objects::{ExternalMetadataId, MovieId, MovieTitle, ReleaseYear},
};
use super::InMemoryStore;
#[async_trait]
impl MovieCommand for InMemoryStore {
async fn upsert_movie(&self, movie: &Movie, events: &[DomainEvent]) -> Result<(), DomainError> {
let mut movies = self.movies.write().unwrap();
if let Some(external) = movie.external_metadata_id() {
let taken = movies.values().any(|m| {
m.id() != movie.id()
&& m.external_metadata_id().map(|e| e.value()) == Some(external.value())
});
if taken {
return Err(DomainError::InfrastructureError(
"Database operation failed".into(),
));
}
}
movies.insert(movie.id().value(), movie.clone());
drop(movies);
self.record_events(events);
Ok(())
}
async fn delete_movie(
&self,
movie_id: &MovieId,
events: &[DomainEvent],
) -> Result<(), DomainError> {
let id = movie_id.value();
if !self.movies.read().unwrap().contains_key(&id) {
return Ok(());
}
if self
.reviews
.read()
.unwrap()
.values()
.any(|r| r.movie_id().value() == id)
{
return Err(DomainError::InfrastructureError(
"Database operation failed".into(),
));
}
self.remove_movie_with_cascades(id);
self.record_events(events);
Ok(())
}
}
impl InMemoryStore {
pub(super) fn remove_movie_with_cascades(&self, id: uuid::Uuid) {
self.movies.write().unwrap().remove(&id);
self.watchlist
.write()
.unwrap()
.retain(|(_, movie), _| *movie != id);
self.movie_profiles.write().unwrap().remove(&id);
let mut events_guard = self.watch_events.write().unwrap();
for event in events_guard.iter_mut() {
if event.movie_id().map(|m| m.value()) == Some(id) {
*event = detached_from_movie(event);
}
}
}
}
#[async_trait]
impl MovieQuery for InMemoryStore {
async fn get_movie_by_external_id(
&self,
external_metadata_id: &ExternalMetadataId,
) -> Result