halley-cli 0.1.0

Command-line interface for interacting with and controlling the Halley Wayland compositor.
use halley_ipc::{NodeRequest, Request};

use crate::help::HelpTopic;
use crate::parse::{
    ParseOutcome, UsageError, contains_help_flag, parse_move_direction, parse_output_option,
    parse_selector_flags,
};

pub(crate) fn parse_node_request(args: &[String]) -> Result<ParseOutcome, UsageError> {
    match args.first().map(String::as_str) {
        None | Some("-h" | "--help") => Ok(ParseOutcome::Help(HelpTopic::Node)),
        Some("list") => parse_node_list(&args[1..]),
        Some("info") => parse_node_selector_command(&args[1..], NodeSelectorCommand::Info),
        Some("focus") => parse_node_selector_command(&args[1..], NodeSelectorCommand::Focus),
        Some("close") => parse_node_selector_command(&args[1..], NodeSelectorCommand::Close),
        Some("move") => parse_node_move(&args[1..]),
        Some(other) => Err(UsageError::new(
            format!("unknown node command: {other}"),
            HelpTopic::Node,
        )),
    }
}

fn parse_node_list(args: &[String]) -> Result<ParseOutcome, UsageError> {
    if contains_help_flag(args) {
        return Ok(ParseOutcome::Help(HelpTopic::NodeList));
    }
    let output = parse_output_option(args, HelpTopic::NodeList)?;
    Ok(ParseOutcome::Request(Request::Node(NodeRequest::List {
        output,
    })))
}

fn parse_node_move(args: &[String]) -> Result<ParseOutcome, UsageError> {
    if args.is_empty() || contains_help_flag(args) {
        return Ok(ParseOutcome::Help(HelpTopic::NodeMove));
    }
    let direction = parse_move_direction(&args[0])?;
    let (selector, output, _json) = parse_selector_flags(&args[1..], HelpTopic::NodeMove)?;
    Ok(ParseOutcome::Request(Request::Node(NodeRequest::Move {
        direction,
        selector,
        output,
    })))
}

#[derive(Clone, Copy)]
enum NodeSelectorCommand {
    Info,
    Focus,
    Close,
}

impl NodeSelectorCommand {
    fn help_topic(self) -> HelpTopic {
        match self {
            Self::Info => HelpTopic::NodeInfo,
            Self::Focus => HelpTopic::NodeFocus,
            Self::Close => HelpTopic::NodeClose,
        }
    }
}

fn parse_node_selector_command(
    args: &[String],
    command: NodeSelectorCommand,
) -> Result<ParseOutcome, UsageError> {
    if contains_help_flag(args) {
        return Ok(ParseOutcome::Help(command.help_topic()));
    }
    let (selector, output, _json) = parse_selector_flags(args, command.help_topic())?;
    Ok(ParseOutcome::Request(Request::Node(match command {
        NodeSelectorCommand::Info => NodeRequest::Info { selector, output },
        NodeSelectorCommand::Focus => NodeRequest::Focus { selector, output },
        NodeSelectorCommand::Close => NodeRequest::Close { selector, output },
    })))
}