rmux-server 0.8.0

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

use super::parse_pane_target;
use super::tokens::{parse_compact_flag_cluster, CommandTokens, CompactFlag};
use super::values::unsupported_flag;

pub(super) fn parse_copy_mode(mut args: CommandTokens) -> Result<Request, RmuxError> {
    let mut target = None;
    let mut source = None;
    let page_down = false;
    let mut exit_on_scroll = false;
    let mut hide_position = false;
    let mut mouse_drag_start = false;
    let mut cancel_mode = false;
    let scrollbar_scroll = false;
    let mut page_up = false;

    while let Some(token) = args.peek() {
        match token {
            "--" => {
                let _ = args.optional();
                break;
            }
            "-d" => return Err(unsupported_flag("copy-mode", "-d")),
            "-e" => {
                let _ = args.optional();
                exit_on_scroll = true;
            }
            "-H" => {
                let _ = args.optional();
                hide_position = true;
            }
            "-M" => {
                let _ = args.optional();
                mouse_drag_start = true;
            }
            "-q" => {
                let _ = args.optional();
                cancel_mode = true;
            }
            "-S" => return Err(unsupported_flag("copy-mode", "-S")),
            "-s" => {
                let _ = args.optional();
                source = Some(parse_pane_target(
                    "copy-mode",
                    args.required("-s src-pane")?,
                )?);
            }
            "-t" => {
                let _ = args.optional();
                target = Some(parse_pane_target("copy-mode", args.required("-t target")?)?);
            }
            "-u" => {
                let _ = args.optional();
                page_up = true;
            }
            token => {
                let Some(cluster) = parse_compact_flag_cluster(token, "deHMSqu", "st") else {
                    break;
                };
                let _ = args.optional();
                for flag in cluster {
                    match flag {
                        CompactFlag::Bare('d') => return Err(unsupported_flag("copy-mode", "-d")),
                        CompactFlag::Bare('e') => exit_on_scroll = true,
                        CompactFlag::Bare('H') => hide_position = true,
                        CompactFlag::Bare('M') => mouse_drag_start = true,
                        CompactFlag::Bare('q') => cancel_mode = true,
                        CompactFlag::Bare('S') => return Err(unsupported_flag("copy-mode", "-S")),
                        compact_flag @ CompactFlag::Value { flag: 's', .. } => {
                            source = Some(parse_pane_target(
                                "copy-mode",
                                compact_flag.value_or_next(&mut args, "-s src-pane")?,
                            )?);
                        }
                        compact_flag @ CompactFlag::Value { flag: 't', .. } => {
                            target = Some(parse_pane_target(
                                "copy-mode",
                                compact_flag.value_or_next(&mut args, "-t target")?,
                            )?);
                        }
                        CompactFlag::Bare('u') => page_up = true,
                        CompactFlag::Bare(flag) | CompactFlag::Value { flag, .. } => {
                            return Err(unsupported_flag("copy-mode", &format!("-{flag}")));
                        }
                    }
                }
            }
        }
    }

    args.no_extra("copy-mode")?;
    Ok(Request::CopyMode(rmux_proto::CopyModeRequest {
        target,
        page_down,
        exit_on_scroll,
        hide_position,
        mouse_drag_start,
        cancel_mode,
        scrollbar_scroll,
        source,
        page_up,
    }))
}

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

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

    args.no_extra("clock-mode")?;
    Ok(Request::ClockMode(rmux_proto::ClockModeRequest { target }))
}

#[cfg(test)]
mod tests {
    use super::*;

    fn token(value: &str) -> String {
        value.to_owned()
    }

    #[test]
    fn parse_copy_mode_accepts_tmux_compact_hidden_target() {
        let request = parse_copy_mode(CommandTokens::new(vec![token("-Ht=")]))
            .expect("compact hidden-position target parses");

        let Request::CopyMode(request) = request else {
            panic!("compact target must parse as copy-mode request");
        };
        assert!(request.hide_position);
        assert_eq!(
            request.target,
            Some(parse_pane_target("copy-mode", "=".to_owned()).unwrap())
        );
    }
}