I was developing a rust cli for work and noticed several of the functions seemed pretty inflated due to
the same attempts at error handling. Usually this was around generically handling Result<Error>
types.
In application code, it's pretty common to "attempt" a function; if it errors, propagate the error, otherwise, continue. This is pretty hard to do without a bunch of boilerplate.
Unless you use the anyhow
crate.
Consider the following function that attempts to propagate Result<Error>
at various places.
pub async fn from_local_cookies() -> Result<Self, Box<dyn std::error::Error>> {
let mut cookies_path = get_dir().expect("could not get config dir");
cookies_path.push("cookies.json");
let file_res = std::fs::File::open(cookies_path.clone()).map(std::io::BufReader::new);
if file_res.is_err() {
let e = file_res.unwrap_err();
return Err(Box::new(e));
}
let cookie_store_res = reqwest_cookie_store::CookieStore::load_json(file_res.unwrap());
if cookie_store_res.is_err() {
let e = cookie_store_res.unwrap_err();
return Err(Box::new(e));
}
let cookie_store_mtx = reqwest_cookie_store::CookieStoreMutex::new(cookie_store_res.unwrap());
let cookie_store_arc = std::sync::Arc::new(cookie_store_mtx);
let client_init = reqwest::Client::builder()
.cookie_store(true)
.cookie_provider(std::sync::Arc::clone(&cookie_store_arc))
.build();
match client_init {
Ok(client) => {
let config = MyConfig::new();
Ok(Self { config, client })
}
Err(e) => Err(Box::new(e)),
}
}
As you can see, this code is pretty inflated, lots of distractions from the core logic.
With anyhow
, the code above becomes:
pub async fn from_local_cookies_with_anyhow() -> Result<Self> {
let mut cookies_path = get_dir()?;
cookies_path.push("cookies.json");
let cookies_file = std::fs::File::open(cookies_path).map(std::io::BufReader::new)?;
let cookies_store = reqwest_cookie_store::CookieStore::load_json(cookies_file)
.map_err(|e| anyhow::anyhow!("failed to load cookies from json: {:?}", e))?;
let cookies_store_mtx = reqwest_cookie_store::CookieStoreMutex::new(cookies_store);
let cookies_store_arc = std::sync::Arc::new(cookies_store_mtx);
let client = reqwest::Client::builder()
.cookie_store(true)
.cookie_provider(std::sync::Arc::clone(&cookies_store_arc))
.build()?;
let config = MyConfig::new();
Ok(Self { config, client })
}
Pretty nice right?