1use std::env;
2use std::collections::HashMap;
3
4use super::parameter::Parameter;
5use super::args::Args;
6
7#[cfg(test)]
8mod tests {
9 use super::{Parser, Parameter};
10
11 #[test]
12 fn can_create_parser() {
13 let _parser = Parser::new("");
14 }
15
16 #[test]
17 fn can_help() {
18 let mut parser = Parser::new("Test Parser 0");
19 parser.add_parameter(Parameter::param("--test", "A test parameter").alias("-t"));
20 parser.help();
21 }
22
23 #[test]
24 fn can_parse() {
25 let mut parser = Parser::new("Test Parser 0");
26 parser.add_parameter(Parameter::param("--test", "A test parameter").alias("-t"));
27 println!("{:?}", parser.parse_args());
28 }
29}
30
31#[derive(Debug)]
33struct ParamInfo {
34 takes_value: bool,
35 description: String,
36}
37
38impl ParamInfo {
39 pub fn new(takes_value: bool, description: String) -> ParamInfo {
40 return ParamInfo{takes_value: takes_value, description: description};
41 }
42}
43
44#[derive(Debug)]
45pub struct Parser {
46 description: String,
48 parameters: HashMap<String, ParamInfo>,
50 aliases: HashMap<String, String>,
52 default_args: Args,
54}
55
56impl Parser {
57 pub fn new(description: &str) -> Parser {
58 return Parser{description: String::from(description), parameters: HashMap::new(), aliases: HashMap::new(), default_args: Args::new()};
59 }
60
61 pub fn add_parameter(&mut self, param: Parameter) {
62 if let Some(alias) = param.alias {
63 self.aliases.insert(alias, param.name.clone());
64 }
65 if param.takes_value {
68 self.default_args.arguments.insert(param.name.clone(), param.default);
69 }
70 self.parameters.insert(param.name, ParamInfo::new(param.takes_value, param.description));
71 }
72
73 pub fn help(&self) {
74 let mut parameter_aliases = HashMap::new();
76 for (key, value) in &self.aliases {
77 parameter_aliases.insert(value, key);
78 }
79 let mut usage = format!("{}\nUsage: {}", self.description, env::current_exe().unwrap().to_str().unwrap());
81 for (mut param, _) in &self.parameters {
82 let metavar = String::from(param.to_uppercase().trim_start_matches("-"));
83 if let Some(short) = parameter_aliases.get(param) {
84 param = short;
85 }
86 usage += &format!(" [{} {}]", param, metavar);
87 }
88 println!("{}", usage);
89 for (param, info) in &self.parameters {
91 let mut print_arg = String::from("\t");
92 if let Some(short) = parameter_aliases.get(param) {
93 print_arg += &format!("{},", short);
94 }
95 print_arg += &format!("\t{}", param);
96 println!("{}\t\t{}", print_arg, info.description);
97 }
98 }
99
100 pub fn parse_args(&self) -> Args {
101 fn fail(parser: &Parser) -> ! {
102 parser.help();
103 std::process::exit(1);
104 }
105
106 let mut args = self.default_args.clone();
107 let mut args_iter = env::args().into_iter().skip(1);
109 while let Some(arg) = args_iter.next() {
110 let mut full_arg = arg.clone();
112 if let Some(full_name) = self.aliases.get(&arg) {
113 full_arg = full_name.clone();
114 }
115 if let Some(info) = self.parameters.get(&full_arg) {
117 if info.takes_value {
118 match args_iter.next() {
119 Some(value) => args.arguments.insert(full_arg, Some(value)),
121 None => fail(&self),
122 };
123 } else {
124 args.flags.insert(full_arg);
125 }
126 } else if arg == "-h" || arg == "--help" {
127 self.help();
130 std::process::exit(0);
131 } else {
132 args.positional.push(arg);
134 }
135 }
136
137 let mut missing_args = Vec::new();
139 for (arg, value_opt) in &args.arguments {
140 if value_opt.is_none() {
141 missing_args.push(arg);
142 }
143 }
144 if !missing_args.is_empty() {
145 println!("Missing required arguments: {:?}", missing_args);
146 fail(&self);
147 }
148
149 return args;
150 }
151}