1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
|
//! 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<String>),
Disconnect,
Hosts,
ForgetHost(Option<String>),
ForgetLogin(Option<String>),
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 <game> [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::<u32>() {
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 <message>".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<String> {
let (word, _) = split_word(rest);
if word.is_empty() {
None
} else {
Some(word.to_string())
}
}
|