use rmux_proto::{AttachShellCommand, RmuxError};
use crate::ClientError;
use super::terminal::RawTerminal;
pub(super) trait AttachActionExecutor {
fn handle_lock(&mut self, command: &AttachShellCommand)
-> std::result::Result<(), ClientError>;
fn handle_legacy_lock(&mut self, command: &str) -> std::result::Result<(), ClientError>;
fn handle_suspend(&mut self) -> std::result::Result<(), ClientError>;
fn handle_detach_kill(&mut self) -> std::result::Result<(), ClientError>;
fn handle_detach_exec(
&mut self,
command: &AttachShellCommand,
) -> std::result::Result<(), ClientError>;
fn handle_legacy_detach_exec(&mut self, command: &str) -> std::result::Result<(), ClientError>;
}
#[derive(Debug)]
pub(super) enum AttachAction {
Lock(AttachShellCommand),
LegacyLock(String),
Suspend,
DetachKill,
DetachExec(AttachShellCommand),
LegacyDetachExec(String),
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(super) enum AttachActionOutcome {
Unlock,
Exit,
}
#[derive(Debug)]
pub(super) struct ManagedTerminalActions {
terminal: RawTerminal,
}
impl ManagedTerminalActions {
pub(super) fn new(terminal: RawTerminal) -> Self {
Self { terminal }
}
}
impl AttachActionExecutor for ManagedTerminalActions {
fn handle_lock(
&mut self,
command: &AttachShellCommand,
) -> std::result::Result<(), ClientError> {
self.terminal
.run_lock_command(command)
.map_err(ClientError::from)
}
fn handle_legacy_lock(&mut self, command: &str) -> std::result::Result<(), ClientError> {
self.terminal
.run_legacy_lock_command(command)
.map_err(ClientError::from)
}
fn handle_suspend(&mut self) -> std::result::Result<(), ClientError> {
self.terminal.suspend_self().map_err(ClientError::from)
}
fn handle_detach_kill(&mut self) -> std::result::Result<(), ClientError> {
self.terminal.restore().map_err(ClientError::from)
}
fn handle_detach_exec(
&mut self,
command: &AttachShellCommand,
) -> std::result::Result<(), ClientError> {
self.terminal
.run_detach_exec_command(command)
.map_err(ClientError::from)
}
fn handle_legacy_detach_exec(&mut self, command: &str) -> std::result::Result<(), ClientError> {
self.terminal
.run_legacy_detach_exec_command(command)
.map_err(ClientError::from)
}
}
#[derive(Debug, Default)]
pub(super) struct StreamOnlyActions;
impl AttachActionExecutor for StreamOnlyActions {
fn handle_lock(
&mut self,
_command: &AttachShellCommand,
) -> std::result::Result<(), ClientError> {
Err(unmanaged_terminal_error("lock"))
}
fn handle_legacy_lock(&mut self, _command: &str) -> std::result::Result<(), ClientError> {
Err(unmanaged_terminal_error("lock"))
}
fn handle_suspend(&mut self) -> std::result::Result<(), ClientError> {
Err(unmanaged_terminal_error("suspend"))
}
fn handle_detach_kill(&mut self) -> std::result::Result<(), ClientError> {
Ok(())
}
fn handle_detach_exec(
&mut self,
_command: &AttachShellCommand,
) -> std::result::Result<(), ClientError> {
Err(unmanaged_terminal_error("detach exec"))
}
fn handle_legacy_detach_exec(
&mut self,
_command: &str,
) -> std::result::Result<(), ClientError> {
Err(unmanaged_terminal_error("detach exec"))
}
}
fn unmanaged_terminal_error(action: &str) -> ClientError {
ClientError::Protocol(RmuxError::Decode(format!(
"received unexpected {action} request without a managed terminal"
)))
}
pub(super) fn run_attach_action(
actions: &mut impl AttachActionExecutor,
action: AttachAction,
) -> std::result::Result<AttachActionOutcome, ClientError> {
match action {
AttachAction::Lock(command) => {
actions.handle_lock(&command)?;
Ok(AttachActionOutcome::Unlock)
}
AttachAction::LegacyLock(command) => {
actions.handle_legacy_lock(&command)?;
Ok(AttachActionOutcome::Unlock)
}
AttachAction::Suspend => {
actions.handle_suspend()?;
Ok(AttachActionOutcome::Unlock)
}
AttachAction::DetachKill => {
actions.handle_detach_kill()?;
Ok(AttachActionOutcome::Exit)
}
AttachAction::DetachExec(command) => {
actions.handle_detach_exec(&command)?;
Ok(AttachActionOutcome::Exit)
}
AttachAction::LegacyDetachExec(command) => {
actions.handle_legacy_detach_exec(&command)?;
Ok(AttachActionOutcome::Exit)
}
}
}