From d1295092a4dd193bd33ac404bbe13e3d51d3afe1 Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Wed, 19 Dec 2018 16:57:35 +0100 Subject: [PATCH] factor out example --- Cargo.toml | 2 +- examples/configapp.rs | 169 ++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 72 ++++-------------- 3 files changed, 184 insertions(+), 59 deletions(-) create mode 100644 examples/configapp.rs diff --git a/Cargo.toml b/Cargo.toml index 5dda0f1..1951723 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Harald Hoyer "] edition = "2018" [features] -default = [ "debug-cause", "fileline" ] +default = [ "debug-cause", "fileline"] fileline = [] display-cause = [] debug-cause = [] \ No newline at end of file diff --git a/examples/configapp.rs b/examples/configapp.rs new file mode 100644 index 0000000..a7b147a --- /dev/null +++ b/examples/configapp.rs @@ -0,0 +1,169 @@ +use std::error::Error; +use std::io::Error as IoError; +use std::io::ErrorKind as IoErrorKind; +use std::path::Path; + +use chainerror::prelude::*; + +#[derive(Clone, PartialEq, Debug)] +enum ParseError { + InvalidValue(u32), + InvalidParameter(String), + NoOpen, + NoClose, +} + +impl ::std::fmt::Display for ParseError { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + match self { + ParseError::InvalidValue(a) => write!(f, "InvalidValue: {}", a), + ParseError::InvalidParameter(a) => write!(f, "InvalidParameter: '{}'", a), + ParseError::NoOpen => write!(f, "No opening '{{' in config file"), + ParseError::NoClose => write!(f, "No closing '}}' in config file"), + } + } +} + +fn parse_config(c: String) -> Result<(), Box> { + if !c.starts_with('{') { + Err(cherr!(ParseError::NoOpen))?; + } + if !c.ends_with('}') { + Err(cherr!(ParseError::NoClose))?; + } + let c = &c[1..(c.len() - 1)]; + let v = c + .parse::() + .map_err(|e| cherr!(e, ParseError::InvalidParameter(c.into())))?; + if v > 100 { + Err(cherr!(ParseError::InvalidValue(v)))?; + } + Ok(()) +} + +derive_str_cherr!(ConfigFileError); +derive_str_cherr!(SeriousError); +derive_str_cherr!(FileError); +derive_str_cherr!(AppError); + +fn file_reader(_filename: &Path) -> Result<(), Box> { + Err(IoError::from(IoErrorKind::NotFound)).map_err(mstrerr!(FileError, "File reader error"))?; + Ok(()) +} + +fn read_config(filename: &Path) -> Result<(), Box> { + if filename.eq(Path::new("global.ini")) { + // assume we got an IO error + file_reader(filename).map_err(mstrerr!( + ConfigFileError, + "Error reading file {:?}", + filename + ))?; + } + // assume we read some buffer + if filename.eq(Path::new("local.ini")) { + let buf = String::from("{1000}"); + parse_config(buf)?; + } + + if filename.eq(Path::new("user.ini")) { + let buf = String::from("foo"); + parse_config(buf)?; + } + + if filename.eq(Path::new("user2.ini")) { + let buf = String::from("{foo"); + parse_config(buf)?; + } + + if filename.eq(Path::new("user3.ini")) { + let buf = String::from("{foo}"); + parse_config(buf)?; + } + + if filename.eq(Path::new("custom.ini")) { + let buf = String::from("{10}"); + parse_config(buf)?; + } + + if filename.eq(Path::new("essential.ini")) { + Err(cherr!(SeriousError("Something is really wrong".into())))?; + } + + Ok(()) +} + +fn read_verbose_config(p: &str) -> Result<(), Box> { + eprintln!("Reading '{}' ... ", p); + read_config(Path::new(p)).map_err(mstrerr!(AppError, "{}", p))?; + eprintln!("Ok reading {}", p); + Ok(()) +} + +fn start_app(debug: bool) -> Result<(), Box> { + for p in &[ + "global.ini", + "local.ini", + "user.ini", + "user2.ini", + "user3.ini", + "custom.ini", + "essential.ini", + ] { + if let Err(e) = read_verbose_config(p) { + assert!(e.is_chain::()); + let app_err = e.downcast_chain_ref::().unwrap(); + + if app_err.find_kind::().is_some() { + // Bail out on SeriousError + eprintln!("---> Serious Error:\n{:?}", e); + Err(cherr!(e, AppError("Seriously".into())))?; + } else if let Some(cfg_error) = app_err.find_kind::() { + if debug { + eprintln!("{:?}\n", cfg_error); + } else { + // Deep Error handling + if let Some(chioerror) = cfg_error.find_kind::() { + let ioerror = chioerror.kind(); + match ioerror.kind() { + IoErrorKind::NotFound => { + eprint!("Ignoring missing file"); + if let Some(root_cause) = cfg_error.root_cause() { + eprint!(", because of: {}\n", root_cause); + } + eprintln!(); + } + _ => Err(cherr!(e, AppError("Unhandled IOError".into())))?, + } + } else { + eprintln!("ConfigFileError for: {}", e); + } + } + } else { + if debug { + eprintln!("Error reading:\n{:?}\n", e) + } else { + eprintln!("Error reading: {}\n", e) + } + } + } + eprintln!(); + } + Ok(()) +} + + +fn main() -> Result<(), Box> { + eprintln!("Display:\n"); + let e = start_app(false).unwrap_err(); + assert!(e.is_chain::()); + eprintln!("\n\n=================================="); + eprintln!("==== Debug output"); + eprintln!("==================================\n"); + let r = start_app(true); + + eprintln!("\n\n=================================="); + eprintln!("==== Main return output"); + eprintln!("==================================\n"); + r +} diff --git a/src/lib.rs b/src/lib.rs index e404343..e621fcb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -302,12 +302,9 @@ mod tests { derive_str_cherr!(FileError); derive_str_cherr!(AppError); - fn file_reader(filename: &Path) -> Result<(), Box> { - Err(IoError::from(IoErrorKind::NotFound)).map_err(mstrerr!( - FileError, - "Can't find {:?}", - filename - ))?; + fn file_reader(_filename: &Path) -> Result<(), Box> { + Err(IoError::from(IoErrorKind::NotFound)) + .map_err(mstrerr!(FileError, "File reader error"))?; Ok(()) } @@ -316,7 +313,7 @@ mod tests { // assume we got an IO error file_reader(filename).map_err(mstrerr!( ConfigFileError, - "Can't find {:?}", + "Error reading file {:?}", filename ))?; } @@ -353,14 +350,13 @@ mod tests { Ok(()) } - fn read_verbose_config(p: &str) -> Result<(), Box> { - eprintln!("Reading '{}' ... ", p); + fn read_config_pre(p: &str) -> Result<(), Box> { read_config(Path::new(p)).map_err(mstrerr!(AppError, "{}", p))?; - eprintln!("Ok reading {}", p); Ok(()) } - fn start_app(debug: bool) -> Result<(), Box> { + #[test] + fn test_chain_error() { for p in &[ "global.ini", "local.ini", @@ -370,57 +366,17 @@ mod tests { "custom.ini", "essential.ini", ] { - if let Err(e) = read_verbose_config(p) { - assert!(e.is_chain::()); + if let Err(e) = read_config_pre(p) { let app_err = e.downcast_chain_ref::().unwrap(); - if app_err.find_kind::().is_some() { - // Bail out on SeriousError - eprintln!("---> Serious Error:\n{:?}", e); - Err(cherr!(e, AppError("Seriously".into())))?; - } else if let Some(cfg_error) = app_err.find_kind::() { - if debug { - eprintln!("{:?}\n", cfg_error); - } else { - // Deep Error handling - if let Some(chioerror) = cfg_error.find_kind::() { - let ioerror = chioerror.kind(); - match ioerror.kind() { - IoErrorKind::NotFound => { - eprint!("Ignoring missing file"); - if let Some(root_cause) = cfg_error.root_cause() { - eprint!(", because of: {}\n", root_cause); - } - eprintln!(); - } - _ => Err(cherr!(e, AppError("Unhandled IOError".into())))?, - } - } else { - eprintln!("ConfigFileError for: {}", e); - } - } - } else { - if debug { - eprintln!("Error reading:\n{:?}\n", e) - } else { - eprintln!("Error reading: {}\n", e) - } + match p { + &"global.ini" => { + assert!(app_err.find_kind::().is_some()); + assert!(app_err.root_cause().unwrap().is::()); + }, + _ => {} } } - eprintln!(); } - Ok(()) - } - - #[test] - fn test_chain_error() { - eprintln!("Display:\n"); - let e = start_app(false).unwrap_err(); - assert!(e.is_chain::()); - eprintln!("\n\n=================================="); - eprintln!("==== Debug output"); - eprintln!("==================================\n"); - let r = start_app(true); - assert!(r.unwrap_err().is_chain::()); } }