mirror of
				https://github.com/haraldh/chainerror.git
				synced 2025-10-31 14:41:37 +01:00 
			
		
		
		
	
						commit
						25ebe8333f
					
				
					 31 changed files with 230 additions and 153 deletions
				
			
		
							
								
								
									
										77
									
								
								README.md
									
										
									
									
									
								
							
							
						
						
									
										77
									
								
								README.md
									
										
									
									
									
								
							|  | @ -46,7 +46,7 @@ and you have no idea where it comes from. | |||
| With `chainerror`, you can supply a context and get a nice error backtrace: | ||||
| 
 | ||||
| ```rust | ||||
| use chainerror::prelude::v1::*; | ||||
| use chainerror::Context as _; | ||||
| use std::path::PathBuf; | ||||
| 
 | ||||
| type BoxedError = Box<dyn std::error::Error + Send + Sync>; | ||||
|  | @ -90,6 +90,81 @@ Along with the `Error<T>` struct, `chainerror` comes with some useful helper mac | |||
| 
 | ||||
| Debug information is worth it! | ||||
| 
 | ||||
| ## Multiple Output Formats | ||||
| 
 | ||||
| `chainerror` supports multiple output formats, which can be selected with the different format specifiers: | ||||
| 
 | ||||
| * `{}`: Display | ||||
| ```text | ||||
| func1 error calling func2 | ||||
| ``` | ||||
| 
 | ||||
| * `{:#}`: Alternative Display | ||||
| ```text | ||||
| func1 error calling func2 | ||||
| Caused by: | ||||
|   func2 error: calling func3 | ||||
| Caused by: | ||||
|   (passed error) | ||||
| Caused by: | ||||
|   Error reading 'foo.txt' | ||||
| Caused by: | ||||
|   entity not found | ||||
| ``` | ||||
| 
 | ||||
| * `{:?}`: Debug | ||||
| ```text | ||||
| examples/example.rs:50:13: func1 error calling func2 | ||||
| Caused by: | ||||
| examples/example.rs:25:13: Func2Error(func2 error: calling func3) | ||||
| Caused by: | ||||
| examples/example.rs:18:13: (passed error) | ||||
| Caused by: | ||||
| examples/example.rs:13:18: Error reading 'foo.txt' | ||||
| Caused by: | ||||
| Kind(NotFound) | ||||
| 
 | ||||
| ``` | ||||
| 
 | ||||
| * `{:#?}`: Alternative Debug | ||||
| ```text | ||||
| Error<example::Func1Error> { | ||||
|     occurrence: Some( | ||||
|         "examples/example.rs:50:13", | ||||
|     ), | ||||
|     kind: func1 error calling func2, | ||||
|     source: Some( | ||||
|         Error<example::Func2Error> { | ||||
|             occurrence: Some( | ||||
|                 "examples/example.rs:25:13", | ||||
|             ), | ||||
|             kind: Func2Error(func2 error: calling func3), | ||||
|             source: Some( | ||||
|                 Error<chainerror::AnnotatedError> { | ||||
|                     occurrence: Some( | ||||
|                         "examples/example.rs:18:13", | ||||
|                     ), | ||||
|                     kind: (passed error), | ||||
|                     source: Some( | ||||
|                         Error<alloc::string::String> { | ||||
|                             occurrence: Some( | ||||
|                                 "examples/example.rs:13:18", | ||||
|                             ), | ||||
|                             kind: "Error reading 'foo.txt'", | ||||
|                             source: Some( | ||||
|                                 Kind( | ||||
|                                     NotFound, | ||||
|                                 ), | ||||
|                             ), | ||||
|                         }, | ||||
|                     ), | ||||
|                 }, | ||||
|             ), | ||||
|         }, | ||||
|     ), | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| ## Tutorial | ||||
| 
 | ||||
| Read the [Tutorial](https://haraldh.github.io/chainerror/tutorial1.html) | ||||
|  |  | |||
							
								
								
									
										24
									
								
								README.tpl
									
										
									
									
									
								
							
							
						
						
									
										24
									
								
								README.tpl
									
										
									
									
									
								
							|  | @ -1,24 +0,0 @@ | |||
| [](https://crates.io/crates/chainerror) | ||||
| [](https://docs.rs/chainerror/) | ||||
| [](https://coveralls.io/github/haraldh/chainerror?branch=master) | ||||
| {{badges}} | ||||
| 
 | ||||
| # {{crate}} | ||||
| 
 | ||||
| {{readme}} | ||||
| 
 | ||||
| ## License | ||||
| 
 | ||||
| Licensed under either of | ||||
| 
 | ||||
| * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or https://www.apache.org/licenses/LICENSE-2.0) | ||||
| * MIT license ([LICENSE-MIT](LICENSE-MIT) or https://opensource.org/licenses/MIT) | ||||
| 
 | ||||
| at your option. | ||||
| 
 | ||||
| ### Contribution | ||||
| 
 | ||||
| Unless you explicitly state otherwise, any contribution intentionally | ||||
| submitted for inclusion in the work by you, as defined in the Apache-2.0 | ||||
| license, shall be dual licensed as above, without any additional terms or | ||||
| conditions. | ||||
							
								
								
									
										1
									
								
								booksrc/LICENSE-APACHE
									
										
									
									
									
										Symbolic link
									
								
							
							
						
						
									
										1
									
								
								booksrc/LICENSE-APACHE
									
										
									
									
									
										Symbolic link
									
								
							|  | @ -0,0 +1 @@ | |||
| ../LICENSE-APACHE | ||||
							
								
								
									
										1
									
								
								booksrc/LICENSE-MIT
									
										
									
									
									
										Symbolic link
									
								
							
							
						
						
									
										1
									
								
								booksrc/LICENSE-MIT
									
										
									
									
									
										Symbolic link
									
								
							|  | @ -0,0 +1 @@ | |||
| ../LICENSE-MIT | ||||
|  | @ -1,6 +1,6 @@ | |||
| # ErrorKind to the rescue | ||||
| 
 | ||||
| To cope with different kind of errors, we introduce the kind of an error `Func1ErrorKind` with an enum. | ||||
| To cope with different kind of errors, we introduce the `kind` of an error `Func1ErrorKind` with an enum. | ||||
| 
 | ||||
| Because we derive `Debug` and implement `Display` our `Func1ErrorKind` enum, this enum can be used as | ||||
| a `std::error::Error`. | ||||
|  | @ -8,10 +8,9 @@ a `std::error::Error`. | |||
| Only returning `Func1ErrorKind` in `func1()` now let us get rid of `Result<(), Box<Error + Send + Sync>>` and we can | ||||
| use `ChainResult<(), Func1ErrorKind>`. | ||||
| 
 | ||||
| In `main` we can now directly use the methods of `ChainError<T>` without downcasting the error first. | ||||
| In `main` we can now directly use the methods of `chainerror::Error<T>` without downcasting the error first. | ||||
| 
 | ||||
| Also a nice `match` on `ChainError<T>.kind()` is now possible, which returns `&T`, meaning  | ||||
| `&Func1ErrorKind` here. | ||||
| Also, a nice `match` on `chainerror::Error<T>.kind()` is now possible, which returns `&T`, meaning `&Func1ErrorKind` here. | ||||
| 
 | ||||
| ~~~rust | ||||
| {{#include ../examples/tutorial10.rs}} | ||||
|  |  | |||
|  | @ -21,7 +21,7 @@ which gives us a lot more detail. | |||
| 
 | ||||
| To create your own Errors, you might find [crates](https://crates.io) which create enum `Display+Debug` via derive macros. | ||||
| 
 | ||||
| Also noteworthy is [custom_error](https://crates.io/crates/custom_error) to define your custom errors, | ||||
| Also, noteworthy is [custom_error](https://crates.io/crates/custom_error) to define your custom errors, | ||||
| which can then be used with `chainerror`. | ||||
| 
 | ||||
| ~~~rust | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| # Deref for the ErrorKind | ||||
| 
 | ||||
| Because ChainError<T> implements Deref to &T, we can also match on `*e` instead of `e.kind()` | ||||
| Because chainerror::Error<T> implements Deref to &T, we can also match on `*e` instead of `e.kind()` | ||||
| or call a function with `&e` | ||||
| ~~~rust | ||||
| {{#include ../examples/tutorial12.rs}} | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| # Writing a library | ||||
| 
 | ||||
| I would advise to only expose an `mycrate::ErrorKind` and type alias `mycrate::Error` to `ChainError<mycrate::ErrorKind>` | ||||
| I would advise to only expose an `mycrate::ErrorKind` and type alias `mycrate::Error` to `chainerror::Error<mycrate::ErrorKind>` | ||||
| so you can tell your library users to use the `.kind()` method as `std::io::Error` does. | ||||
| 
 | ||||
| If you later decide to make your own `Error` implementation, your library users don't | ||||
|  |  | |||
|  | @ -25,7 +25,7 @@ along with the `Location` of the `context()` call and returns `Err(newerror)`. | |||
| `?` then returns the inner error applying `.into()`, so that we | ||||
| again have a `Err(Box<Error + Send + Sync>)` as a result. | ||||
| 
 | ||||
| The `Debug` implementation of `ChainError<T>` (which is returned by `context()`) | ||||
| The `Debug` implementation of `chainerror::Error<T>` (which is returned by `context()`) | ||||
| prints the `Debug` of `T` prefixed with the stored filename and line number. | ||||
| 
 | ||||
| `ChainError<T>` in our case is `ChainError<&str>`. | ||||
| `chainerror::Error<T>` in our case is `chainerror::Error<&str>`. | ||||
|  |  | |||
|  | @ -14,13 +14,13 @@ If you compare the output to the previous example, you will see, | |||
| that: | ||||
| 
 | ||||
| ~~~ | ||||
| Error: src/main.rs:19: "func1 error" | ||||
| Error: examples/tutorial2.rs:20:16: func1 error | ||||
| ~~~ | ||||
| 
 | ||||
| changed to just: | ||||
| 
 | ||||
| ~~~ | ||||
| src/main.rs:16: "func1 error" | ||||
| examples/tutorial3.rs:17:13: func1 error | ||||
| ~~~ | ||||
| 
 | ||||
| This is, because we caught the error of `func1()` in `main()` and print it out ourselves. | ||||
|  |  | |||
|  | @ -14,5 +14,4 @@ Sometimes you want to inspect the `source()` of an `Error`. | |||
| Note, that because we changed the output of the error in `main()` from  | ||||
| `Debug` to `Display`, we don't see the error backtrace with filename and line number. | ||||
| 
 | ||||
| To enable the `Display` backtrace, you have to enable the feature `display-cause` for `chainerror`. | ||||
|   | ||||
| To use the `Display` backtrace, you have to use the alternative display format output `{:#}`. | ||||
|  |  | |||
|  | @ -4,15 +4,15 @@ | |||
| 
 | ||||
| ~~~rust,ignore | ||||
| fn is_chain<T: 'static + Display + Debug>(&self) -> bool | ||||
| fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> | ||||
| fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> | ||||
| fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&chainerror::Error<T>> | ||||
| fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut chainerror::Error<T>> | ||||
| fn root_cause(&self) -> Option<&(dyn Error + 'static)> | ||||
| fn find_cause<U: Error + 'static>(&self) -> Option<&U> | ||||
| fn find_chain_cause<U: Error + 'static>(&self) -> Option<&ChainError<U>> | ||||
| fn find_chain_cause<U: Error + 'static>(&self) -> Option<&chainerror::Error<U>> | ||||
| fn kind<'a>(&'a self) -> &'a T | ||||
| ~~~ | ||||
| 
 | ||||
| Using `downcast_chain_ref::<String>()` gives a `ChainError<String>`, which can be used | ||||
| Using `downcast_chain_ref::<String>()` gives a `chainerror::Error<String>`, which can be used | ||||
| to call `.find_cause::<io::Error>()`.  | ||||
| 
 | ||||
| ~~~rust,ignore | ||||
|  |  | |||
|  | @ -1,14 +1,14 @@ | |||
| # Finding an Error cause | ||||
| 
 | ||||
| To distinguish the errors occuring in various places, we can define named string errors with the | ||||
| To distinguish the errors occurring in various places, we can define named string errors with the | ||||
| "new type" pattern. | ||||
| 
 | ||||
| ~~~rust,ignore | ||||
| derive_str_context!(Func2Error); | ||||
| derive_str_context!(Func1Error); | ||||
| chainerror::str_context!(Func2Error); | ||||
| chainerror::str_context!(Func1Error); | ||||
| ~~~ | ||||
| 
 | ||||
| Instead of `ChainError<String>` we now have `struct Func1Error(String)` and `ChainError<Func1Error>`. | ||||
| Instead of `chainerror::Error<String>` we now have `struct Func1Error(String)` and `chainerror::Error<Func1Error>`. | ||||
| 
 | ||||
| In the `main` function you can see, how we can match the different errors. | ||||
| 
 | ||||
|  | @ -18,9 +18,9 @@ Also see: | |||
| ~~~ | ||||
| as a shortcut to | ||||
| ~~~rust,ignore | ||||
|             if let Some(f2err) = f1err.find_cause::<ChainError<Func2Error>>() { | ||||
|             if let Some(f2err) = f1err.find_cause::<chainerror::Error<Func2Error>>() { | ||||
| ~~~ | ||||
| hiding the `ChainError<T>` implementation detail. | ||||
| hiding the `chainerror::Error<T>` implementation detail. | ||||
| 
 | ||||
| ~~~rust | ||||
| {{#include ../examples/tutorial8.rs}} | ||||
|  |  | |||
|  | @ -1,24 +1,28 @@ | |||
| use chainerror::Context as _; | ||||
| use std::error::Error; | ||||
| use std::fmt; | ||||
| use std::io; | ||||
| use std::result::Result; | ||||
| 
 | ||||
| use chainerror::prelude::v1::*; | ||||
| 
 | ||||
| fn do_some_io() -> Result<(), Box<dyn Error + Send + Sync>> { | ||||
|     Err(io::Error::from(io::ErrorKind::NotFound))?; | ||||
|     Ok(()) | ||||
| } | ||||
| 
 | ||||
| fn func3() -> Result<(), Box<dyn Error + Send + Sync>> { | ||||
| fn func4() -> Result<(), Box<dyn Error + Send + Sync>> { | ||||
|     let filename = "foo.txt"; | ||||
|     do_some_io().context(format!("Error reading '{}'", filename))?; | ||||
|     Ok(()) | ||||
| } | ||||
| 
 | ||||
| derive_str_context!(Func2Error); | ||||
| fn func3() -> Result<(), Box<dyn Error + Send + Sync>> { | ||||
|     func4().annotate()?; | ||||
|     Ok(()) | ||||
| } | ||||
| 
 | ||||
| fn func2() -> ChainResult<(), Func2Error> { | ||||
|     func3().context(Func2Error("func2 error: calling func3".to_string()))?; | ||||
| chainerror::str_context!(Func2Error); | ||||
| 
 | ||||
| fn func2() -> chainerror::Result<(), Func2Error> { | ||||
|     func3().context(Func2Error::new("func2 error: calling func3"))?; | ||||
|     Ok(()) | ||||
| } | ||||
| 
 | ||||
|  | @ -27,8 +31,8 @@ enum Func1Error { | |||
|     IO(String), | ||||
| } | ||||
| 
 | ||||
| impl ::std::fmt::Display for Func1Error { | ||||
|     fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { | ||||
| impl fmt::Display for Func1Error { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         match self { | ||||
|             Func1Error::Func2 => write!(f, "func1 error calling func2"), | ||||
|             Func1Error::IO(filename) => write!(f, "Error reading '{}'", filename), | ||||
|  | @ -36,13 +40,13 @@ impl ::std::fmt::Display for Func1Error { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| impl ::std::fmt::Debug for Func1Error { | ||||
|     fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { | ||||
| impl fmt::Debug for Func1Error { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         write!(f, "{}", self) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn func1() -> ChainResult<(), Func1Error> { | ||||
| fn func1() -> chainerror::Result<(), Func1Error> { | ||||
|     func2().context(Func1Error::Func2)?; | ||||
|     let filename = String::from("bar.txt"); | ||||
|     do_some_io().context(Func1Error::IO(filename))?; | ||||
|  |  | |||
|  | @ -3,7 +3,6 @@ | |||
| 
 | ||||
| use std::error::Error; | ||||
| use std::io; | ||||
| use std::result::Result; | ||||
| 
 | ||||
| fn do_some_io() -> Result<(), Box<dyn Error + Send + Sync>> { | ||||
|     Err(io::Error::from(io::ErrorKind::NotFound))?; | ||||
|  |  | |||
|  | @ -1,14 +1,14 @@ | |||
| use chainerror::prelude::v1::*; | ||||
| use chainerror::Context as _; | ||||
| 
 | ||||
| use std::error::Error; | ||||
| use std::io; | ||||
| use std::result::Result; | ||||
| 
 | ||||
| fn do_some_io() -> Result<(), Box<dyn Error + Send + Sync>> { | ||||
|     Err(io::Error::from(io::ErrorKind::NotFound))?; | ||||
|     Ok(()) | ||||
| } | ||||
| 
 | ||||
| derive_str_context!(Func2Error); | ||||
| chainerror::str_context!(Func2Error); | ||||
| 
 | ||||
| fn func2() -> Result<(), Box<dyn Error + Send + Sync>> { | ||||
|     let filename = "foo.txt"; | ||||
|  | @ -32,7 +32,7 @@ impl ::std::fmt::Display for Func1ErrorKind { | |||
| } | ||||
| impl ::std::error::Error for Func1ErrorKind {} | ||||
| 
 | ||||
| fn func1() -> ChainResult<(), Func1ErrorKind> { | ||||
| fn func1() -> chainerror::Result<(), Func1ErrorKind> { | ||||
|     func2().context(Func1ErrorKind::Func2)?; | ||||
|     let filename = String::from("bar.txt"); | ||||
|     do_some_io().context(Func1ErrorKind::IO(filename))?; | ||||
|  |  | |||
|  | @ -1,14 +1,14 @@ | |||
| use chainerror::prelude::v1::*; | ||||
| use chainerror::Context as _; | ||||
| 
 | ||||
| use std::error::Error; | ||||
| use std::io; | ||||
| use std::result::Result; | ||||
| 
 | ||||
| fn do_some_io() -> Result<(), Box<dyn Error + Send + Sync>> { | ||||
|     Err(io::Error::from(io::ErrorKind::NotFound))?; | ||||
|     Ok(()) | ||||
| } | ||||
| 
 | ||||
| derive_str_context!(Func2Error); | ||||
| chainerror::str_context!(Func2Error); | ||||
| 
 | ||||
| fn func2() -> Result<(), Box<dyn Error + Send + Sync>> { | ||||
|     let filename = "foo.txt"; | ||||
|  | @ -38,7 +38,7 @@ impl ::std::fmt::Debug for Func1ErrorKind { | |||
| 
 | ||||
| impl ::std::error::Error for Func1ErrorKind {} | ||||
| 
 | ||||
| fn func1() -> ChainResult<(), Func1ErrorKind> { | ||||
| fn func1() -> chainerror::Result<(), Func1ErrorKind> { | ||||
|     func2().context(Func1ErrorKind::Func2)?; | ||||
|     let filename = String::from("bar.txt"); | ||||
|     do_some_io().context(Func1ErrorKind::IO(filename))?; | ||||
|  |  | |||
|  | @ -1,14 +1,14 @@ | |||
| use chainerror::prelude::v1::*; | ||||
| use chainerror::Context as _; | ||||
| 
 | ||||
| use std::error::Error; | ||||
| use std::io; | ||||
| use std::result::Result; | ||||
| 
 | ||||
| fn do_some_io() -> Result<(), Box<dyn Error + Send + Sync>> { | ||||
|     Err(io::Error::from(io::ErrorKind::NotFound))?; | ||||
|     Ok(()) | ||||
| } | ||||
| 
 | ||||
| derive_str_context!(Func2Error); | ||||
| chainerror::str_context!(Func2Error); | ||||
| 
 | ||||
| fn func2() -> Result<(), Box<dyn Error + Send + Sync>> { | ||||
|     let filename = "foo.txt"; | ||||
|  | @ -38,7 +38,7 @@ impl ::std::fmt::Debug for Func1ErrorKind { | |||
| 
 | ||||
| impl ::std::error::Error for Func1ErrorKind {} | ||||
| 
 | ||||
| fn func1() -> ChainResult<(), Func1ErrorKind> { | ||||
| fn func1() -> chainerror::Result<(), Func1ErrorKind> { | ||||
|     func2().context(Func1ErrorKind::Func2)?; | ||||
|     let filename = String::from("bar.txt"); | ||||
|     do_some_io().context(Func1ErrorKind::IO(filename))?; | ||||
|  |  | |||
|  | @ -2,7 +2,8 @@ | |||
| #![allow(clippy::redundant_pattern_matching)] | ||||
| 
 | ||||
| pub mod mycrate { | ||||
|     use chainerror::prelude::v1::*; | ||||
|     use chainerror::Context as _; | ||||
| 
 | ||||
|     use std::io; | ||||
| 
 | ||||
|     fn do_some_io() -> std::result::Result<(), Box<dyn std::error::Error + Send + Sync>> { | ||||
|  | @ -10,7 +11,7 @@ pub mod mycrate { | |||
|         Ok(()) | ||||
|     } | ||||
| 
 | ||||
|     derive_str_context!(Func2Error); | ||||
|     chainerror::str_context!(Func2Error); | ||||
| 
 | ||||
|     fn func2() -> std::result::Result<(), Box<dyn std::error::Error + Send + Sync>> { | ||||
|         let filename = "foo.txt"; | ||||
|  | @ -24,7 +25,7 @@ pub mod mycrate { | |||
|         IO(String), | ||||
|     } | ||||
| 
 | ||||
|     derive_err_kind!(Error, ErrorKind); | ||||
|     chainerror::err_kind!(Error, ErrorKind); | ||||
| 
 | ||||
|     pub type Result<T> = std::result::Result<T, Error>; | ||||
| 
 | ||||
|  |  | |||
|  | @ -2,7 +2,8 @@ | |||
| #![allow(clippy::redundant_pattern_matching)] | ||||
| 
 | ||||
| pub mod mycrate { | ||||
|     use chainerror::prelude::v1::*; | ||||
|     use chainerror::{Context as _, ErrorDown as _}; | ||||
| 
 | ||||
|     use std::io; | ||||
| 
 | ||||
|     fn do_some_io(_f: &str) -> std::result::Result<(), io::Error> { | ||||
|  | @ -10,7 +11,7 @@ pub mod mycrate { | |||
|         Ok(()) | ||||
|     } | ||||
| 
 | ||||
|     derive_str_context!(Func2Error); | ||||
|     chainerror::str_context!(Func2Error); | ||||
| 
 | ||||
|     fn func2() -> std::result::Result<(), Box<dyn std::error::Error + Send + Sync>> { | ||||
|         let filename = "foo.txt"; | ||||
|  | @ -26,7 +27,7 @@ pub mod mycrate { | |||
|         Unknown, | ||||
|     } | ||||
| 
 | ||||
|     derive_err_kind!(Error, ErrorKind); | ||||
|     chainerror::err_kind!(Error, ErrorKind); | ||||
|     pub type Result<T> = std::result::Result<T, Error>; | ||||
| 
 | ||||
|     impl std::fmt::Display for ErrorKind { | ||||
|  |  | |||
|  | @ -1,8 +1,7 @@ | |||
| use chainerror::prelude::v1::*; | ||||
| use chainerror::Context as _; | ||||
| 
 | ||||
| use std::error::Error; | ||||
| use std::io; | ||||
| use std::result::Result; | ||||
| 
 | ||||
| fn do_some_io() -> Result<(), Box<dyn Error + Send + Sync>> { | ||||
|     Err(io::Error::from(io::ErrorKind::NotFound))?; | ||||
|  |  | |||
|  | @ -1,8 +1,7 @@ | |||
| use chainerror::prelude::v1::*; | ||||
| use chainerror::Context as _; | ||||
| 
 | ||||
| use std::error::Error; | ||||
| use std::io; | ||||
| use std::result::Result; | ||||
| 
 | ||||
| fn do_some_io() -> Result<(), Box<dyn Error + Send + Sync>> { | ||||
|     Err(io::Error::from(io::ErrorKind::NotFound))?; | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| use chainerror::prelude::v1::*; | ||||
| use chainerror::Context as _; | ||||
| 
 | ||||
| use std::error::Error; | ||||
| use std::io; | ||||
| use std::result::Result; | ||||
| 
 | ||||
| fn do_some_io() -> Result<(), Box<dyn Error + Send + Sync>> { | ||||
|     Err(io::Error::from(io::ErrorKind::NotFound))?; | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| use chainerror::prelude::v1::*; | ||||
| use chainerror::Context as _; | ||||
| 
 | ||||
| use std::error::Error; | ||||
| use std::io; | ||||
| use std::result::Result; | ||||
| 
 | ||||
| fn do_some_io() -> Result<(), Box<dyn Error + Send + Sync>> { | ||||
|     Err(io::Error::from(io::ErrorKind::NotFound))?; | ||||
|  |  | |||
|  | @ -1,10 +1,10 @@ | |||
| #![allow(clippy::single_match)] | ||||
| #![allow(clippy::redundant_pattern_matching)] | ||||
| 
 | ||||
| use chainerror::prelude::v1::*; | ||||
| use chainerror::Context as _; | ||||
| 
 | ||||
| use std::error::Error; | ||||
| use std::io; | ||||
| use std::result::Result; | ||||
| 
 | ||||
| fn do_some_io() -> Result<(), Box<dyn Error + Send + Sync>> { | ||||
|     Err(io::Error::from(io::ErrorKind::NotFound))?; | ||||
|  |  | |||
|  | @ -1,10 +1,10 @@ | |||
| #![allow(clippy::single_match)] | ||||
| #![allow(clippy::redundant_pattern_matching)] | ||||
| 
 | ||||
| use chainerror::prelude::v1::*; | ||||
| use chainerror::{Context as _, ErrorDown as _}; | ||||
| 
 | ||||
| use std::error::Error; | ||||
| use std::io; | ||||
| use std::result::Result; | ||||
| 
 | ||||
| fn do_some_io() -> Result<(), Box<dyn Error + Send + Sync>> { | ||||
|     Err(io::Error::from(io::ErrorKind::NotFound))?; | ||||
|  |  | |||
|  | @ -1,14 +1,14 @@ | |||
| use chainerror::prelude::v1::*; | ||||
| use chainerror::{Context as _, ErrorDown as _}; | ||||
| 
 | ||||
| use std::error::Error; | ||||
| use std::io; | ||||
| use std::result::Result; | ||||
| 
 | ||||
| fn do_some_io() -> Result<(), Box<dyn Error + Send + Sync>> { | ||||
|     Err(io::Error::from(io::ErrorKind::NotFound))?; | ||||
|     Ok(()) | ||||
| } | ||||
| 
 | ||||
| derive_str_context!(Func2Error); | ||||
| chainerror::str_context!(Func2Error); | ||||
| 
 | ||||
| fn func2() -> Result<(), Box<dyn Error + Send + Sync>> { | ||||
|     let filename = "foo.txt"; | ||||
|  | @ -16,10 +16,10 @@ fn func2() -> Result<(), Box<dyn Error + Send + Sync>> { | |||
|     Ok(()) | ||||
| } | ||||
| 
 | ||||
| derive_str_context!(Func1Error); | ||||
| chainerror::str_context!(Func1Error); | ||||
| 
 | ||||
| fn func1() -> Result<(), Box<dyn Error + Send + Sync>> { | ||||
|     func2().context(Func1Error("func1 error".to_string()))?; | ||||
|     func2().context(Func1Error::new("func1 error"))?; | ||||
|     Ok(()) | ||||
| } | ||||
| 
 | ||||
|  | @ -28,7 +28,7 @@ fn main() -> Result<(), Box<dyn Error + Send + Sync>> { | |||
|         if let Some(f1err) = e.downcast_chain_ref::<Func1Error>() { | ||||
|             eprintln!("Func1Error: {}", f1err); | ||||
| 
 | ||||
|             if let Some(f2err) = f1err.find_cause::<ChainError<Func2Error>>() { | ||||
|             if let Some(f2err) = f1err.find_cause::<chainerror::Error<Func2Error>>() { | ||||
|                 eprintln!("Func2Error: {}", f2err); | ||||
|             } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,14 +1,14 @@ | |||
| use chainerror::prelude::v1::*; | ||||
| use chainerror::{Context as _, ErrorDown}; | ||||
| 
 | ||||
| use std::error::Error; | ||||
| use std::io; | ||||
| use std::result::Result; | ||||
| 
 | ||||
| fn do_some_io() -> Result<(), Box<dyn Error + Send + Sync>> { | ||||
|     Err(io::Error::from(io::ErrorKind::NotFound))?; | ||||
|     Ok(()) | ||||
| } | ||||
| 
 | ||||
| derive_str_context!(Func2Error); | ||||
| chainerror::str_context!(Func2Error); | ||||
| 
 | ||||
| fn func2() -> Result<(), Box<dyn Error + Send + Sync>> { | ||||
|     let filename = "foo.txt"; | ||||
|  | @ -16,11 +16,11 @@ fn func2() -> Result<(), Box<dyn Error + Send + Sync>> { | |||
|     Ok(()) | ||||
| } | ||||
| 
 | ||||
| derive_str_context!(Func1ErrorFunc2); | ||||
| derive_str_context!(Func1ErrorIO); | ||||
| chainerror::str_context!(Func1ErrorFunc2); | ||||
| chainerror::str_context!(Func1ErrorIO); | ||||
| 
 | ||||
| fn func1() -> Result<(), Box<dyn Error + Send + Sync>> { | ||||
|     func2().context(Func1ErrorFunc2("func1 error calling func2".to_string()))?; | ||||
|     func2().context(Func1ErrorFunc2::new("func1 error calling func2"))?; | ||||
|     let filename = "bar.txt"; | ||||
|     do_some_io().context(Func1ErrorIO(format!("Error reading '{}'", filename)))?; | ||||
|     Ok(()) | ||||
|  | @ -28,7 +28,7 @@ fn func1() -> Result<(), Box<dyn Error + Send + Sync>> { | |||
| 
 | ||||
| fn main() -> Result<(), Box<dyn Error + Send + Sync>> { | ||||
|     if let Err(e) = func1() { | ||||
|         if let Some(s) = e.downcast_ref::<ChainError<Func1ErrorIO>>() { | ||||
|         if let Some(s) = e.downcast_ref::<chainerror::Error<Func1ErrorIO>>() { | ||||
|             eprintln!("Func1ErrorIO:\n{:?}", s); | ||||
|         } | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										116
									
								
								src/lib.rs
									
										
									
									
									
								
							
							
						
						
									
										116
									
								
								src/lib.rs
									
										
									
									
									
								
							|  | @ -8,18 +8,6 @@ use std::error::Error as StdError; | |||
| use std::fmt::{Debug, Display, Formatter}; | ||||
| use std::panic::Location; | ||||
| 
 | ||||
| pub mod prelude { | ||||
|     //! convenience prelude
 | ||||
|     pub mod v1 { | ||||
|         //! convenience prelude
 | ||||
|         pub use super::super::ChainErrorDown as _; | ||||
|         pub use super::super::Error as ChainError; | ||||
|         pub use super::super::Result as ChainResult; | ||||
|         pub use super::super::ResultTrait as _; | ||||
|         pub use crate::{derive_err_kind, derive_str_context}; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// chains an inner error kind `T` with a causing error
 | ||||
| pub struct Error<T> { | ||||
|     occurrence: Option<String>, | ||||
|  | @ -55,7 +43,8 @@ impl<T: 'static + Display + Debug> Error<T> { | |||
|     /// # Examples
 | ||||
|     ///
 | ||||
|     /// ```rust
 | ||||
|     /// use chainerror::prelude::v1::*;
 | ||||
|     /// use chainerror::Context as _;
 | ||||
|     /// use chainerror::ErrorDown as _;
 | ||||
|     /// use std::error::Error;
 | ||||
|     /// use std::io;
 | ||||
|     ///
 | ||||
|  | @ -64,7 +53,7 @@ impl<T: 'static + Display + Debug> Error<T> { | |||
|     ///     Ok(())
 | ||||
|     /// }
 | ||||
|     ///
 | ||||
|     /// derive_str_context!(Func2Error);
 | ||||
|     /// chainerror::str_context!(Func2Error);
 | ||||
|     ///
 | ||||
|     /// fn func2() -> Result<(), Box<dyn Error + Send + Sync>> {
 | ||||
|     ///     let filename = "foo.txt";
 | ||||
|  | @ -72,10 +61,10 @@ impl<T: 'static + Display + Debug> Error<T> { | |||
|     ///     Ok(())
 | ||||
|     /// }
 | ||||
|     ///
 | ||||
|     /// derive_str_context!(Func1Error);
 | ||||
|     /// chainerror::str_context!(Func1Error);
 | ||||
|     ///
 | ||||
|     /// fn func1() -> Result<(), Box<dyn Error + Send + Sync>> {
 | ||||
|     ///     func2().context(Func1Error("func1 error".into()))?;
 | ||||
|     ///     func2().context(Func1Error::new("func1 error"))?;
 | ||||
|     ///     Ok(())
 | ||||
|     /// }
 | ||||
|     ///
 | ||||
|  | @ -107,13 +96,12 @@ impl<T: 'static + Display + Debug> Error<T> { | |||
|     /// # Examples
 | ||||
|     ///
 | ||||
|     /// ```rust
 | ||||
|     /// # use chainerror::prelude::v1::*;
 | ||||
|     /// # derive_str_context!(FooError);
 | ||||
|     /// # let err = ChainError::new(String::new(), None, None);
 | ||||
|     /// # chainerror::str_context!(FooError);
 | ||||
|     /// # let err = chainerror::Error::new(String::new(), None, None);
 | ||||
|     /// // Instead of writing
 | ||||
|     /// err.find_cause::<ChainError<FooError>>();
 | ||||
|     /// err.find_cause::<chainerror::Error<FooError>>();
 | ||||
|     ///
 | ||||
|     /// // leave out the ChainError<FooError> implementation detail
 | ||||
|     /// // leave out the chainerror::Error<FooError> implementation detail
 | ||||
|     /// err.find_chain_cause::<FooError>();
 | ||||
|     /// ```
 | ||||
|     #[inline] | ||||
|  | @ -130,17 +118,16 @@ impl<T: 'static + Display + Debug> Error<T> { | |||
|     /// # Examples
 | ||||
|     ///
 | ||||
|     /// ```rust
 | ||||
|     /// # use chainerror::prelude::v1::*;
 | ||||
|     /// # derive_str_context!(FooErrorKind);
 | ||||
|     /// # let err = ChainError::new(String::new(), None, None);
 | ||||
|     /// # chainerror::str_context!(FooErrorKind);
 | ||||
|     /// # let err = chainerror::Error::new(String::new(), None, None);
 | ||||
|     /// // Instead of writing
 | ||||
|     /// err.find_cause::<ChainError<FooErrorKind>>();
 | ||||
|     /// err.find_cause::<chainerror::Error<FooErrorKind>>();
 | ||||
|     /// // and/or
 | ||||
|     /// err.find_chain_cause::<FooErrorKind>();
 | ||||
|     /// // and/or
 | ||||
|     /// err.find_cause::<FooErrorKind>();
 | ||||
|     ///
 | ||||
|     /// // leave out the ChainError<FooErrorKind> implementation detail
 | ||||
|     /// // leave out the chainerror::Error<FooErrorKind> implementation detail
 | ||||
|     /// err.find_kind_or_cause::<FooErrorKind>();
 | ||||
|     /// ```
 | ||||
|     #[inline] | ||||
|  | @ -159,7 +146,7 @@ impl<T: 'static + Display + Debug> Error<T> { | |||
|     /// # Examples
 | ||||
|     ///
 | ||||
|     /// ```rust
 | ||||
|     /// use chainerror::prelude::v1::*;
 | ||||
|     /// use chainerror::Context as _;
 | ||||
|     /// use std::error::Error;
 | ||||
|     /// use std::io;
 | ||||
|     ///
 | ||||
|  | @ -168,7 +155,7 @@ impl<T: 'static + Display + Debug> Error<T> { | |||
|     ///     Ok(())
 | ||||
|     /// }
 | ||||
|     ///
 | ||||
|     /// derive_str_context!(Func2Error);
 | ||||
|     /// chainerror::str_context!(Func2Error);
 | ||||
|     ///
 | ||||
|     /// fn func2() -> Result<(), Box<dyn Error + Send + Sync>> {
 | ||||
|     ///     let filename = "foo.txt";
 | ||||
|  | @ -192,7 +179,7 @@ impl<T: 'static + Display + Debug> Error<T> { | |||
|     /// #     }
 | ||||
|     /// # }
 | ||||
|     ///
 | ||||
|     /// fn func1() -> ChainResult<(), Func1ErrorKind> {
 | ||||
|     /// fn func1() -> chainerror::Result<(), Func1ErrorKind> {
 | ||||
|     ///     func2().context(Func1ErrorKind::Func2)?;
 | ||||
|     ///     do_some_io().context(Func1ErrorKind::IO("bar.txt".into()))?;
 | ||||
|     ///     Ok(())
 | ||||
|  | @ -225,10 +212,13 @@ impl<T: 'static + Display + Debug> Error<T> { | |||
| } | ||||
| 
 | ||||
| /// Convenience methods for `Result<>` to turn the error into a decorated [`Error`](Error)
 | ||||
| pub trait ResultTrait<O, E: Into<Box<dyn StdError + 'static + Send + Sync>>> { | ||||
| pub trait Context<O, E: Into<Box<dyn StdError + 'static + Send + Sync>>> { | ||||
|     /// Decorate the error with a `kind` of type `T` and the source `Location`
 | ||||
|     fn context<T: 'static + Display + Debug>(self, kind: T) -> std::result::Result<O, Error<T>>; | ||||
| 
 | ||||
|     /// Decorate the error just with the source `Location`
 | ||||
|     fn annotate(self) -> std::result::Result<O, Error<AnnotatedError>>; | ||||
| 
 | ||||
|     /// Decorate the `error` with a `kind` of type `T` produced with a `FnOnce(&error)` and the source `Location`
 | ||||
|     fn map_context<T: 'static + Display + Debug, F: FnOnce(&E) -> T>( | ||||
|         self, | ||||
|  | @ -236,7 +226,22 @@ pub trait ResultTrait<O, E: Into<Box<dyn StdError + 'static + Send + Sync>>> { | |||
|     ) -> std::result::Result<O, Error<T>>; | ||||
| } | ||||
| 
 | ||||
| impl<O, E: Into<Box<dyn StdError + 'static + Send + Sync>>> ResultTrait<O, E> | ||||
| /// Convenience type to just decorate the error with the source `Location`
 | ||||
| pub struct AnnotatedError(()); | ||||
| 
 | ||||
| impl Display for AnnotatedError { | ||||
|     fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { | ||||
|         write!(f, "(passed error)") | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Debug for AnnotatedError { | ||||
|     fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { | ||||
|         write!(f, "(passed error)") | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<O, E: Into<Box<dyn StdError + 'static + Send + Sync>>> Context<O, E> | ||||
|     for std::result::Result<O, E> | ||||
| { | ||||
|     #[track_caller] | ||||
|  | @ -252,6 +257,19 @@ impl<O, E: Into<Box<dyn StdError + 'static + Send + Sync>>> ResultTrait<O, E> | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     #[track_caller] | ||||
|     #[inline] | ||||
|     fn annotate(self) -> std::result::Result<O, Error<AnnotatedError>> { | ||||
|         match self { | ||||
|             Ok(t) => Ok(t), | ||||
|             Err(error_cause) => Err(Error::new( | ||||
|                 AnnotatedError(()), | ||||
|                 Some(error_cause.into()), | ||||
|                 Some(Location::caller().to_string()), | ||||
|             )), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     #[track_caller] | ||||
|     #[inline] | ||||
|     fn map_context<T: 'static + Display + Debug, F: FnOnce(&E) -> T>( | ||||
|  | @ -298,7 +316,7 @@ impl<T: 'static + Display + Debug> std::ops::Deref for Error<T> { | |||
| } | ||||
| 
 | ||||
| /// Convenience trait to hide the [`Error<T>`](Error) implementation internals
 | ||||
| pub trait ChainErrorDown { | ||||
| pub trait ErrorDown { | ||||
|     /// Test if of type `Error<T>`
 | ||||
|     fn is_chain<T: 'static + Display + Debug>(&self) -> bool; | ||||
|     /// Downcast to a reference of `Error<T>`
 | ||||
|  | @ -311,7 +329,7 @@ pub trait ChainErrorDown { | |||
|     fn downcast_inner_mut<T: 'static + StdError>(&mut self) -> Option<&mut T>; | ||||
| } | ||||
| 
 | ||||
| impl<U: 'static + Display + Debug> ChainErrorDown for Error<U> { | ||||
| impl<U: 'static + Display + Debug> ErrorDown for Error<U> { | ||||
|     #[inline] | ||||
|     fn is_chain<T: 'static + Display + Debug>(&self) -> bool { | ||||
|         TypeId::of::<T>() == TypeId::of::<U>() | ||||
|  | @ -369,7 +387,7 @@ impl<U: 'static + Display + Debug> ChainErrorDown for Error<U> { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| impl ChainErrorDown for dyn StdError + 'static { | ||||
| impl ErrorDown for dyn StdError + 'static { | ||||
|     #[inline] | ||||
|     fn is_chain<T: 'static + Display + Debug>(&self) -> bool { | ||||
|         self.is::<Error<T>>() | ||||
|  | @ -402,7 +420,7 @@ impl ChainErrorDown for dyn StdError + 'static { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| impl ChainErrorDown for dyn StdError + 'static + Send { | ||||
| impl ErrorDown for dyn StdError + 'static + Send { | ||||
|     #[inline] | ||||
|     fn is_chain<T: 'static + Display + Debug>(&self) -> bool { | ||||
|         self.is::<Error<T>>() | ||||
|  | @ -435,7 +453,7 @@ impl ChainErrorDown for dyn StdError + 'static + Send { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| impl ChainErrorDown for dyn StdError + 'static + Send + Sync { | ||||
| impl ErrorDown for dyn StdError + 'static + Send + Sync { | ||||
|     #[inline] | ||||
|     fn is_chain<T: 'static + Display + Debug>(&self) -> bool { | ||||
|         self.is::<Error<T>>() | ||||
|  | @ -549,7 +567,8 @@ where | |||
| /// # Examples
 | ||||
| ///
 | ||||
| /// ```rust
 | ||||
| /// # use chainerror::prelude::v1::*;
 | ||||
| /// # use chainerror::Context as _;
 | ||||
| /// # use chainerror::ErrorDown as _;
 | ||||
| /// # use std::error::Error;
 | ||||
| /// # use std::io;
 | ||||
| /// # use std::result::Result;
 | ||||
|  | @ -557,23 +576,23 @@ where | |||
| /// #     Err(io::Error::from(io::ErrorKind::NotFound))?;
 | ||||
| /// #     Ok(())
 | ||||
| /// # }
 | ||||
| /// derive_str_context!(Func2Error);
 | ||||
| /// chainerror::str_context!(Func2Error);
 | ||||
| ///
 | ||||
| /// fn func2() -> ChainResult<(), Func2Error> {
 | ||||
| /// fn func2() -> chainerror::Result<(), Func2Error> {
 | ||||
| ///     let filename = "foo.txt";
 | ||||
| ///     do_some_io().context(Func2Error(format!("Error reading '{}'", filename)))?;
 | ||||
| ///     Ok(())
 | ||||
| /// }
 | ||||
| ///
 | ||||
| /// derive_str_context!(Func1Error);
 | ||||
| /// chainerror::str_context!(Func1Error);
 | ||||
| ///
 | ||||
| /// fn func1() -> Result<(), Box<dyn Error>> {
 | ||||
| ///     func2().context(Func1Error("func1 error".into()))?;
 | ||||
| ///     func2().context(Func1Error::new("func1 error"))?;
 | ||||
| ///     Ok(())
 | ||||
| /// }
 | ||||
| /// #     if let Err(e) = func1() {
 | ||||
| /// #         if let Some(f1err) = e.downcast_chain_ref::<Func1Error>() {
 | ||||
| /// #             assert!(f1err.find_cause::<ChainError<Func2Error>>().is_some());
 | ||||
| /// #             assert!(f1err.find_cause::<chainerror::Error<Func2Error>>().is_some());
 | ||||
| /// #             assert!(f1err.find_chain_cause::<Func2Error>().is_some());
 | ||||
| /// #         } else {
 | ||||
| /// #             panic!();
 | ||||
|  | @ -583,10 +602,15 @@ where | |||
| /// #     }
 | ||||
| /// ```
 | ||||
| #[macro_export] | ||||
| macro_rules! derive_str_context { | ||||
| macro_rules! str_context { | ||||
|     ($e:ident) => { | ||||
|         #[derive(Clone)] | ||||
|         pub struct $e(pub String); | ||||
|         impl $e { | ||||
|             pub fn new<S: Into<String>>(s: S) -> Self { | ||||
|                 $e(s.into()) | ||||
|             } | ||||
|         } | ||||
|         impl ::std::fmt::Display for $e { | ||||
|             fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { | ||||
|                 write!(f, "{}", self.0) | ||||
|  | @ -612,7 +636,7 @@ macro_rules! derive_str_context { | |||
| /// # Examples
 | ||||
| ///
 | ||||
| /// ```rust
 | ||||
| /// use chainerror::prelude::v1::*;
 | ||||
| /// use chainerror::Context as _;
 | ||||
| /// use std::io;
 | ||||
| ///
 | ||||
| /// fn do_some_io(_f: &str) -> std::result::Result<(), io::Error> {
 | ||||
|  | @ -626,7 +650,7 @@ macro_rules! derive_str_context { | |||
| ///     Unknown,
 | ||||
| /// }
 | ||||
| ///
 | ||||
| /// derive_err_kind!(Error, ErrorKind);
 | ||||
| /// chainerror::err_kind!(Error, ErrorKind);
 | ||||
| ///
 | ||||
| /// impl std::fmt::Display for ErrorKind {
 | ||||
| ///     fn fmt(&self, f: &mut ::std::fmt::Formatter) -> std::fmt::Result {
 | ||||
|  | @ -666,7 +690,7 @@ macro_rules! derive_str_context { | |||
| /// }
 | ||||
| /// ```
 | ||||
| #[macro_export] | ||||
| macro_rules! derive_err_kind { | ||||
| macro_rules! err_kind { | ||||
|     ($e:ident, $k:ident) => { | ||||
|         pub struct $e($crate::Error<$k>); | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| use chainerror::prelude::v1::*; | ||||
| use chainerror::Context; | ||||
| 
 | ||||
| #[test] | ||||
| fn test_basic() { | ||||
|  | @ -19,7 +19,6 @@ fn test_basic() { | |||
| 
 | ||||
|     if let Err(e) = process_config_file() { | ||||
|         let os_notfound_error = std::io::Error::from_raw_os_error(2); | ||||
|         eprintln!("Error:\n{:?}", e); | ||||
|         let s = format!("{:?}", e); | ||||
|         let lines = s.lines().collect::<Vec<_>>(); | ||||
|         assert_eq!(lines.len(), 5); | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| use chainerror::prelude::v1::*; | ||||
| use chainerror::Context; | ||||
| use std::error::Error; | ||||
| use std::io; | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 GitHub
							GitHub