Skip to main content

muster/application/
session_restore.rs

1use std::{collections::HashSet, path::Path};
2
3use getset::Getters;
4use typed_builder::TypedBuilder;
5
6use crate::domain::{
7    agent_session::{AgentSession, AgentSessionId},
8    value::CommandLine,
9};
10
11/// One durable session the active workspace should represent at startup.
12#[derive(Getters, TypedBuilder)]
13#[getset(get = "pub")]
14pub struct SessionRestore {
15    /// Durable record that remains the source of lifecycle truth.
16    session: AgentSession,
17    /// Resume command when the provider identity is usable.
18    #[builder(default)]
19    command: Option<CommandLine>,
20}
21
22/// Selects durable sessions that belong in one workspace without performing I/O.
23pub struct SessionRestorer;
24
25impl SessionRestorer {
26    /// Returns open or pending sessions for `project` not already represented by a pane.
27    pub fn for_project(
28        sessions: impl IntoIterator<Item = AgentSession>,
29        project: &Path,
30        existing: &HashSet<AgentSessionId>,
31        owns_project: impl Fn(&Path, &Path) -> bool,
32        owner_is_live: impl Fn(&AgentSession) -> bool,
33    ) -> Vec<SessionRestore> {
34        sessions
35            .into_iter()
36            .filter(|session| {
37                matches!(
38                    session.state(),
39                    crate::domain::agent_session::AgentSessionState::Pending
40                        | crate::domain::agent_session::AgentSessionState::Open
41                ) && owns_project(session.project(), project)
42                    && !existing.contains(session.id())
43                    && !owner_is_live(session)
44            })
45            .map(|session| {
46                let command = session.restore_command();
47                SessionRestore::builder()
48                    .session(session)
49                    .command(command)
50                    .build()
51            })
52            .collect()
53    }
54}