rmux-server 0.1.1

Tokio daemon and request dispatcher for the RMUX terminal multiplexer.
Documentation
use std::collections::HashMap;

use super::super::prompt_support::ClientPromptState;
use super::super::RequestHandler;
use crate::pane_io::AttachControl;

impl RequestHandler {
    pub(crate) async fn refresh_attached_session(&self, session_name: &rmux_proto::SessionName) {
        let attached_count = { self.attached_count(session_name).await };
        let prompts = {
            let active_attach = self.active_attach.lock().await;
            active_attach
                .by_pid
                .iter()
                .filter(|(_, active)| &active.session_name == session_name && !active.suspended)
                .map(|(pid, active)| {
                    (
                        *pid,
                        active
                            .prompt
                            .as_ref()
                            .map(ClientPromptState::rendered_prompt),
                        active.terminal_context.clone(),
                        active.mode_tree_state_id,
                        active.mode_tree.is_some(),
                    )
                })
                .collect::<Vec<_>>()
        };
        let mode_tree_pids = {
            let active_attach = self.active_attach.lock().await;
            active_attach
                .by_pid
                .iter()
                .filter_map(|(pid, active)| {
                    (&active.session_name == session_name
                        && !active.suspended
                        && active.mode_tree.is_some())
                    .then_some(*pid)
                })
                .collect::<Vec<_>>()
        };
        let overlay_pids = {
            let active_attach = self.active_attach.lock().await;
            active_attach
                .by_pid
                .iter()
                .filter_map(|(pid, active)| {
                    (&active.session_name == session_name
                        && !active.suspended
                        && active.overlay.is_some())
                    .then_some(*pid)
                })
                .collect::<Vec<_>>()
        };
        let targets = {
            let state = self.state.lock().await;
            let mut targets = Vec::with_capacity(prompts.len());
            for (pid, prompt, terminal_context, mode_tree_state_id, mode_tree_active) in &prompts {
                let Ok(mut target) = super::attach_target_for_session_with_prompt(
                    &state,
                    session_name,
                    attached_count,
                    prompt.as_ref(),
                    terminal_context,
                ) else {
                    return;
                };
                if *mode_tree_active {
                    target.persistent_overlay_state_id = Some(*mode_tree_state_id);
                }
                targets.push((*pid, target));
            }
            targets
        };

        let mut target_by_pid = targets.into_iter().collect::<HashMap<_, _>>();
        let mut active_attach = self.active_attach.lock().await;
        active_attach.by_pid.retain(|pid, active| {
            if &active.session_name != session_name {
                return true;
            }
            if active.suspended {
                return true;
            }
            let Some(target) = target_by_pid.remove(pid) else {
                return false;
            };
            active.render_generation = active.render_generation.saturating_add(1);
            active
                .control_tx
                .send(AttachControl::switch(target))
                .is_ok()
        });
        drop(active_attach);
        self.refresh_clock_overlays_for_session(session_name).await;
        for attach_pid in mode_tree_pids {
            let _ = self.refresh_mode_tree_overlay_if_active(attach_pid).await;
        }
        for attach_pid in overlay_pids {
            let _ = self.refresh_interactive_overlay_if_active(attach_pid).await;
        }
        self.refresh_control_session(session_name).await;
    }

    pub(crate) async fn refresh_attached_client(
        &self,
        attach_pid: u32,
        session_name: &rmux_proto::SessionName,
    ) {
        let attached_count = self.attached_count(session_name).await;
        let prompt = {
            let active_attach = self.active_attach.lock().await;
            active_attach
                .by_pid
                .get(&attach_pid)
                .filter(|active| &active.session_name == session_name && !active.suspended)
                .map(|active| {
                    (
                        active
                            .prompt
                            .as_ref()
                            .map(ClientPromptState::rendered_prompt),
                        active.terminal_context.clone(),
                        active.mode_tree_state_id,
                        active.mode_tree.is_some(),
                    )
                })
        };
        let Some((prompt, terminal_context, mode_tree_state_id, mode_tree_active)) = prompt else {
            return;
        };
        let target = {
            let state = self.state.lock().await;
            super::attach_target_for_session_with_prompt(
                &state,
                session_name,
                attached_count,
                prompt.as_ref(),
                &terminal_context,
            )
            .ok()
        };
        let Some(mut target) = target else {
            return;
        };
        if mode_tree_active {
            target.persistent_overlay_state_id = Some(mode_tree_state_id);
        }

        let mut active_attach = self.active_attach.lock().await;
        let remove = match active_attach.by_pid.get_mut(&attach_pid) {
            Some(active) if &active.session_name == session_name && !active.suspended => {
                active.render_generation = active.render_generation.saturating_add(1);
                active
                    .control_tx
                    .send(AttachControl::switch(target))
                    .is_err()
            }
            _ => false,
        };
        if remove {
            active_attach.by_pid.remove(&attach_pid);
        }
        drop(active_attach);
        self.refresh_clock_overlays_for_session(session_name).await;
        let _ = self.refresh_mode_tree_overlay_if_active(attach_pid).await;
        let _ = self.refresh_interactive_overlay_if_active(attach_pid).await;
    }

    pub(crate) async fn refresh_attached_client_base_only(
        &self,
        attach_pid: u32,
        session_name: &rmux_proto::SessionName,
    ) {
        let attached_count = self.attached_count(session_name).await;
        let prompt = {
            let active_attach = self.active_attach.lock().await;
            active_attach
                .by_pid
                .get(&attach_pid)
                .filter(|active| &active.session_name == session_name && !active.suspended)
                .map(|active| {
                    (
                        active
                            .prompt
                            .as_ref()
                            .map(ClientPromptState::rendered_prompt),
                        active.terminal_context.clone(),
                        active.mode_tree_state_id,
                        active.mode_tree.is_some(),
                    )
                })
        };
        let Some((prompt, terminal_context, mode_tree_state_id, mode_tree_active)) = prompt else {
            return;
        };
        let target = {
            let state = self.state.lock().await;
            super::attach_target_for_session_with_prompt(
                &state,
                session_name,
                attached_count,
                prompt.as_ref(),
                &terminal_context,
            )
            .ok()
        };
        let Some(mut target) = target else {
            return;
        };
        if mode_tree_active {
            target.persistent_overlay_state_id = Some(mode_tree_state_id);
        }

        let mut active_attach = self.active_attach.lock().await;
        let remove = match active_attach.by_pid.get_mut(&attach_pid) {
            Some(active) if &active.session_name == session_name && !active.suspended => {
                active.render_generation = active.render_generation.saturating_add(1);
                active
                    .control_tx
                    .send(AttachControl::switch(target))
                    .is_err()
            }
            _ => false,
        };
        if remove {
            active_attach.by_pid.remove(&attach_pid);
        }
        drop(active_attach);
        self.refresh_clock_overlays_for_session(session_name).await;
    }

    pub(in crate::handler) async fn refresh_all_attached_sessions(&self) {
        let session_names = {
            let active_attach = self.active_attach.lock().await;
            active_attach
                .by_pid
                .values()
                .map(|active| active.session_name.clone())
                .collect::<Vec<_>>()
        };

        for session_name in session_names {
            self.refresh_attached_session(&session_name).await;
        }
        self.refresh_all_control_sessions().await;
    }

    pub(in crate::handler) async fn refresh_persistent_overlays_for_session(
        &self,
        session_name: &rmux_proto::SessionName,
    ) {
        let (mode_tree_pids, overlay_pids) = {
            let active_attach = self.active_attach.lock().await;
            let mode_tree_pids = active_attach
                .by_pid
                .iter()
                .filter_map(|(pid, active)| {
                    (&active.session_name == session_name
                        && !active.suspended
                        && active.mode_tree.is_some())
                    .then_some(*pid)
                })
                .collect::<Vec<_>>();
            let overlay_pids = active_attach
                .by_pid
                .iter()
                .filter_map(|(pid, active)| {
                    (&active.session_name == session_name
                        && !active.suspended
                        && active.overlay.is_some())
                    .then_some(*pid)
                })
                .collect::<Vec<_>>();
            (mode_tree_pids, overlay_pids)
        };

        for attach_pid in mode_tree_pids {
            let _ = self.refresh_mode_tree_overlay_if_active(attach_pid).await;
        }
        for attach_pid in overlay_pids {
            let _ = self.refresh_interactive_overlay_if_active(attach_pid).await;
        }
    }
}