mirror of
https://github.com/haraldh/chainerror.git
synced 2025-01-31 00:56:41 +01:00
add tutorial
This commit is contained in:
parent
d1295092a4
commit
46e2c78aa8
|
@ -1,169 +0,0 @@
|
||||||
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<Error>> {
|
|
||||||
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::<u32>()
|
|
||||||
.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<Error>> {
|
|
||||||
Err(IoError::from(IoErrorKind::NotFound)).map_err(mstrerr!(FileError, "File reader error"))?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_config(filename: &Path) -> Result<(), Box<Error>> {
|
|
||||||
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<Error>> {
|
|
||||||
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<Error>> {
|
|
||||||
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::<AppError>());
|
|
||||||
let app_err = e.downcast_chain_ref::<AppError>().unwrap();
|
|
||||||
|
|
||||||
if app_err.find_kind::<SeriousError>().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::<ConfigFileError>() {
|
|
||||||
if debug {
|
|
||||||
eprintln!("{:?}\n", cfg_error);
|
|
||||||
} else {
|
|
||||||
// Deep Error handling
|
|
||||||
if let Some(chioerror) = cfg_error.find_kind::<IoError>() {
|
|
||||||
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<Error>> {
|
|
||||||
eprintln!("Display:\n");
|
|
||||||
let e = start_app(false).unwrap_err();
|
|
||||||
assert!(e.is_chain::<AppError>());
|
|
||||||
eprintln!("\n\n==================================");
|
|
||||||
eprintln!("==== Debug output");
|
|
||||||
eprintln!("==================================\n");
|
|
||||||
let r = start_app(true);
|
|
||||||
|
|
||||||
eprintln!("\n\n==================================");
|
|
||||||
eprintln!("==== Main return output");
|
|
||||||
eprintln!("==================================\n");
|
|
||||||
r
|
|
||||||
}
|
|
34
examples/tutorial1.rs
Normal file
34
examples/tutorial1.rs
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
/*!
|
||||||
|
|
||||||
|
~~~bash
|
||||||
|
$ cargo run -q --example tutorial1
|
||||||
|
Error: StringError("func1 error")
|
||||||
|
~~~
|
||||||
|
|
||||||
|
!*/
|
||||||
|
|
||||||
|
use std::error::Error;
|
||||||
|
use std::result::Result;
|
||||||
|
|
||||||
|
fn do_some_io() -> Result<(), Box<Error>> {
|
||||||
|
Err("do_some_io error")?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn func2() -> Result<(), Box<Error>> {
|
||||||
|
if let Err(_) = do_some_io() {
|
||||||
|
Err("func2 error")?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn func1() -> Result<(), Box<Error>> {
|
||||||
|
if let Err(_) = func2() {
|
||||||
|
Err("func1 error")?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<Error>> {
|
||||||
|
func1()
|
||||||
|
}
|
74
examples/tutorial10.rs
Normal file
74
examples/tutorial10.rs
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
/*!
|
||||||
|
|
||||||
|
~~~bash
|
||||||
|
$ cargo run -q --example tutorial10
|
||||||
|
Main Error Report: func1 error calling func2
|
||||||
|
Error reported by Func2Error: Error reading 'foo.txt'
|
||||||
|
|
||||||
|
Debug Error:
|
||||||
|
examples/tutorial10.rs:49: Func2
|
||||||
|
Caused by:
|
||||||
|
examples/tutorial10.rs:29: Func2Error(Error reading 'foo.txt')
|
||||||
|
Caused by:
|
||||||
|
Kind(NotFound)
|
||||||
|
~~~
|
||||||
|
|
||||||
|
!*/
|
||||||
|
|
||||||
|
use chainerror::prelude::*;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::io;
|
||||||
|
use std::result::Result;
|
||||||
|
|
||||||
|
fn do_some_io() -> Result<(), Box<Error>> {
|
||||||
|
Err(io::Error::from(io::ErrorKind::NotFound))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
derive_str_cherr!(Func2Error);
|
||||||
|
|
||||||
|
fn func2() -> Result<(), Box<Error>> {
|
||||||
|
let filename = "foo.txt";
|
||||||
|
do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum Func1Error {
|
||||||
|
Func2,
|
||||||
|
IO(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ::std::fmt::Display for Func1Error {
|
||||||
|
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Func1Error::Func2 => write!(f, "func1 error calling func2"),
|
||||||
|
Func1Error::IO(filename) => write!(f, "Error reading '{}'", filename),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn func1() -> ChainResult<(), Func1Error> {
|
||||||
|
func2().map_err(|e| cherr!(e, Func1Error::Func2))?;
|
||||||
|
let filename = String::from("bar.txt");
|
||||||
|
do_some_io().map_err(|e| cherr!(e, Func1Error::IO(filename)))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<Error>> {
|
||||||
|
if let Err(e) = func1() {
|
||||||
|
match e.kind() {
|
||||||
|
Func1Error::Func2 => eprintln!("Main Error Report: func1 error calling func2"),
|
||||||
|
Func1Error::IO(filename) => {
|
||||||
|
eprintln!("Main Error Report: func1 error reading '{}'", filename)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(e) = e.find_chain_cause::<Func2Error>() {
|
||||||
|
eprintln!("Error reported by Func2Error: {}", e)
|
||||||
|
}
|
||||||
|
|
||||||
|
eprintln!("\nDebug Error:\n{:?}", e);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
79
examples/tutorial11.rs
Normal file
79
examples/tutorial11.rs
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
/*!
|
||||||
|
|
||||||
|
~~~bash
|
||||||
|
$ cargo run -q --example tutorial11
|
||||||
|
Main Error Report: func1 error calling func2
|
||||||
|
Error reported by Func2Error: Error reading 'foo.txt'
|
||||||
|
|
||||||
|
Debug Error:
|
||||||
|
examples/tutorial11.rs:57: func1 error calling func2
|
||||||
|
Caused by:
|
||||||
|
examples/tutorial11.rs:32: Func2Error(Error reading 'foo.txt')
|
||||||
|
Caused by:
|
||||||
|
Kind(NotFound)
|
||||||
|
~~~
|
||||||
|
|
||||||
|
!*/
|
||||||
|
|
||||||
|
use chainerror::prelude::*;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::io;
|
||||||
|
use std::result::Result;
|
||||||
|
|
||||||
|
fn do_some_io() -> Result<(), Box<Error>> {
|
||||||
|
Err(io::Error::from(io::ErrorKind::NotFound))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
derive_str_cherr!(Func2Error);
|
||||||
|
|
||||||
|
fn func2() -> Result<(), Box<Error>> {
|
||||||
|
let filename = "foo.txt";
|
||||||
|
do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Func1Error {
|
||||||
|
Func2,
|
||||||
|
IO(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ::std::fmt::Display for Func1Error {
|
||||||
|
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Func1Error::Func2 => write!(f, "func1 error calling func2"),
|
||||||
|
Func1Error::IO(filename) => write!(f, "Error reading '{}'", filename),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ::std::fmt::Debug for Func1Error {
|
||||||
|
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||||
|
write!(f, "{}", self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn func1() -> ChainResult<(), Func1Error> {
|
||||||
|
func2().map_err(|e| cherr!(e, Func1Error::Func2))?;
|
||||||
|
let filename = String::from("bar.txt");
|
||||||
|
do_some_io().map_err(|e| cherr!(e, Func1Error::IO(filename)))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<Error>> {
|
||||||
|
if let Err(e) = func1() {
|
||||||
|
match e.kind() {
|
||||||
|
Func1Error::Func2 => eprintln!("Main Error Report: func1 error calling func2"),
|
||||||
|
Func1Error::IO(filename) => {
|
||||||
|
eprintln!("Main Error Report: func1 error reading '{}'", filename)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(e) = e.find_chain_cause::<Func2Error>() {
|
||||||
|
eprintln!("Error reported by Func2Error: {}", e)
|
||||||
|
}
|
||||||
|
|
||||||
|
eprintln!("\nDebug Error:\n{:?}", e);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
39
examples/tutorial2.rs
Normal file
39
examples/tutorial2.rs
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
/*!
|
||||||
|
|
||||||
|
~~~bash
|
||||||
|
$ cargo run -q --example tutorial2
|
||||||
|
Error: examples/tutorial2.rs:28: "func1 error"
|
||||||
|
Caused by:
|
||||||
|
examples/tutorial2.rs:21: "func2 error"
|
||||||
|
Caused by:
|
||||||
|
StringError("do_some_io error")
|
||||||
|
~~~
|
||||||
|
|
||||||
|
!*/
|
||||||
|
|
||||||
|
use chainerror::prelude::*;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::result::Result;
|
||||||
|
|
||||||
|
fn do_some_io() -> Result<(), Box<Error>> {
|
||||||
|
Err("do_some_io error")?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn func2() -> Result<(), Box<Error>> {
|
||||||
|
if let Err(e) = do_some_io() {
|
||||||
|
Err(cherr!(e, "func2 error"))?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn func1() -> Result<(), Box<Error>> {
|
||||||
|
if let Err(e) = func2() {
|
||||||
|
Err(cherr!(e, "func1 error"))?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<Error>> {
|
||||||
|
func1()
|
||||||
|
}
|
38
examples/tutorial3.rs
Normal file
38
examples/tutorial3.rs
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
/*!
|
||||||
|
|
||||||
|
~~~bash
|
||||||
|
$ cargo run -q --example tutorial3
|
||||||
|
examples/tutorial3.rs:30: "func1 error"
|
||||||
|
Caused by:
|
||||||
|
examples/tutorial3.rs:25: "func2 error"
|
||||||
|
Caused by:
|
||||||
|
StringError("do_some_io error")
|
||||||
|
~~~
|
||||||
|
|
||||||
|
!*/
|
||||||
|
|
||||||
|
use chainerror::prelude::*;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::result::Result;
|
||||||
|
|
||||||
|
fn do_some_io() -> Result<(), Box<Error>> {
|
||||||
|
Err("do_some_io error")?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn func2() -> Result<(), Box<Error>> {
|
||||||
|
do_some_io().map_err(|e| cherr!(e, "func2 error"))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn func1() -> Result<(), Box<Error>> {
|
||||||
|
func2().map_err(|e| cherr!(e, "func1 error"))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<Error>> {
|
||||||
|
if let Err(e) = func1() {
|
||||||
|
eprintln!("{:?}", e);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
38
examples/tutorial4.rs
Normal file
38
examples/tutorial4.rs
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
/*!
|
||||||
|
|
||||||
|
~~~bash
|
||||||
|
$ cargo run -q --example tutorial4
|
||||||
|
examples/tutorial4.rs:29: "func1 error"
|
||||||
|
Caused by:
|
||||||
|
examples/tutorial4.rs:24: "func2 error"
|
||||||
|
Caused by:
|
||||||
|
StringError("do_some_io error")
|
||||||
|
~~~
|
||||||
|
|
||||||
|
!*/
|
||||||
|
|
||||||
|
use chainerror::prelude::*;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::result::Result;
|
||||||
|
|
||||||
|
fn do_some_io() -> Result<(), Box<Error>> {
|
||||||
|
Err("do_some_io error")?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn func2() -> Result<(), Box<Error>> {
|
||||||
|
do_some_io().map_err(mstrerr!("func2 error"))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn func1() -> Result<(), Box<Error>> {
|
||||||
|
func2().map_err(mstrerr!("func1 error"))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<Error>> {
|
||||||
|
if let Err(e) = func1() {
|
||||||
|
eprintln!("{:?}", e);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
42
examples/tutorial5.rs
Normal file
42
examples/tutorial5.rs
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
/*!
|
||||||
|
|
||||||
|
~~~bash
|
||||||
|
$ cargo run -q --example tutorial5
|
||||||
|
Func2 failed because of 'entity not found'
|
||||||
|
func1 error
|
||||||
|
~~~
|
||||||
|
|
||||||
|
!*/
|
||||||
|
|
||||||
|
use chainerror::prelude::*;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::io;
|
||||||
|
use std::result::Result;
|
||||||
|
|
||||||
|
fn do_some_io() -> Result<(), Box<Error>> {
|
||||||
|
Err(io::Error::from(io::ErrorKind::NotFound))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn func2() -> Result<(), Box<Error>> {
|
||||||
|
let filename = "foo.txt";
|
||||||
|
do_some_io().map_err(mstrerr!("Error reading '{}'", filename))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn func1() -> Result<(), Box<Error>> {
|
||||||
|
if let Err(e) = func2() {
|
||||||
|
if let Some(s) = e.source() {
|
||||||
|
eprintln!("func2 failed because of '{}'", s);
|
||||||
|
Err(e).map_err(mstrerr!("func1 error"))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<Error>> {
|
||||||
|
if let Err(e) = func1() {
|
||||||
|
eprintln!("{}", e);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
52
examples/tutorial6.rs
Normal file
52
examples/tutorial6.rs
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
/*!
|
||||||
|
|
||||||
|
~~~bash
|
||||||
|
$ cargo run -q --example tutorial6
|
||||||
|
Error: func1 error
|
||||||
|
caused by: Error reading 'foo.txt'
|
||||||
|
caused by: std::io::Error: entity not found
|
||||||
|
of kind: std::io::ErrorKind::NotFound
|
||||||
|
~~~
|
||||||
|
|
||||||
|
!*/
|
||||||
|
|
||||||
|
use chainerror::prelude::*;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::io;
|
||||||
|
use std::result::Result;
|
||||||
|
|
||||||
|
fn do_some_io() -> Result<(), Box<Error>> {
|
||||||
|
Err(io::Error::from(io::ErrorKind::NotFound))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn func2() -> Result<(), Box<Error>> {
|
||||||
|
let filename = "foo.txt";
|
||||||
|
do_some_io().map_err(mstrerr!("Error reading '{}'", filename))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn func1() -> Result<(), Box<Error>> {
|
||||||
|
func2().map_err(mstrerr!("func1 error"))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<Error>> {
|
||||||
|
if let Err(e) = func1() {
|
||||||
|
eprintln!("Error: {}", e);
|
||||||
|
let mut s = e.as_ref();
|
||||||
|
while let Some(c) = s.source() {
|
||||||
|
if let Some(ioerror) = c.downcast_ref::<io::Error>() {
|
||||||
|
eprintln!("caused by: std::io::Error: {}", ioerror);
|
||||||
|
match ioerror.kind() {
|
||||||
|
io::ErrorKind::NotFound => eprintln!("of kind: std::io::ErrorKind::NotFound"),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
eprintln!("caused by: {}", c);
|
||||||
|
}
|
||||||
|
s = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
55
examples/tutorial7.rs
Normal file
55
examples/tutorial7.rs
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
/*!
|
||||||
|
|
||||||
|
~~~bash
|
||||||
|
$ cargo run -q --example tutorial7
|
||||||
|
Error: func1 error
|
||||||
|
caused by: std::io::Error: entity not found
|
||||||
|
of kind: std::io::ErrorKind::NotFound
|
||||||
|
The root cause was: std::io::Error: Kind(
|
||||||
|
NotFound
|
||||||
|
)
|
||||||
|
~~~
|
||||||
|
|
||||||
|
!*/
|
||||||
|
|
||||||
|
use chainerror::prelude::*;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::io;
|
||||||
|
use std::result::Result;
|
||||||
|
|
||||||
|
fn do_some_io() -> Result<(), Box<Error>> {
|
||||||
|
Err(io::Error::from(io::ErrorKind::NotFound))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn func2() -> Result<(), Box<Error>> {
|
||||||
|
let filename = "foo.txt";
|
||||||
|
do_some_io().map_err(mstrerr!("Error reading '{}'", filename))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn func1() -> Result<(), Box<Error>> {
|
||||||
|
func2().map_err(mstrerr!("func1 error"))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<Error>> {
|
||||||
|
if let Err(e) = func1() {
|
||||||
|
eprintln!("Error: {}", e);
|
||||||
|
if let Some(s) = e.downcast_chain_ref::<String>() {
|
||||||
|
if let Some(ioerror) = s.find_cause::<io::Error>() {
|
||||||
|
eprintln!("caused by: std::io::Error: {}", ioerror);
|
||||||
|
match ioerror.kind() {
|
||||||
|
io::ErrorKind::NotFound => eprintln!("of kind: std::io::ErrorKind::NotFound"),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(e) = s.root_cause() {
|
||||||
|
let ioerror = e.downcast_ref::<io::Error>().unwrap();
|
||||||
|
eprintln!("The root cause was: std::io::Error: {:#?}", ioerror);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
55
examples/tutorial8.rs
Normal file
55
examples/tutorial8.rs
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
/*!
|
||||||
|
|
||||||
|
~~~bash
|
||||||
|
$ cargo run -q --example tutorial8
|
||||||
|
Func1Error: func1 error
|
||||||
|
Func2Error: Error reading 'foo.txt'
|
||||||
|
Debug Func2Error:
|
||||||
|
examples/tutorial8.rs:27: Func2Error(Error reading 'foo.txt')
|
||||||
|
Caused by:
|
||||||
|
Kind(NotFound)
|
||||||
|
~~~
|
||||||
|
|
||||||
|
!*/
|
||||||
|
|
||||||
|
use chainerror::prelude::*;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::io;
|
||||||
|
use std::result::Result;
|
||||||
|
|
||||||
|
fn do_some_io() -> Result<(), Box<Error>> {
|
||||||
|
Err(io::Error::from(io::ErrorKind::NotFound))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
derive_str_cherr!(Func2Error);
|
||||||
|
|
||||||
|
fn func2() -> Result<(), Box<Error>> {
|
||||||
|
let filename = "foo.txt";
|
||||||
|
do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
derive_str_cherr!(Func1Error);
|
||||||
|
|
||||||
|
fn func1() -> Result<(), Box<Error>> {
|
||||||
|
func2().map_err(mstrerr!(Func1Error, "func1 error"))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<Error>> {
|
||||||
|
if let Err(e) = func1() {
|
||||||
|
if let Some(f1err) = e.downcast_chain_ref::<Func1Error>() {
|
||||||
|
eprintln!("Func1Error: {}", f1err);
|
||||||
|
|
||||||
|
if let Some(f2err) = f1err.find_cause::<ChainError<Func2Error>>() {
|
||||||
|
eprintln!("Func2Error: {}", f2err);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(f2err) = f1err.find_chain_cause::<Func2Error>() {
|
||||||
|
eprintln!("Debug Func2Error:\n{:?}", f2err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
54
examples/tutorial9.rs
Normal file
54
examples/tutorial9.rs
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
/*!
|
||||||
|
|
||||||
|
~~~bash
|
||||||
|
$ cargo run -q --example tutorial9
|
||||||
|
Func1ErrorFunc2:
|
||||||
|
examples/tutorial9.rs:37: Func1ErrorFunc2(func1 error calling func2)
|
||||||
|
Caused by:
|
||||||
|
examples/tutorial9.rs:29: Func2Error(Error reading 'foo.txt')
|
||||||
|
Caused by:
|
||||||
|
Kind(NotFound)
|
||||||
|
~~~
|
||||||
|
|
||||||
|
!*/
|
||||||
|
|
||||||
|
use chainerror::prelude::*;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::io;
|
||||||
|
use std::result::Result;
|
||||||
|
|
||||||
|
fn do_some_io() -> Result<(), Box<Error>> {
|
||||||
|
Err(io::Error::from(io::ErrorKind::NotFound))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
derive_str_cherr!(Func2Error);
|
||||||
|
|
||||||
|
fn func2() -> Result<(), Box<Error>> {
|
||||||
|
let filename = "foo.txt";
|
||||||
|
do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
derive_str_cherr!(Func1ErrorFunc2);
|
||||||
|
derive_str_cherr!(Func1ErrorIO);
|
||||||
|
|
||||||
|
fn func1() -> Result<(), Box<Error>> {
|
||||||
|
func2().map_err(mstrerr!(Func1ErrorFunc2, "func1 error calling func2"))?;
|
||||||
|
let filename = "bar.txt";
|
||||||
|
do_some_io().map_err(mstrerr!(Func1ErrorIO, "Error reading '{}'", filename))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<Error>> {
|
||||||
|
if let Err(e) = func1() {
|
||||||
|
if let Some(s) = e.downcast_ref::<ChainError<Func1ErrorIO>>() {
|
||||||
|
eprintln!("Func1ErrorIO:\n{:?}", s);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(s) = try_cherr_ref!(e, Func1ErrorFunc2) {
|
||||||
|
eprintln!("Func1ErrorFunc2:\n{:?}", s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
163
src/lib.rs
163
src/lib.rs
|
@ -1,5 +1,6 @@
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fmt::{Debug, Display, Formatter, Result};
|
use std::fmt::{Debug, Display, Formatter, Result};
|
||||||
|
use std::result::Result as StdResult;
|
||||||
|
|
||||||
pub struct ChainError<T> {
|
pub struct ChainError<T> {
|
||||||
#[cfg(feature = "fileline")]
|
#[cfg(feature = "fileline")]
|
||||||
|
@ -8,6 +9,8 @@ pub struct ChainError<T> {
|
||||||
error_cause: Option<Box<dyn Error + 'static>>,
|
error_cause: Option<Box<dyn Error + 'static>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub type ChainResult<O, E> = StdResult<O, ChainError<E>>;
|
||||||
|
|
||||||
impl<T: 'static + Display + Debug> ChainError<T> {
|
impl<T: 'static + Display + Debug> ChainError<T> {
|
||||||
#[cfg(feature = "fileline")]
|
#[cfg(feature = "fileline")]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
|
@ -39,11 +42,11 @@ impl<T: 'static + Display + Debug> ChainError<T> {
|
||||||
Some(cause)
|
Some(cause)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_cause<U: Error + 'static>(&self) -> Option<&(dyn Error + 'static)> {
|
pub fn find_cause<U: Error + 'static>(&self) -> Option<&U> {
|
||||||
let mut cause = self as &(dyn Error + 'static);
|
let mut cause = self as &(dyn Error + 'static);
|
||||||
loop {
|
loop {
|
||||||
if cause.is::<U>() {
|
if cause.is::<U>() {
|
||||||
return Some(cause);
|
return cause.downcast_ref::<U>();
|
||||||
}
|
}
|
||||||
|
|
||||||
match cause.source() {
|
match cause.source() {
|
||||||
|
@ -53,7 +56,7 @@ impl<T: 'static + Display + Debug> ChainError<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_kind<U: 'static + Display + Debug>(&self) -> Option<&ChainError<U>> {
|
pub fn find_chain_cause<U: Error + 'static>(&self) -> Option<&ChainError<U>> {
|
||||||
let mut cause = self as &(dyn Error + 'static);
|
let mut cause = self as &(dyn Error + 'static);
|
||||||
loop {
|
loop {
|
||||||
if cause.is::<ChainError<U>>() {
|
if cause.is::<ChainError<U>>() {
|
||||||
|
@ -229,6 +232,9 @@ macro_rules! mstrerr {
|
||||||
( $t:path, $v:expr $(, $more:expr)* ) => {
|
( $t:path, $v:expr $(, $more:expr)* ) => {
|
||||||
|e| cherr!(e, $t (format!($v, $( $more , )* )))
|
|e| cherr!(e, $t (format!($v, $( $more , )* )))
|
||||||
};
|
};
|
||||||
|
( $v:expr $(, $more:expr)* ) => {
|
||||||
|
|e| cherr!(e, format!($v, $( $more , )* ))
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
|
@ -245,138 +251,37 @@ macro_rules! derive_str_cherr {
|
||||||
write!(f, "{}({})", stringify!($e), self.0)
|
write!(f, "{}({})", stringify!($e), self.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl ::std::error::Error for $e {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! try_cherr_ref {
|
||||||
|
( $e:expr, $t:ident ) => {
|
||||||
|
$e.downcast_ref::<ChainError<$t>>()
|
||||||
|
};
|
||||||
|
( $e:expr, $t:path ) => {
|
||||||
|
$e.downcast_ref::<ChainError<$t>>()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! try_cherr_mut {
|
||||||
|
( $e:expr, $t:ident ) => {
|
||||||
|
$e.downcast_mut::<ChainError<$t>>()
|
||||||
|
};
|
||||||
|
( $e:expr, $t:path ) => {
|
||||||
|
$e.downcast_mut::<ChainError<$t>>()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
pub use super::{cherr, derive_str_cherr, mstrerr, ChainError, ChainErrorDown};
|
pub use super::{
|
||||||
|
cherr, derive_str_cherr, mstrerr, try_cherr_mut, try_cherr_ref, ChainError, ChainErrorDown,
|
||||||
|
ChainResult,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::error::Error;
|
|
||||||
use std::io::Error as IoError;
|
|
||||||
use std::io::ErrorKind as IoErrorKind;
|
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
use crate::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<Error>> {
|
|
||||||
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::<u32>()
|
|
||||||
.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<Error>> {
|
|
||||||
Err(IoError::from(IoErrorKind::NotFound))
|
|
||||||
.map_err(mstrerr!(FileError, "File reader error"))?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_config(filename: &Path) -> Result<(), Box<Error>> {
|
|
||||||
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_config_pre(p: &str) -> Result<(), Box<Error>> {
|
|
||||||
read_config(Path::new(p)).map_err(mstrerr!(AppError, "{}", p))?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_chain_error() {
|
|
||||||
for p in &[
|
|
||||||
"global.ini",
|
|
||||||
"local.ini",
|
|
||||||
"user.ini",
|
|
||||||
"user2.ini",
|
|
||||||
"user3.ini",
|
|
||||||
"custom.ini",
|
|
||||||
"essential.ini",
|
|
||||||
] {
|
|
||||||
if let Err(e) = read_config_pre(p) {
|
|
||||||
let app_err = e.downcast_chain_ref::<AppError>().unwrap();
|
|
||||||
|
|
||||||
match p {
|
|
||||||
&"global.ini" => {
|
|
||||||
assert!(app_err.find_kind::<ConfigFileError>().is_some());
|
|
||||||
assert!(app_err.root_cause().unwrap().is::<IoError>());
|
|
||||||
},
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue