//! Parser for the terminal's command line. //! //! The core speaks `Command`; this module turns one typed line into either a //! core command or an action only an interactive frontend can perform //! (connecting, prompting for a password, listing hosts). use crate::client::ui::parse_bet_arg; use crate::client::view::Command; use crate::history::{DEFAULT_HISTORY, MAX_HISTORY}; #[derive(Debug, Clone, PartialEq, Eq)] pub enum Shell { /// A command the core can run on its own. Run(Command), Login, Register, SetCred, Connect(Option), Disconnect, Hosts, ForgetHost(Option), ForgetLogin(Option), Help, Quit, Empty, Usage(String), } /// Split off the first whitespace-delimited word. /// /// The remainder keeps its internal spacing (so `send` can echo a message /// verbatim) but loses the separator run before it. fn split_word(line: &str) -> (&str, &str) { match line.find(char::is_whitespace) { Some(index) => (&line[..index], line[index..].trim_start()), None => (line, ""), } } pub fn parse(line: &str) -> Shell { let line = line.trim(); if line.is_empty() { return Shell::Empty; } let (word, rest) = split_word(line); match word { "play" => { let (game, bet) = split_word(rest); if game.is_empty() { return Shell::Usage("Usage: play [bet]".to_string()); } let bet = if bet.is_empty() { None } else { match parse_bet_arg(bet) { Ok(bet) => Some(bet), Err(message) => return Shell::Usage(message), } }; Shell::Run(Command::Play { game: game.to_string(), bet, }) } "history" => { let (raw, _) = split_word(rest); let limit = if raw.is_empty() { DEFAULT_HISTORY } else { match raw.parse::() { Ok(count) if (1..=MAX_HISTORY).contains(&count) => count, _ => return Shell::Usage(format!("Usage: history [1-{MAX_HISTORY}]")), } }; Shell::Run(Command::History { limit }) } "games" => Shell::Run(Command::Games), "logout" => Shell::Run(Command::Logout), "ping" => Shell::Run(Command::Ping), "send" => { if rest.is_empty() { Shell::Usage("Usage: send ".to_string()) } else { Shell::Run(Command::Echo(rest.to_string())) } } "info" => Shell::Run(Command::Info), "help" => Shell::Help, "connect" => Shell::Connect(optional_word(rest)), "login" => Shell::Login, "register" => Shell::Register, "setcred" => Shell::SetCred, "disconnect" => Shell::Disconnect, "hosts" => Shell::Hosts, "forget-host" => Shell::ForgetHost(optional_word(rest)), "forget-login" => Shell::ForgetLogin(optional_word(rest)), "quit" => Shell::Quit, _ => Shell::Help, } } fn optional_word(rest: &str) -> Option { let (word, _) = split_word(rest); if word.is_empty() { None } else { Some(word.to_string()) } }