//! [`CloseProject`] / [`CloseTab`] (ARCHITECTURE §6). //! //! In L2 there are no PTYs to release yet, so closing is essentially *persisting //! the current state*. The use case takes the workspace image to persist (the //! UI owns the windows/tabs arrangement) and saves it through the //! [`ProjectStore`]. It is written so the L3 "release PTYs" step slots in here //! without changing the call sites. use std::sync::Arc; use domain::ports::ProjectStore; use domain::{ProjectId, Workspace}; use crate::error::AppError; /// Input for [`CloseProject::execute`]. #[derive(Debug, Clone, PartialEq)] pub struct CloseProjectInput { /// The project being closed. pub project_id: ProjectId, /// The workspace state to persist (windows/tabs/layouts). `None` skips /// persistence (e.g. the UI has nothing to save). pub workspace: Option, } /// Output of [`CloseProject::execute`]. #[derive(Debug, Clone, PartialEq, Eq)] pub struct CloseProjectOutput { /// The project that was closed. pub project_id: ProjectId, } /// Closes a project: persists the workspace state and releases resources. pub struct CloseProject { store: Arc, } impl CloseProject { /// Builds the use case from its injected port. #[must_use] pub fn new(store: Arc) -> Self { Self { store } } /// Executes the close. /// /// # Errors /// [`AppError::Store`] if persisting the workspace fails. pub async fn execute(&self, input: CloseProjectInput) -> Result { if let Some(workspace) = &input.workspace { self.store.save_workspace(workspace).await?; } // L3 will release the project's PTYs here. Ok(CloseProjectOutput { project_id: input.project_id, }) } } /// Input for [`CloseTab::execute`] — closing one tab (a single open project). #[derive(Debug, Clone, PartialEq)] pub struct CloseTabInput { /// The project shown in the tab being closed. pub project_id: ProjectId, /// The workspace state to persist after the tab is removed. pub workspace: Option, } /// Closes a single tab. In L2 this delegates to the same persistence path as /// [`CloseProject`]; it exists as a distinct intention so the multi-window lot /// (L10) can give it tab-specific behaviour without touching callers. pub struct CloseTab { inner: CloseProject, } impl CloseTab { /// Builds the use case from its injected port. #[must_use] pub fn new(store: Arc) -> Self { Self { inner: CloseProject::new(store), } } /// Executes the tab close. /// /// # Errors /// [`AppError::Store`] if persisting the workspace fails. pub async fn execute(&self, input: CloseTabInput) -> Result { self.inner .execute(CloseProjectInput { project_id: input.project_id, workspace: input.workspace, }) .await } }