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] Arguments: 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, pub n_sentences: u32, pub unique: bool, } impl Args { pub fn parse() -> Result { let arg_vec: Vec = env::args().collect(); let mut i = 1; //skip first arg (execution path) let mut opt_n_sentences: Option = None; let mut opt_unique: Option = None; let mut opt_directory: Option = None; while i < arg_vec.len() { match arg_vec[i].as_str() { "-h" | "--help" => { println!("{}", HELP_STR); process::exit(0); }, "-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::() .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 Err(ArgError::UnknownArg(other.to_string())); }, None => Some(other.to_string()), }; i += 1; }, } } 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); Ok(Args { directory, n_sentences, unique, }) } }