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
111
112
113
|
mod query;
pub use query::*;
pub enum SwitchName {
Short(char),
Long(String),
}
pub struct Switchboard {
// First positional argument.
pub program: String,
// Positional arguments, stored in reverse order.
pub positional: Vec<String>,
// Named arguments, stored in forward order.
pub switches: Vec<(SwitchName, Option<String>)>,
// All arguments following a '--' token, stored in forward order.
pub unprocessed: Option<Vec<String>>,
}
impl Switchboard {
pub fn parse(mut args: Vec<String>) -> Self {
let mut positional = Vec::new();
let mut switches = Vec::new();
let mut unprocessed = None;
args.reverse();
while let Some(arg) = args.pop() {
if arg.is_empty() {
continue;
} else if arg == "--" {
args.reverse();
unprocessed = Some(args);
break;
} else if let Some(arg) = arg.strip_prefix("--") {
let (name, value) = match arg.split_once("=") {
Some((name, "")) => (name, None),
Some((name, value)) => (name, Some(value.to_string())),
None => (arg, None),
};
if name.is_empty() { continue }
switches.push((SwitchName::Long(name.to_string()), value));
} else if let Some(arg) = arg.strip_prefix("-") {
let (name, value) = match arg.split_once("=") {
Some((name, "")) => (name, None),
Some((name, value)) => (name, Some(value.to_string())),
None => (arg, None),
};
let chars: Vec<char> = name.chars().collect();
if chars.len() != 1 { continue }
switches.push((SwitchName::Short(chars[0]), value));
} else {
positional.push(arg)
}
}
// Reverse order of positional arguments and move program name.
positional.reverse();
let program = positional.pop().unwrap();
Self {
program, positional, switches, unprocessed,
}
}
pub fn from_env() -> Self {
let mut args = Vec::new();
for arg_os in std::env::args_os() {
args.push(arg_os.to_string_lossy().to_string());
}
Self::parse(args)
}
pub fn named(&mut self, name: &str) -> NamedSwitchQuery {
validate_name(name);
NamedSwitchQuery {
switchboard: self,
name: name.to_string(),
short: None,
default: None,
quick: None,
}
}
pub fn positional(&mut self, name: &str) -> PositionalSwitchQuery {
validate_name(name);
PositionalSwitchQuery {
switchboard: self,
name: name.to_string(),
default: None,
}
}
/// Check the next positional argument without consuming it.
pub fn peek(&self) -> Option<&str> {
self.positional.last().map(|p| p.as_str())
}
/// Consume the next positional argument.
pub fn pop(&mut self) {
self.positional.pop();
}
}
pub enum SwitchboardError {
}
fn validate_name(name: &str) {
if !name.chars().all(|c| c.is_ascii_lowercase() || c.is_digit(10) || c == '-') {
panic!("Invalid name for argument: {name:?}");
}
}
|