I checked out thiserror, and liked some things about it, but I ultimately went with error chain. This is what my new error.rs
file looks like:
use std::path::Path;
mod error {
error_chain! {}
}
error_chain! {
foreign_links {
Clap(::clap::Error);
Yaml(::serde_yaml::Error);
Json(::serde_json::Error);
Io(::std::io::Error);
}
errors {
ScoreError(s: String) {
display("Name not found in scores.yaml file: '{}'", s)
}
LcovReaderError(e: lcov::reader::Error) {
display("{}", e)
}
}
}
impl From<lcov::reader::Error> for Error {
fn from(err: lcov::reader::Error) -> Error {
Error::from(ErrorKind::LcovReaderError(err))
}
}
For the most part I just use ?
to convert from other errors to my error type. Here's a slightly more involved example:
let records = reader
.collect::<std::result::Result<Vec<_>, lcov::reader::Error>>()
.map_err(Error::from)
.chain_err(|| {
format!(
"Unable to parse lcov string:
{}",
lcov_string
)
})?;
This was necessary because lcov::reader::Error
happens not to implement the Error
trait. Hence the extra logic in error.rs
.
I went with error chain because it has good documentation, gets referenced from several tutorials, and the chain_err
method is very useful for giving informative errors. For example if I introduce an unparsable error into my lcov string:
error: Unable to parse lcov string:
/*TN:*/
SF:src/solution.rs
FN:1,_ZN10assignment8solution3fib17ha0ec88ff33def664E
FN:1,_ZN10assignment8solution3fib17hbf227c473e17027fE
FNDA:1,_ZN10assignment8solution3fib17ha0ec88ff33def664E
FNDA:0,_ZN10assignment8solution3fib17hbf227c473e17027fE
FNF:2
FNH:1
BRDA:3,0,0,1
BRDA:3,0,1,1
BRDA:3,0,2,1
BRDA:5,0,0,1
BRDA:5,0,1,-
BRF:2
BRH:4
DA:1,9
DA:2,9
DA:3,9
DA:4,3
DA:5,4
DA:6,0
DA:8,9
LF:7
LH:6
end_of_record
caused by: invalid record syntax at line 1: unknown record
The part after "caused by:" is from the original error and the part after "error:" is my custom error string from chain_err
.