fn run() -> Result<Reply> {
let filename = env::args().skip(1).next().chain_err(
|| Reply::error("no query specified"))?;
let mut data = String::new();
let mut file = File::open(filename).chain_err(
|| Reply::error("failed to open query"))?;
file.read_to_string(&mut data).chain_err(
|| Reply::error("failed to read query"))?;
let query: Query = serde_json::from_str(&data).chain_err(
|| Reply::error("invalid query"))?;
//...
But this produces errors like this:
error[E0277]: the trait bound `errors::ErrorKind: std::convert::From<Reply>` is not satisfied
--> myapp\src\main.rs:62:47
|
62 | let filename = env::args().skip(1).next().chain_err(
| ^^^^^^^^^ the trait `std::convert::From<Reply>` is not
mplemented for `errors::ErrorKind`
|
= help: the following implementations were found:
<errors::ErrorKind as std::convert::From<&'a str>>
<errors::ErrorKind as std::convert::From<std::string::String>>
<errors::ErrorKind as std::convert::From<errors::Error>>
= note: required because of the requirements on the impl of `std::convert::Into<errors::ErrorKind>` for
`Reply`
I can see it is telling me to provide a conversion, but how do I do that? And how do I integrate it with error chain?
I think you’re going about this a bit incorrectly. IIUC, Reply should only serve the purpose of representing a successful request/operation; any error along the way should be encapsulated into the Error type you define using error_chain. If you then want to add contextual information to the chain, such as the file name it failed to open, you can chain_err a message on top; that will create a top level error with the message, and the underlying std::io::Error will be its cause. For example:
#[derive(Debug)]
struct Reply {
filename: String,
}
error_chain! {
foreign_links {
Io(std::io::Error);
Json(serde_json::error::Error);
}
errors {
UnspecifiedQuery {
description("No query specified")
display("No query specified")
}
}
}
fn run() -> Result<Reply> {
let filename = env::args()
.skip(1)
.next()
.ok_or_else(|| ErrorKind::UnspecifiedQuery)?;
let mut data = String::new();
// add `filename` as a contextual bit of info
let mut file = File::open(&filename).chain_err(|| format!("Failed to open file {}", filename))?;
file.read_to_string(&mut data)
.chain_err(|| "failed to read query")?;
let query: Query = serde_json::from_str(&data)?;
Ok(Reply {
filename: "blah".into(),
})
}
How you define the chain also depends on how callers of run() will process errors. For instance, you may want to do the following instead (all else the same):
error_chain! {
...
errors {
...
FileOpen(file: String) {
description("Failed to open file")
display("Failed to open file: {}", file)
}
}
}
fn run() -> Result<Reply> {
...
let mut file = File::open(&filename).chain_err(|| ErrorKind::FileOpen(filename.into()))?;
...
}
This chains a concrete top level error you’ve defined, and puts the std:io::Error as the cause. This makes it easier to match for FileOpen error kind in the caller, rather than having a generic error when chaining a string.