use async_trait::async_trait;
use bamboo_domain::Session;
use super::errors::{RespondError, SessionLoadError, SessionSaveError};
use crate::SessionRepository;
#[async_trait]
pub trait SessionAccess: Send + Sync {
fn bind_response_writer(
&self,
owner: Option<bamboo_domain::RootActorRuntimeWrite>,
) -> Result<Option<std::sync::Arc<dyn SessionAccess>>, SessionSaveError> {
if owner.is_some() {
return Err(SessionSaveError::StorageError(
"Root response writer is unsupported".into(),
));
}
Ok(None)
}
fn bind_response_writer_with_inherited(
&self,
owner: Option<bamboo_domain::RootActorRuntimeWrite>,
inherited: Option<bamboo_domain::InheritedChildWait>,
) -> Result<Option<std::sync::Arc<dyn SessionAccess>>, SessionSaveError> {
if inherited.is_some() {
return Err(SessionSaveError::StorageError(
"inherited response writer is unsupported".into(),
));
}
self.bind_response_writer(owner)
}
async fn load_session(&self, id: &str) -> Result<Option<Session>, SessionLoadError>;
async fn load_or_create(&self, id: &str, model: &str) -> Result<Session, SessionLoadError>;
async fn load_merged(&self, id: &str) -> Result<Option<Session>, SessionLoadError>;
async fn save_session(&self, session: &mut Session) -> Result<(), SessionSaveError>;
async fn save_and_cache(&self, session: &mut Session) -> Result<(), SessionSaveError>;
async fn inspect_for_response(&self, id: &str) -> Result<Option<Session>, SessionLoadError> {
self.load_merged(id).await
}
async fn mutate_for_response(
&self,
id: &str,
mutate: Box<
dyn for<'session> FnOnce(&'session mut Session) -> Result<(), RespondError>
+ Send
+ 'static,
>,
) -> Result<Option<Session>, RespondError> {
let Some(mut session) = self.load_merged(id).await? else {
return Ok(None);
};
mutate(&mut session)?;
self.save_and_cache(&mut session).await?;
Ok(Some(session))
}
}
#[async_trait]
impl SessionAccess for SessionRepository {
fn bind_response_writer(
&self,
owner: Option<bamboo_domain::RootActorRuntimeWrite>,
) -> Result<Option<std::sync::Arc<dyn SessionAccess>>, SessionSaveError> {
owner
.map(|owner| {
self.bind_root_response_writer(owner)
.map(|bound| std::sync::Arc::new(bound) as std::sync::Arc<dyn SessionAccess>)
.map_err(|error| SessionSaveError::StorageError(error.to_string()))
})
.transpose()
}
fn bind_response_writer_with_inherited(
&self,
owner: Option<bamboo_domain::RootActorRuntimeWrite>,
inherited: Option<bamboo_domain::InheritedChildWait>,
) -> Result<Option<std::sync::Arc<dyn SessionAccess>>, SessionSaveError> {
match owner {
Some(owner) => self
.bind_root_response_writer_with_inherited(owner, inherited)
.map(|bound| Some(std::sync::Arc::new(bound) as std::sync::Arc<dyn SessionAccess>))
.map_err(|error| SessionSaveError::StorageError(error.to_string())),
None if inherited.is_some() => Err(SessionSaveError::StorageError(
"inherited response writer requires its Root owner".into(),
)),
None => Ok(None),
}
}
async fn load_session(&self, id: &str) -> Result<Option<Session>, SessionLoadError> {
match SessionRepository::load(self, id).await {
Some(session) => Ok(Some(session)),
None => Err(SessionLoadError::NotFound(id.to_string())),
}
}
async fn load_or_create(&self, id: &str, model: &str) -> Result<Session, SessionLoadError> {
Ok(SessionRepository::load_or_create(self, id, model).await)
}
async fn load_merged(&self, id: &str) -> Result<Option<Session>, SessionLoadError> {
SessionRepository::load_merged_checked(self, id)
.await
.map_err(|error| SessionLoadError::StorageError(error.to_string()))
}
async fn save_session(&self, session: &mut Session) -> Result<(), SessionSaveError> {
self.persistence()
.merge_save_runtime(session)
.await
.map_err(|e| SessionSaveError::StorageError(e.to_string()))
}
async fn save_and_cache(&self, session: &mut Session) -> Result<(), SessionSaveError> {
SessionRepository::save_and_cache(self, session).await;
Ok(())
}
async fn inspect_for_response(&self, id: &str) -> Result<Option<Session>, SessionLoadError> {
let cache = self.cache().clone();
let session_id = id.to_string();
self.persistence()
.inspect_runtime_session_for_response(id, move || {
crate::read_cached_session(&cache, &session_id)
})
.await
.map_err(|error| SessionLoadError::StorageError(error.to_string()))
}
async fn mutate_for_response(
&self,
id: &str,
mutate: Box<
dyn for<'session> FnOnce(&'session mut Session) -> Result<(), RespondError>
+ Send
+ 'static,
>,
) -> Result<Option<Session>, RespondError> {
let cache_for_load = self.cache().clone();
let publish_cache = self.cache().clone();
let session_id = id.to_string();
match self
.persistence()
.mutate_runtime_session_and_publish(
id,
move || crate::read_cached_session(&cache_for_load, &session_id),
move |session| mutate(session),
move |saved| {
publish_cache.insert(
saved.id.clone(),
std::sync::Arc::new(crate::SessionSnapshot::new(saved.clone())),
);
},
)
.await
{
Ok(Ok(session)) => Ok(session),
Ok(Err(error)) => Err(error),
Err(error) => Err(RespondError::SaveFailed(SessionSaveError::StorageError(
error.to_string(),
))),
}
}
}