rmux-server 0.10.0

Tokio daemon and request dispatcher for the RMUX terminal multiplexer.
Documentation
use rmux_proto::{ErrorResponse, HookName, Response, ScopeSelector, Target};

use super::super::{
    client_environment_snapshot, client_spawn_environment,
    scripting_support::{format_context_for_target, render_start_directory_template},
    RequestHandler,
};
#[cfg(windows)]
use super::format_references_pane_pid;
use crate::format_runtime::render_runtime_template;
use crate::hook_runtime::PendingInlineHookFormat;
use crate::pane_terminal_lookup::pane_id_for_target;

impl RequestHandler {
    pub(in crate::handler) async fn handle_pipe_pane(
        &self,
        _requester_pid: u32,
        request: rmux_proto::PipePaneRequest,
    ) -> Response {
        let session_name = request.target.session_name().clone();
        let target = request.target.clone();
        let attached_count = self.attached_count(&session_name).await;
        let write_to_pipe = if !request.stdin && !request.stdout {
            true
        } else {
            request.stdout
        };
        let response = {
            let mut state = self.state.lock().await;
            let command = match request.command.as_deref() {
                Some(command) => {
                    let runtime = match format_context_for_target(
                        &state,
                        &Target::Pane(target.clone()),
                        attached_count,
                    ) {
                        Ok(runtime) => runtime,
                        Err(error) => return Response::Error(ErrorResponse { error }),
                    };
                    Some(render_runtime_template(command, &runtime, true))
                }
                None => None,
            };

            match state.pipe_pane(
                target.clone(),
                command,
                request.stdin,
                write_to_pipe,
                request.once,
            ) {
                Ok(response) => Response::PipePane(response),
                Err(error) => Response::Error(ErrorResponse { error }),
            }
        };

        if matches!(response, Response::PipePane(_)) {
            self.queue_inline_hook(
                HookName::AfterPipePane,
                ScopeSelector::Pane(target.clone()),
                Some(Target::Pane(target)),
                PendingInlineHookFormat::AfterCommand,
            );
        }

        response
    }

    pub(in crate::handler) async fn handle_respawn_pane(
        &self,
        requester_pid: u32,
        mut request: rmux_proto::RespawnPaneRequest,
    ) -> Response {
        #[cfg(windows)]
        if request.start_directory.as_ref().is_some_and(|path| {
            format_references_pane_pid(Some(path.as_os_str().to_string_lossy().as_ref()))
        }) {
            self.wait_for_windows_deferred_all_pane_pids().await;
        }
        let session_name = request.target.session_name().clone();
        let target = request.target.clone();
        let socket_path = self.socket_path();
        let client_environment = client_environment_snapshot(requester_pid);
        let spawn_environment = client_spawn_environment(client_environment.as_ref());
        let attached_count = self.attached_count(&session_name).await;
        let (response, respawned_pane_id) = {
            let mut state = self.state.lock().await;
            let target_window = rmux_proto::WindowTarget::with_window(
                request.target.session_name().clone(),
                request.target.window_index(),
            );
            if let Err(error) =
                super::super::require_expected_window_identity(&state, &target_window)
            {
                return Response::Error(ErrorResponse { error });
            }
            request.start_directory = match render_start_directory_template(
                &state,
                &Target::Pane(target),
                attached_count,
                request.start_directory,
            ) {
                Ok(start_directory) => start_directory,
                Err(error) => return Response::Error(ErrorResponse { error }),
            };
            let pane_id = match pane_id_for_target(
                &state.sessions,
                request.target.session_name(),
                request.target.window_index(),
                request.target.pane_index(),
            ) {
                Ok(pane_id) => pane_id,
                Err(error) => return Response::Error(ErrorResponse { error }),
            };
            match state.respawn_pane(
                request,
                &socket_path,
                spawn_environment.as_ref(),
                Some(self.pane_alert_callback()),
                Some(self.pane_exit_callback()),
                |_, _| {},
            ) {
                Ok(response) => {
                    self.record_pane_respawn_boundary(pane_id);
                    state.retire_respawned_lifecycle_panes(&[pane_id]);
                    (Response::RespawnPane(response), Some(pane_id))
                }
                Err(error) => (Response::Error(ErrorResponse { error }), None),
            }
        };

        if respawned_pane_id.is_some() {
            self.refresh_attached_session(&session_name).await;
        }

        response
    }
}