[snowpatch] [PATCH 3/3] Rewrite settings parsing to avoid panic

Andrew Donnellan andrew.donnellan at au1.ibm.com
Thu Dec 13 14:15:51 AEDT 2018


On 13/12/18 1:43 pm, Michael Ellerman wrote:
> Change parse() to return a Result, using the question mark operator to
> catch errors and return early.
> 
> Use map_err() to turn the low-level errors into something human
> readable. We could define our own error type but that seems like
> overkill for this.
> 
> Rework the tests to cope. For the two failing cases that's a bit ugly
> because we have to map the Ok() to Err() and vice versa.
> 
> There are now no panic()s or unwrap()s in settings.rs!
> 
> Error messages seen by the user look like, eg:
>    Error: Unable to parse config file: missing field `git`
>    Error: Unable to open config file: Permission denied (os error 13)
>    Error: Unable to open config file: No such file or directory (os error 2)

woo!

Reviewed-by: Andrew Donnellan <andrew.donnellan at au1.ibm.com>

> ---
>   src/main.rs     |  2 +-
>   src/settings.rs | 42 +++++++++++++++++++++---------------------
>   2 files changed, 22 insertions(+), 22 deletions(-)
> 
> diff --git a/src/main.rs b/src/main.rs
> index 2e022a3939e9..d2c24be3a59f 100644
> --- a/src/main.rs
> +++ b/src/main.rs
> @@ -303,7 +303,7 @@ fn run() -> Result<(), Box<Error>> {
>           .and_then(|d| d.version(Some(version)).deserialize())
>           .unwrap_or_else(|e| e.exit());
>   
> -    let settings = settings::parse(&args.arg_config_file);
> +    let settings = settings::parse(&args.arg_config_file)?;
>   
>       // The HTTP client we'll use to access the APIs
>       // TODO: Handle https_proxy
> diff --git a/src/settings.rs b/src/settings.rs
> index a5513728fbaa..8e86b213a6bc 100644
> --- a/src/settings.rs
> +++ b/src/settings.rs
> @@ -22,6 +22,7 @@ use git2;
>   use git2::Repository;
>   
>   use std::collections::BTreeMap;
> +use std::error::Error;
>   use std::fmt;
>   use std::fs::File;
>   use std::io::Read;
> @@ -186,23 +187,18 @@ pub struct Config {
>       pub projects: BTreeMap<String, Project>,
>   }
>   
> -pub fn parse(path: &str) -> Config {
> +pub fn parse(path: &str) -> Result<Config, Box<Error>> {
>       let mut toml_config = String::new();
>   
> -    let mut file = File::open(&path).expect("Couldn't open config file, exiting.");
> +    File::open(&path)
> +        .map_err(|e| format!("Unable to open config file: {}", e))?
> +        .read_to_string(&mut toml_config)
> +        .map_err(|e| format!("Unable to read config file: {}", e))?;
>   
> -    file.read_to_string(&mut toml_config)
> -        .unwrap_or_else(|err| panic!("Couldn't read config: {}", err));
> +    let config = toml::de::from_str::<Config>(&toml_config)
> +        .map_err(|e| format!("Unable to parse config file: {}", e))?;
>   
> -    let toml_config = toml::de::from_str::<Config>(&toml_config);
> -
> -    match toml_config {
> -        Ok(config_inside) => config_inside,
> -        Err(err) => {
> -            error!("TOML error: {}", err);
> -            panic!("Could not parse configuration file, exiting");
> -        }
> -    }
> +    Ok(config)
>   }
>   
>   #[cfg(test)]
> @@ -210,19 +206,23 @@ mod test {
>       use settings::*;
>   
>       #[test]
> -    #[should_panic(expected = "Couldn't open config file")]
> -    fn bad_path() {
> -        parse("/nonexistent/config.file");
> +    fn bad_path() -> Result<(), &'static str> {
> +        match parse("/nonexistent/config.file") {
> +            Ok(_) => Err("Didn't fail opening non-existent file?"),
> +            Err(_) => Ok(()),
> +        }
>       }
>   
>       #[test]
> -    fn parse_example_openpower() {
> -        parse("examples/openpower.toml");
> +    fn parse_example_openpower() -> Result<(), Box<Error>> {
> +        parse("examples/openpower.toml").map(|_| ())
>       }
>   
>       #[test]
> -    #[should_panic(expected = "Could not parse configuration file, exiting")]
> -    fn parse_example_invalid() {
> -        parse("examples/tests/invalid.toml");
> +    fn parse_example_invalid() -> Result<(), &'static str> {
> +        match parse("examples/tests/invalid.toml") {
> +            Ok(_) => Err("Didn't fail parsing invalid TOML?"),
> +            Err(_) => Ok(())
> +        }
>       }
>   }
> 

-- 
Andrew Donnellan              OzLabs, ADL Canberra
andrew.donnellan at au1.ibm.com  IBM Australia Limited



More information about the snowpatch mailing list