aboutsummaryrefslogtreecommitdiff
path: root/src/args.rs
diff options
context:
space:
mode:
authorMatthew Hall <hallmatthew314@gmail.com>2024-03-30 23:09:34 +1300
committerMatthew Hall <hallmatthew314@gmail.com>2024-03-30 23:09:34 +1300
commit437b8b7a17dc09cd7281d1a09d681874fb166da8 (patch)
tree51a86482d6af12f3c0503a7c9cb46eae57ca5061 /src/args.rs
parent4ef3b4a2997e11a7969ffb02e3cda6f3b9a3b651 (diff)
More robust argument parsing
Diffstat (limited to 'src/args.rs')
-rw-r--r--src/args.rs96
1 files changed, 79 insertions, 17 deletions
diff --git a/src/args.rs b/src/args.rs
index 6753664..5e99dc5 100644
--- a/src/args.rs
+++ b/src/args.rs
@@ -1,4 +1,44 @@
use std::env;
+use std::fmt;
+use std::process;
+
+const UNIQUE_DEFAULT: bool = false;
+const N_SENTENCES_DEFAULT: u32 = 1;
+pub const HELP_STR: &str = "Usage: spout [OPTIONS] <DIRECTORY>
+
+Arguments:
+ <DIRECTORY> Path to data directory
+
+Options:
+ -n, --sentences Number of sentences to generate [default: 1]
+ --unique Don't re-use entries in data files (program will fail upon running out)
+ -h, --help Print help";
+
+#[derive(Debug)]
+pub enum ArgError {
+ NoDirectory,
+ UnknownArg(String),
+ OutOfArgs(String),
+ BadNumber(String),
+ FlagReuse(String),
+}
+
+impl fmt::Display for ArgError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ ArgError::NoDirectory =>
+ write!(f, "no directory path provided"),
+ ArgError::OutOfArgs(last) =>
+ write!(f, "expected one or more arguments after '{}'", last),
+ ArgError::UnknownArg(s) =>
+ write!(f, "unknown argument: '{}'", s),
+ ArgError::BadNumber(s) =>
+ write!(f, "'{}' is not a valid number", s),
+ ArgError::FlagReuse(s) =>
+ write!(f, "multiple uses of the '{}' flag", s),
+ }
+ }
+}
pub struct Args {
pub directory: String,
@@ -7,41 +47,63 @@ pub struct Args {
}
impl Args {
- pub fn parse() -> Option<Args> {
+ pub fn parse() -> Result<Args, ArgError> {
let arg_vec: Vec<String> = env::args().collect();
let mut i = 1; //skip first arg (execution path)
- let mut n_sentences = 1;
- let mut unique = false;
+ let mut opt_n_sentences: Option<u32> = None;
+ let mut opt_unique: Option<bool> = None;
let mut opt_directory: Option<String> = None;
while i < arg_vec.len() {
match arg_vec[i].as_str() {
- "-n" | "--sentences" => {
- if i + 1 < arg_vec.len() {
- n_sentences = arg_vec[i+1].parse::<u32>().ok()?;
- } else {
- return None;
- }
- i += 2;
+ "-h" | "--help" => {
+ println!("{}", HELP_STR);
+ process::exit(0);
},
- "--unique" => {
- unique = true;
- i += 1;
+ "-n" | "--sentences" => match opt_n_sentences {
+ Some(_) => {
+ return Err(ArgError::FlagReuse(arg_vec[i].to_string()));
+ },
+ None => {
+ if i + 1 < arg_vec.len() {
+ // manually converting the error because it
+ // gives more useful information than ParseIntError
+ let n = arg_vec[i+1].parse::<u32>()
+ .map_err(|_| ArgError::BadNumber(arg_vec[i+1].to_string()))?;
+ opt_n_sentences = Some(n);
+ } else {
+ return Err(ArgError::OutOfArgs(arg_vec[i].to_string()));
+ }
+ i += 2;
+ },
+ },
+ "--unique" => match opt_unique {
+ Some(_) => {
+ return Err(ArgError::FlagReuse(arg_vec[i].to_string()));
+ },
+ None => {
+ opt_unique = Some(true);
+ i += 1;
+ },
},
other => {
opt_directory = match opt_directory {
- Some(_) => { return None; },
- None => Some(other.to_string()),
+ Some(_) => {
+ return Err(ArgError::UnknownArg(other.to_string()));
+ },
+ None => Some(other.to_string()),
};
i += 1;
},
}
}
- let directory = opt_directory?;
+ let directory = opt_directory.ok_or(ArgError::NoDirectory)?;
+ let n_sentences = opt_n_sentences.unwrap_or(N_SENTENCES_DEFAULT);
+ let unique = opt_unique.unwrap_or(UNIQUE_DEFAULT);
- Some(Args {
+ Ok(Args {
directory,
n_sentences,
unique,