refactor: workspace/session/item ... ja ja, i know but now it is nailed.
This commit is contained in:
parent
ca6f8f788f
commit
79cc199d11
6 changed files with 878 additions and 0 deletions
165
src/active_state.rs
Normal file
165
src/active_state.rs
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// src/active_state.rs
|
||||
//
|
||||
// Pure navigation state: tracks which session and item are currently active.
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::workspace::model::{Item, Session, Workspace};
|
||||
|
||||
/// Tracks the active session and item selection.
|
||||
///
|
||||
/// `ActiveState` does not own or store a `Workspace`. All methods that need
|
||||
/// workspace data receive it as a parameter. This keeps navigation state
|
||||
/// cleanly separated from domain data.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ActiveState {
|
||||
active_session: Option<Uuid>,
|
||||
active_item: Option<usize>,
|
||||
}
|
||||
|
||||
impl Default for ActiveState {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl ActiveState {
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
active_session: None,
|
||||
active_item: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Clear all navigation state.
|
||||
pub fn reset(&mut self) {
|
||||
self.active_session = None;
|
||||
self.active_item = None;
|
||||
}
|
||||
|
||||
pub fn select_session(&mut self, workspace: &Workspace, name: &str) -> Result<()> {
|
||||
let session = workspace.session_by_name(name)?;
|
||||
if self.active_session == Some(session.id) {
|
||||
return Ok(());
|
||||
}
|
||||
self.active_session = Some(session.id);
|
||||
self.active_item = if session.items.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(0)
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn select_session_by_id(&mut self, workspace: &Workspace, id: Uuid) -> Result<()> {
|
||||
if self.active_session == Some(id) {
|
||||
return Ok(());
|
||||
}
|
||||
let session = workspace.session(id)?;
|
||||
self.active_session = Some(session.id);
|
||||
self.active_item = if session.items.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(0)
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn clear_session(&mut self) {
|
||||
self.active_session = None;
|
||||
self.active_item = None;
|
||||
}
|
||||
|
||||
pub fn select_item(&mut self, workspace: &Workspace, index: usize) -> Result<()> {
|
||||
let session = self.require_active_session(workspace)?;
|
||||
if index >= session.items.len() {
|
||||
anyhow::bail!(
|
||||
"Item index {index} out of range (session {:?} has {} items)",
|
||||
session.name,
|
||||
session.items.len()
|
||||
);
|
||||
}
|
||||
self.active_item = Some(index);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn next_item(&mut self, workspace: &Workspace) {
|
||||
let count = self.item_count(workspace);
|
||||
if let Some(idx) = self.active_item {
|
||||
if idx + 1 < count {
|
||||
self.active_item = Some(idx + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn previous_item(&mut self) {
|
||||
if let Some(idx) = self.active_item {
|
||||
if idx > 0 {
|
||||
self.active_item = Some(idx - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Adjust the item cursor after an item was removed from the active session.
|
||||
pub fn adjust_after_remove(&mut self, workspace: &Workspace) {
|
||||
let count = self.item_count(workspace);
|
||||
self.active_item = if count == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(self.active_item.unwrap_or(0).min(count - 1))
|
||||
};
|
||||
}
|
||||
|
||||
/// Point the item cursor to the last item in the active session.
|
||||
pub fn select_last_item(&mut self, workspace: &Workspace) {
|
||||
let count = self.item_count(workspace);
|
||||
self.active_item = if count == 0 { None } else { Some(count - 1) };
|
||||
}
|
||||
|
||||
// -- Read-only accessors --
|
||||
|
||||
#[must_use]
|
||||
pub fn active_session_id(&self) -> Option<Uuid> {
|
||||
self.active_session
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn active_item_index(&self) -> Option<usize> {
|
||||
self.active_item
|
||||
}
|
||||
|
||||
/// Returns the active session from the workspace.
|
||||
#[must_use]
|
||||
pub fn active_session<'a>(&self, workspace: &'a Workspace) -> Option<&'a Session> {
|
||||
let id = self.active_session?;
|
||||
workspace.session(id).ok()
|
||||
}
|
||||
|
||||
/// Returns the active item from the active session.
|
||||
#[must_use]
|
||||
pub fn active_item<'a>(&self, workspace: &'a Workspace) -> Option<&'a Item> {
|
||||
let session = self.active_session(workspace)?;
|
||||
let idx = self.active_item?;
|
||||
session.items.get(idx)
|
||||
}
|
||||
|
||||
/// Returns the UUID of the active item.
|
||||
#[must_use]
|
||||
pub fn active_item_id(&self, workspace: &Workspace) -> Option<Uuid> {
|
||||
self.active_item(workspace).map(|i| i.id)
|
||||
}
|
||||
|
||||
// Private helpers
|
||||
|
||||
fn require_active_session<'a>(&self, workspace: &'a Workspace) -> Result<&'a Session> {
|
||||
let id = self.active_session.context("No active session")?;
|
||||
workspace.session(id)
|
||||
}
|
||||
|
||||
fn item_count(&self, workspace: &Workspace) -> usize {
|
||||
self.active_session(workspace).map_or(0, |s| s.items.len())
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue