//! Session management use-cases. //! //! `SessionServiceImpl` implements [`SessionService`] by delegating to //! injected repository implementations, keeping the orchestration logic //! independent of any concrete persistence mechanism. //! //! # Flow //! //! - **`create_session`** — generates a UUID v4 id, creates a `Session` entity, //! delegates persistence to `SessionRepository`. //! - **`list_all`** — delegates to `SessionRepository::list_sessions`. //! - **`archive_session`** — loads session, sets `archived = true`, persists. //! //! # Components //! //! - `SessionServiceImpl` — service over two generic repositories //! - `new` / `create_session` / `list_all` / `archive_session` — lifecycle ops use std::path::PathBuf; use zesdex_entities::domain::auth::SessionId; use zesdex_utils::CastOr; use tracing; use uuid::Uuid; use crate::domain::error::ServiceError; use crate::domain::repository::{SessionLockRepository, SessionRepository}; use crate::domain::service::SessionService; use crate::domain::session::Session; /// Concrete session service backed by generic repository implementations. pub struct SessionServiceImpl { /// Repository for session CRUD operations. pub session_repo: R, /// Repository for session lock acquire/release. pub lock_repo: L, /// Base data directory passed to repository methods. pub base_dir: PathBuf, } impl SessionServiceImpl { /// Create a new session service with the given repositories and base /// data directory. pub fn new(session_repo: R, lock_repo: L, base_dir: PathBuf) -> Self { SessionServiceImpl { session_repo, lock_repo, base_dir, } } } impl SessionService for SessionServiceImpl { fn create_session(&self, title: &str) -> Result { let id = SessionId::new(&Uuid::new_v4().to_string()) .expect("UUID is always a valid session id"); let title_owned = if title.is_empty() { "New Session".to_string() } else { title.to_string() }; let session = Session::new(id.into_string(), title_owned); tracing::debug!(session_id = %session.id, title = %session.title, "creating new session"); self.session_repo .save_session(&self.base_dir, &session)?; // RepositoryError → ServiceError via From Ok(session) } fn list_all(&self) -> Result, ServiceError> { tracing::debug!("listing all sessions"); self.session_repo .list_sessions(&self.base_dir) .map_err(ServiceError::Repository) } fn archive_session(&self, id: SessionId) -> Result<(), ServiceError> { tracing::debug!(session_id = %id, "archiving session"); let mut session = self.session_repo .load_session(&self.base_dir, &id)?; // RepositoryError → ServiceError session.archived = true; let millis = std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) .unwrap_or_default() .as_millis(); session.updated_at = millis.cast_or(i64::MAX); self.session_repo .save_session(&self.base_dir, &session)?; // RepositoryError → ServiceError Ok(()) } }