rmux-server 0.1.1

Tokio daemon and request dispatcher for the RMUX terminal multiplexer.
Documentation
use rmux_proto::request::{
    DetachClientExtRequest, ListClientsRequest, RefreshClientRequest, SuspendClientRequest,
    SwitchClientExt3Request,
};
use rmux_proto::{Request, RmuxError};

use super::parse_session_name;
use super::tokens::CommandTokens;
use super::values::unsupported_flag;

pub(super) fn parse_switch_client(mut args: CommandTokens) -> Result<Request, RmuxError> {
    let mut target = None;
    let mut target_client = None;
    let mut key_table = None;
    let mut last_session = false;
    let mut next_session = false;
    let mut previous_session = false;
    let mut toggle_read_only = false;
    let mut sort_order = None;
    let mut skip_environment_update = false;
    let mut zoom = false;

    while let Some(token) = args.peek() {
        match token {
            "--" => {
                let _ = args.optional();
                break;
            }
            "-c" => {
                let _ = args.optional();
                target_client = Some(args.required("-c target-client")?);
            }
            "-E" => {
                let _ = args.optional();
                skip_environment_update = true;
            }
            "-l" => {
                let _ = args.optional();
                last_session = true;
            }
            "-n" => {
                let _ = args.optional();
                next_session = true;
            }
            "-O" => {
                let _ = args.optional();
                sort_order = Some(args.required("-O sort-order")?);
            }
            "-p" => {
                let _ = args.optional();
                previous_session = true;
            }
            "-r" => {
                let _ = args.optional();
                toggle_read_only = true;
            }
            "-T" => {
                let _ = args.optional();
                key_table = Some(args.required("-T key-table")?);
            }
            "-t" => {
                let _ = args.optional();
                target = Some(args.required("-t target")?);
            }
            "-Z" => {
                let _ = args.optional();
                zoom = true;
            }
            flag if flag.starts_with('-') => return Err(unsupported_flag("switch-client", flag)),
            _ => break,
        }
    }
    args.no_extra("switch-client")?;

    let selector_count = usize::from(target.is_some())
        + usize::from(last_session)
        + usize::from(next_session)
        + usize::from(previous_session);
    if selector_count > 1 {
        return Err(RmuxError::Server(
            "switch-client accepts only one of -t, -l, -n, or -p".to_owned(),
        ));
    }

    Ok(Request::SwitchClientExt3(SwitchClientExt3Request {
        target_client,
        target,
        key_table,
        last_session,
        next_session,
        previous_session,
        toggle_read_only,
        sort_order,
        skip_environment_update,
        zoom,
    }))
}

pub(super) fn parse_detach_client(mut args: CommandTokens) -> Result<Request, RmuxError> {
    let mut target_client = None;
    let mut all_other_clients = false;
    let mut target_session = None;
    let mut kill_on_detach = false;
    let mut exec_command = None;

    while let Some(token) = args.peek() {
        match token {
            "--" => {
                let _ = args.optional();
                break;
            }
            "-a" => {
                let _ = args.optional();
                all_other_clients = true;
            }
            "-E" => {
                let _ = args.optional();
                exec_command = Some(args.required("-E shell-command")?);
            }
            "-P" => {
                let _ = args.optional();
                kill_on_detach = true;
            }
            "-s" => {
                let _ = args.optional();
                target_session = Some(parse_session_name(args.required("-s target-session")?)?);
            }
            "-t" => {
                let _ = args.optional();
                target_client = Some(args.required("-t target-client")?);
            }
            flag if flag.starts_with('-') => return Err(unsupported_flag("detach-client", flag)),
            _ => break,
        }
    }
    args.no_extra("detach-client")?;
    Ok(Request::DetachClientExt(DetachClientExtRequest {
        target_client,
        all_other_clients,
        target_session,
        kill_on_detach,
        exec_command,
    }))
}

pub(super) fn parse_refresh_client(mut args: CommandTokens) -> Result<Request, RmuxError> {
    let mut target_client = None;
    let mut subscriptions = Vec::new();
    let mut subscriptions_format = Vec::new();
    let mut clear_pan = false;
    let mut control_size = None;
    let mut pan_down = false;
    let mut flags = None;
    let mut flags_alias = None;
    let mut colour_report = None;
    let mut clipboard_query = false;
    let mut pan_left = false;
    let mut pan_right = false;
    let mut status_only = false;
    let mut pan_up = false;

    while let Some(token) = args.peek() {
        match token {
            "--" => {
                let _ = args.optional();
                break;
            }
            "-A" => {
                let _ = args.optional();
                subscriptions.push(args.required("-A pane:state")?);
            }
            "-B" => {
                let _ = args.optional();
                subscriptions_format.push(args.required("-B name:pane:format")?);
            }
            "-c" => {
                let _ = args.optional();
                clear_pan = true;
            }
            "-C" => {
                let _ = args.optional();
                control_size = Some(args.required("-C widthxheight")?);
            }
            "-D" => {
                let _ = args.optional();
                pan_down = true;
            }
            "-f" => {
                let _ = args.optional();
                flags = Some(args.required("-f flags")?);
            }
            "-F" => {
                let _ = args.optional();
                flags_alias = Some(args.required("-F flags")?);
            }
            "-l" => {
                let _ = args.optional();
                clipboard_query = true;
            }
            "-L" => {
                let _ = args.optional();
                pan_left = true;
            }
            "-r" => {
                let _ = args.optional();
                colour_report = Some(args.required("-r pane")?);
            }
            "-R" => {
                let _ = args.optional();
                pan_right = true;
            }
            "-S" => {
                let _ = args.optional();
                status_only = true;
            }
            "-t" => {
                let _ = args.optional();
                target_client = Some(args.required("-t target-client")?);
            }
            "-U" => {
                let _ = args.optional();
                pan_up = true;
            }
            flag if flag.starts_with('-') => return Err(unsupported_flag("refresh-client", flag)),
            _ => break,
        }
    }

    let adjustment = args
        .optional()
        .map(|value| {
            value.parse::<u32>().map_err(|_| {
                RmuxError::Server(format!("invalid refresh-client adjustment '{value}'"))
            })
        })
        .transpose()?;
    args.no_extra("refresh-client")?;

    Ok(Request::RefreshClient(RefreshClientRequest {
        target_client,
        adjustment,
        clear_pan,
        pan_left,
        pan_right,
        pan_up,
        pan_down,
        status_only,
        clipboard_query,
        flags,
        flags_alias,
        subscriptions,
        subscriptions_format,
        control_size,
        colour_report,
    }))
}

pub(super) fn parse_list_clients(mut args: CommandTokens) -> Result<Request, RmuxError> {
    let mut format = None;
    let mut filter = None;
    let mut sort_order = None;
    let mut reversed = false;
    let mut target_session = None;

    while let Some(token) = args.peek() {
        match token {
            "--" => {
                let _ = args.optional();
                break;
            }
            "-F" => {
                let _ = args.optional();
                format = Some(args.required("-F format")?);
            }
            "-f" => {
                let _ = args.optional();
                filter = Some(args.required("-f filter")?);
            }
            "-O" => {
                let _ = args.optional();
                sort_order = Some(args.required("-O sort-order")?);
            }
            "-r" => {
                let _ = args.optional();
                reversed = true;
            }
            "-t" => {
                let _ = args.optional();
                target_session = Some(parse_session_name(args.required("-t target-session")?)?);
            }
            flag if flag.starts_with('-') => return Err(unsupported_flag("list-clients", flag)),
            _ => break,
        }
    }
    args.no_extra("list-clients")?;
    Ok(Request::ListClients(ListClientsRequest {
        format,
        filter,
        sort_order,
        reversed,
        target_session,
    }))
}

pub(super) fn parse_suspend_client(mut args: CommandTokens) -> Result<Request, RmuxError> {
    let mut target_client = None;

    while let Some(token) = args.peek() {
        match token {
            "--" => {
                let _ = args.optional();
                break;
            }
            "-t" => {
                let _ = args.optional();
                target_client = Some(args.required("-t target-client")?);
            }
            flag if flag.starts_with('-') => return Err(unsupported_flag("suspend-client", flag)),
            _ => break,
        }
    }
    args.no_extra("suspend-client")?;
    Ok(Request::SuspendClient(SuspendClientRequest {
        target_client,
    }))
}

pub(super) fn parse_lock_client(mut args: CommandTokens) -> Result<Request, RmuxError> {
    let mut target_client = None;

    while let Some(token) = args.peek() {
        match token {
            "--" => {
                let _ = args.optional();
                break;
            }
            "-t" => {
                let _ = args.optional();
                target_client = Some(args.required("-t target-client")?);
            }
            _ => break,
        }
    }

    args.no_extra("lock-client")?;
    Ok(Request::LockClient(rmux_proto::LockClientRequest {
        target_client: target_client.unwrap_or_else(|| "=".to_owned()),
    }))
}

pub(super) fn parse_server_access(mut args: CommandTokens) -> Result<Request, RmuxError> {
    let mut add = false;
    let mut deny = false;
    let mut list = false;
    let mut read_only = false;
    let mut write = false;

    while let Some(token) = args.peek() {
        match token {
            "--" => {
                let _ = args.optional();
                break;
            }
            "-a" => {
                let _ = args.optional();
                add = true;
            }
            "-d" => {
                let _ = args.optional();
                deny = true;
            }
            "-l" => {
                let _ = args.optional();
                list = true;
            }
            "-r" => {
                let _ = args.optional();
                read_only = true;
            }
            "-w" => {
                let _ = args.optional();
                write = true;
            }
            _ => break,
        }
    }

    let user = args.optional();
    args.no_extra("server-access")?;

    Ok(Request::ServerAccess(rmux_proto::ServerAccessRequest {
        add,
        deny,
        list,
        read_only,
        write,
        user,
    }))
}