taskers_core/
terminal_session_manager.rs1use std::{
2 collections::HashMap,
3 sync::{Arc, Mutex},
4};
5
6use anyhow::Result;
7use taskers_domain::{AppModel, PaneKind, SessionId, SurfaceId};
8use taskers_runtime::TerminalSessionClient;
9
10#[derive(Clone)]
11pub struct TerminalSessionManager {
12 client: Option<TerminalSessionClient>,
13 inner: Arc<Mutex<HashMap<SurfaceId, SessionId>>>,
14}
15
16impl TerminalSessionManager {
17 pub fn new(client: Option<TerminalSessionClient>) -> Self {
18 Self {
19 client,
20 inner: Arc::new(Mutex::new(HashMap::new())),
21 }
22 }
23
24 pub fn sync_model(&self, model: &AppModel) -> Result<()> {
25 let current = model_terminal_sessions(model);
26 let removed = {
27 let mut inner = self
28 .inner
29 .lock()
30 .expect("terminal session manager mutex poisoned");
31 let removed = inner
32 .iter()
33 .filter_map(|(surface_id, session_id)| {
34 (!current.contains_key(surface_id)).then_some(*session_id)
35 })
36 .collect::<Vec<_>>();
37 *inner = current;
38 removed
39 };
40
41 let Some(client) = self.client.as_ref() else {
42 return Ok(());
43 };
44 for session_id in removed {
45 if let Err(error) = client.terminate_session(&session_id.to_string()) {
46 eprintln!(
47 "terminal session cleanup failed for {}: {error:#}",
48 session_id
49 );
50 }
51 }
52 Ok(())
53 }
54}
55
56fn model_terminal_sessions(model: &AppModel) -> HashMap<SurfaceId, SessionId> {
57 model
58 .workspaces
59 .values()
60 .flat_map(|workspace| workspace.panes.values())
61 .flat_map(|pane| pane.surfaces.values())
62 .filter(|surface| surface.kind == PaneKind::Terminal)
63 .map(|surface| (surface.id, surface.session_id))
64 .collect()
65}