rmux-server 0.9.0

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

use rmux_core::{HookBindingView, HookScopeIdentity, LifecycleEvent, PaneId, WindowId};
use rmux_proto::{HookName, PaneTarget, RmuxError, ScopeSelector, Target, WindowTarget};

use crate::pane_terminals::{session_not_found, HandlerState};

impl crate::handler::RequestHandler {
    pub(in crate::handler) async fn refresh_hook_identity_aliases(&self) {
        let mut state = self.state.lock().await;
        prune_dead_hook_identities(&mut state);
    }
}

pub(in crate::handler) fn resolve_hook_scope_identity(
    state: &HandlerState,
    scope: &ScopeSelector,
) -> Result<HookScopeIdentity, RmuxError> {
    match scope {
        ScopeSelector::Global => Ok(HookScopeIdentity::Global),
        ScopeSelector::Session(session_name) => {
            if state.sessions.contains_session(session_name) {
                Ok(HookScopeIdentity::Session(session_name.clone()))
            } else {
                Err(session_not_found(session_name))
            }
        }
        ScopeSelector::Window(target) => {
            let session = state
                .sessions
                .session(target.session_name())
                .ok_or_else(|| session_not_found(target.session_name()))?;
            let window = session.window_at(target.window_index()).ok_or_else(|| {
                RmuxError::invalid_target(
                    target.to_string(),
                    "window index does not exist in session",
                )
            })?;
            Ok(HookScopeIdentity::Window {
                session_name: target.session_name().clone(),
                window_id: window.id(),
            })
        }
        ScopeSelector::Pane(target) => {
            let session = state
                .sessions
                .session(target.session_name())
                .ok_or_else(|| session_not_found(target.session_name()))?;
            let window = session.window_at(target.window_index()).ok_or_else(|| {
                RmuxError::invalid_target(
                    format!("{}:{}", target.session_name(), target.window_index()),
                    "window index does not exist in session",
                )
            })?;
            let pane = window.pane(target.pane_index()).ok_or_else(|| {
                RmuxError::invalid_target(
                    target.to_string(),
                    "pane index does not exist in session",
                )
            })?;
            Ok(HookScopeIdentity::Pane {
                session_name: target.session_name().clone(),
                window_id: window.id(),
                pane_id: pane.id(),
            })
        }
    }
}

pub(in crate::handler) fn resolve_hook_scope_identity_for_hook(
    state: &HandlerState,
    scope: &ScopeSelector,
    hook: HookName,
) -> Result<HookScopeIdentity, RmuxError> {
    let natural_scope = match scope {
        ScopeSelector::Global => ScopeSelector::Global,
        ScopeSelector::Session(session_name) => {
            let session = state
                .sessions
                .session(session_name)
                .ok_or_else(|| session_not_found(session_name))?;
            rmux_core::hook_natural_scope_for_session_target(
                hook,
                session_name.clone(),
                session.active_window_index(),
                session.active_pane_index(),
            )
        }
        ScopeSelector::Window(target) => {
            rmux_core::hook_explicit_scope_for_target(hook, Target::Window(target.clone()))
        }
        ScopeSelector::Pane(target) => {
            rmux_core::hook_explicit_scope_for_target(hook, Target::Pane(target.clone()))
        }
    };
    resolve_hook_scope_identity(state, &natural_scope)
}

pub(in crate::handler) fn lifecycle_hook_scope_identity(
    state: &HandlerState,
    scope: &ScopeSelector,
    event: &LifecycleEvent,
) -> Option<HookScopeIdentity> {
    resolve_hook_scope_identity(state, scope).ok().or_else(|| {
        let session_name = event.session_name()?.clone();
        match scope {
            ScopeSelector::Global => Some(HookScopeIdentity::Global),
            ScopeSelector::Session(_) => Some(HookScopeIdentity::Session(session_name)),
            ScopeSelector::Window(_) => Some(HookScopeIdentity::Window {
                session_name,
                window_id: WindowId::new(event.window_id()?),
            }),
            ScopeSelector::Pane(_) => Some(HookScopeIdentity::Pane {
                session_name,
                window_id: WindowId::new(event.window_id()?),
                pane_id: PaneId::new(event.pane_id()?),
            }),
        }
    })
}

pub(in crate::handler) fn hook_bindings_view(
    state: &HandlerState,
    scope: &ScopeSelector,
    hook: Option<HookName>,
) -> Result<Vec<HookBindingView>, RmuxError> {
    let identity = match hook {
        Some(hook) => resolve_hook_scope_identity_for_hook(state, scope, hook)?,
        None => resolve_hook_scope_identity(state, scope)?,
    };
    Ok(state.hooks.bindings_view_with_identity(&identity, hook))
}

pub(in crate::handler) fn prune_dead_hook_identities(state: &mut HandlerState) {
    let mut window_aliases = HashMap::new();
    let mut pane_aliases = HashMap::new();
    for (session_name, session) in state.sessions.iter() {
        for (window_index, window) in session.windows() {
            window_aliases.insert(
                WindowTarget::with_window(session_name.clone(), *window_index),
                window.id(),
            );
            for pane in window.panes() {
                pane_aliases.insert(
                    PaneTarget::with_window(session_name.clone(), *window_index, pane.index()),
                    (window.id(), pane.id()),
                );
            }
        }
    }
    state
        .hooks
        .replace_identity_aliases(window_aliases, pane_aliases);
}