diff --git a/city-pop/src/main.rs b/city-pop/src/main.rs index 63d0715a6761f8d43c9713c216d6e646cfb7f898..59743668830a61241ff1ca1e3fe284060ae826e3 100644 --- a/city-pop/src/main.rs +++ b/city-pop/src/main.rs @@ -4,10 +4,50 @@ extern crate csv; use getopts::Options; -use std::{env,fs,io}; +use std::{env,fmt,fs,io,process}; use std::error::Error; use std::path::Path; + +#[derive(Debug)] +enum CliError { + Io(io::Error), + Csv(csv::Error), + NotFound, +} + +impl fmt::Display for CliError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + CliError::Io(ref err) => err.fmt(f), + CliError::Csv(ref err) => err.fmt(f), + CliError::NotFound => write!(f,"No matching cities with a population were found."), + } + } +} + +impl Error for CliError { + fn description(&self) -> &str { + match *self { + CliError::Io(ref err) => err.description(), + CliError::Csv(ref err) => err.description(), + CliError::NotFound => "not found", + } + } +} + +impl From<io::Error> for CliError { + fn from(err: io::Error) -> CliError { + CliError::Io(err) + } +} + +impl From<csv::Error> for CliError { + fn from(err: csv::Error) -> CliError { + CliError::Csv(err) + } +} + // This struct represents the data in each row of the CSV file. // Type based decoding absolves us of a lot of the nitty gritty error // handling, like parsing strings as integers or floats. @@ -47,7 +87,7 @@ fn print_usage(program: &str, opts: Options) { } fn search<P: AsRef<Path>> (file_path: &Option<P>, city: &str) - -> Result<Vec<PopulationCount>, Box<Error+Send+Sync>> { + -> Result<Vec<PopulationCount>, CliError> { let mut found = vec![]; let input: Box<io::Read> = match *file_path { None => Box::new(io::stdin()), @@ -68,7 +108,7 @@ fn search<P: AsRef<Path>> (file_path: &Option<P>, city: &str) } } if found.is_empty() { - Err(From::from("No matching cities with a population were found.")) + Err(CliError::NotFound) } else { Ok(found) } @@ -81,7 +121,8 @@ fn main() { let mut opts = Options::new(); opts.optopt("f","file", "Choose an input file, instead of using STDIN.","NAME"); opts.optflag("h","help","Show this usage message."); - + opts.optflag("q","quit","Silences errors and warnings."); + let matches = match opts.parse(&args[1..]) { Ok(m) => { m } Err(e) => { panic!(e.to_string()) } @@ -101,8 +142,12 @@ fn main() { return; }; - for pop in search(&data_file, &city).unwrap() { - println!("{}, {}: {:?}",pop.city, pop.country, pop.count); + match search(&data_file, &city) { + Err(CliError::NotFound) if matches.opt_present("q") => process::exit(1), + Err(err) => panic!("{}", err), + Ok(pops) => for pop in pops { + println!("{}, {}: {:?}",pop.city, pop.country, pop.count); + } } }