rmux-server 0.1.1

Tokio daemon and request dispatcher for the RMUX terminal multiplexer.
Documentation
use std::path::{Path, PathBuf};

use rmux_core::{SessionStore, TargetFindContext};
use rmux_proto::{DeleteBufferRequest, ListBuffersRequest, LoadBufferRequest, Request, RmuxError};

use super::tokens::CommandTokens;
use super::values::{missing_argument, unsupported_flag};
use super::{implicit_pane_target, parse_pane_target};

pub(super) fn parse_set_buffer(mut args: CommandTokens) -> Result<Request, RmuxError> {
    let mut name = None;
    let mut append = false;
    let mut new_name = None;
    let mut set_clipboard = false;
    while let Some(token) = args.peek() {
        match token {
            "--" => {
                let _ = args.optional();
                break;
            }
            "-a" => {
                let _ = args.optional();
                append = true;
            }
            "-b" => {
                let _ = args.optional();
                name = Some(args.required("-b buffer name")?);
            }
            "-n" => {
                let _ = args.optional();
                new_name = Some(args.required("-n buffer name")?);
            }
            "-w" => {
                let _ = args.optional();
                set_clipboard = true;
            }
            _ => break,
        }
    }
    let content_parts = args.remaining();
    if new_name.is_none() && content_parts.is_empty() {
        return Err(missing_argument("set-buffer", "content"));
    }
    let content = content_parts.join(" ");

    Ok(Request::SetBuffer(rmux_proto::SetBufferRequest {
        name,
        content: content.into_bytes(),
        append,
        new_name,
        set_clipboard,
    }))
}

pub(super) fn parse_show_buffer(mut args: CommandTokens) -> Result<Request, RmuxError> {
    let name = parse_optional_buffer_name("show-buffer", &mut args)?;
    args.no_extra("show-buffer")?;
    Ok(Request::ShowBuffer(rmux_proto::ShowBufferRequest { name }))
}

pub(super) fn parse_paste_buffer(
    mut args: CommandTokens,
    sessions: &SessionStore,
    find_context: &TargetFindContext,
) -> Result<Request, RmuxError> {
    let mut name = None;
    let mut target = None;
    let mut delete_after = false;
    let mut separator = None;
    let mut linefeed = false;
    let mut raw = false;
    let mut bracketed = false;

    while let Some(token) = args.optional() {
        match token.as_str() {
            "-b" => name = Some(args.required("-b buffer name")?),
            "-t" => {
                target = Some(parse_pane_target(
                    "paste-buffer",
                    args.required("-t target")?,
                )?)
            }
            "-d" => delete_after = true,
            "-p" => bracketed = true,
            "-r" => linefeed = true,
            "-S" => raw = true,
            "-s" => separator = Some(args.required("-s separator")?),
            flag if flag.starts_with('-') => return Err(unsupported_flag("paste-buffer", flag)),
            _ => {
                return Err(RmuxError::Server(format!(
                    "unexpected argument '{token}' for paste-buffer"
                )));
            }
        }
    }

    Ok(Request::PasteBuffer(rmux_proto::PasteBufferRequest {
        name,
        target: target.unwrap_or(implicit_pane_target(
            sessions,
            find_context,
            "paste-buffer",
        )?),
        delete_after,
        separator,
        linefeed,
        raw,
        bracketed,
    }))
}

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

    while let Some(token) = args.optional() {
        match token.as_str() {
            "-F" => format = Some(args.required("-F format")?),
            "-f" => filter = Some(args.required("-f filter")?),
            "-O" => sort_order = Some(args.required("-O sort-order")?),
            "-r" => reversed = true,
            flag if flag.starts_with('-') => return Err(unsupported_flag("list-buffers", flag)),
            _ => {
                return Err(RmuxError::Server(format!(
                    "unexpected argument '{token}' for list-buffers"
                )));
            }
        }
    }

    Ok(Request::ListBuffers(ListBuffersRequest {
        format,
        filter,
        sort_order,
        reversed,
    }))
}

pub(super) fn parse_delete_buffer(mut args: CommandTokens) -> Result<Request, RmuxError> {
    let name = parse_optional_buffer_name("delete-buffer", &mut args)?;
    args.no_extra("delete-buffer")?;
    Ok(Request::DeleteBuffer(DeleteBufferRequest { name }))
}

pub(super) fn parse_load_buffer(
    mut args: CommandTokens,
    caller_cwd: Option<&Path>,
) -> Result<Request, RmuxError> {
    let mut name = None;
    let mut set_clipboard = false;
    while let Some(token) = args.peek() {
        match token {
            "--" => {
                let _ = args.optional();
                break;
            }
            "-b" => {
                let _ = args.optional();
                name = Some(args.required("-b buffer name")?);
            }
            "-w" => {
                let _ = args.optional();
                set_clipboard = true;
            }
            flag if flag.starts_with('-') => return Err(unsupported_flag("load-buffer", flag)),
            _ => break,
        }
    }
    let path = args.required("load-buffer path")?;
    args.no_extra("load-buffer")?;
    Ok(Request::LoadBuffer(LoadBufferRequest {
        path,
        cwd: caller_cwd.map(PathBuf::from),
        name,
        set_clipboard,
    }))
}

pub(super) fn parse_save_buffer(
    mut args: CommandTokens,
    caller_cwd: Option<&Path>,
) -> Result<Request, RmuxError> {
    let mut name = None;
    let mut append = false;
    while let Some(token) = args.peek() {
        match token {
            "--" => {
                let _ = args.optional();
                break;
            }
            "-b" => {
                let _ = args.optional();
                name = Some(args.required("-b buffer name")?);
            }
            "-a" => {
                let _ = args.optional();
                append = true;
            }
            flag if flag.starts_with('-') => return Err(unsupported_flag("save-buffer", flag)),
            _ => break,
        }
    }
    let path = args.required("save-buffer path")?;
    args.no_extra("save-buffer")?;
    Ok(Request::SaveBuffer(rmux_proto::SaveBufferRequest {
        path,
        cwd: caller_cwd.map(PathBuf::from),
        name,
        append,
    }))
}

fn parse_optional_buffer_name(
    command: &str,
    args: &mut CommandTokens,
) -> Result<Option<String>, RmuxError> {
    let mut name = None;
    while args.peek_is_flag() {
        match args.required("buffer flag")?.as_str() {
            "-b" => name = Some(args.required("-b buffer name")?),
            flag => return Err(unsupported_flag(command, flag)),
        }
    }
    Ok(name)
}