2026-07-20 09:04:57 +07:00
|
|
|
//! Conversation use-case implementation.
|
2026-07-16 12:32:17 +07:00
|
|
|
//!
|
2026-07-20 09:04:57 +07:00
|
|
|
//! `ConversationServiceImpl` implements [`ConversationService`] from the
|
|
|
|
|
//! domain layer. It is generic over `R: ConversationRepository`, delegating
|
|
|
|
|
//! all persistence to that adapter.
|
|
|
|
|
//!
|
|
|
|
|
//! # Flow
|
2026-07-19 17:05:27 +07:00
|
|
|
//!
|
|
|
|
|
//! Each method computes the session directory from the session ID, then
|
|
|
|
|
//! delegates the actual I/O to the injected `repo`. Error context is
|
|
|
|
|
//! added at this layer to identify which session caused the failure.
|
|
|
|
|
|
|
|
|
|
use std::path::PathBuf;
|
|
|
|
|
use tracing;
|
2026-07-16 12:32:17 +07:00
|
|
|
|
2026-07-20 09:04:57 +07:00
|
|
|
use zesdex_domain::cms::{Conversation, ConversationRepository, ServiceError};
|
|
|
|
|
use zesdex_domain::core::ChatMessage;
|
2026-07-16 12:32:17 +07:00
|
|
|
|
2026-07-19 17:05:27 +07:00
|
|
|
/// Service implementation for conversation CRUD operations.
|
|
|
|
|
///
|
|
|
|
|
/// Generic over `R: ConversationRepository` so the persistence layer
|
|
|
|
|
/// can be swapped without changing business logic.
|
2026-07-16 12:32:17 +07:00
|
|
|
pub struct ConversationServiceImpl<R> {
|
|
|
|
|
pub repo: R,
|
2026-07-19 17:05:27 +07:00
|
|
|
/// Base directory containing session subdirectories.
|
|
|
|
|
pub sessions_dir: PathBuf,
|
2026-07-16 12:32:17 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<R: ConversationRepository> ConversationServiceImpl<R> {
|
|
|
|
|
/// Create a new service with the given repository and sessions directory.
|
2026-07-19 17:05:27 +07:00
|
|
|
pub fn new(repo: R, sessions_dir: impl Into<PathBuf>) -> Self {
|
|
|
|
|
tracing::debug!("creating ConversationServiceImpl");
|
2026-07-16 12:32:17 +07:00
|
|
|
Self {
|
|
|
|
|
repo,
|
|
|
|
|
sessions_dir: sessions_dir.into(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Compute the session directory for a given session id.
|
2026-07-19 17:05:27 +07:00
|
|
|
fn session_dir(&self, session_id: &str) -> PathBuf {
|
2026-07-16 12:32:17 +07:00
|
|
|
self.sessions_dir.join(session_id)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-20 09:04:57 +07:00
|
|
|
impl<R: ConversationRepository> zesdex_domain::cms::ConversationService
|
|
|
|
|
for ConversationServiceImpl<R>
|
|
|
|
|
{
|
2026-07-20 06:14:23 +07:00
|
|
|
fn load_conversation(&self, session_id: &str) -> Result<Conversation, ServiceError> {
|
2026-07-19 17:05:27 +07:00
|
|
|
tracing::debug!("loading conversation for session {session_id}");
|
2026-07-16 12:32:17 +07:00
|
|
|
let dir = self.session_dir(session_id);
|
2026-07-20 06:39:30 +07:00
|
|
|
self.repo.load(&dir).map_err(ServiceError::Repository)
|
2026-07-16 12:32:17 +07:00
|
|
|
}
|
|
|
|
|
|
2026-07-20 06:14:23 +07:00
|
|
|
fn save_conversation(&self, conv: &Conversation) -> Result<(), ServiceError> {
|
2026-07-19 17:05:27 +07:00
|
|
|
tracing::debug!("saving conversation for session {}", conv.session_id);
|
2026-07-16 12:32:17 +07:00
|
|
|
let dir = self.session_dir(&conv.session_id);
|
2026-07-20 06:39:30 +07:00
|
|
|
self.repo.save(&dir, conv)?;
|
|
|
|
|
Ok(())
|
2026-07-16 12:32:17 +07:00
|
|
|
}
|
|
|
|
|
|
2026-07-20 09:04:57 +07:00
|
|
|
fn add_message(
|
|
|
|
|
&self,
|
|
|
|
|
conv: &mut Conversation,
|
|
|
|
|
msg: ChatMessage,
|
|
|
|
|
) -> Result<(), ServiceError> {
|
2026-07-19 17:05:27 +07:00
|
|
|
tracing::debug!("adding message to session {}", conv.session_id);
|
2026-07-20 09:04:57 +07:00
|
|
|
conv.push(msg);
|
2026-07-16 12:32:17 +07:00
|
|
|
let dir = self.session_dir(&conv.session_id);
|
2026-07-20 06:39:30 +07:00
|
|
|
self.repo.save(&dir, conv)?;
|
|
|
|
|
Ok(())
|
2026-07-16 12:32:17 +07:00
|
|
|
}
|
|
|
|
|
}
|