Expand description
§anyhow-std
This crate wraps certain std functionality to produce anyhow::Results providing better error messages w/ context.
§Example: Expecting a UTF8 Path Extension
Suppose we are processing a user-provided path and we expect it to have an extension that is valid UTF8:
use std::path::Path;
use anyhow_std::{PathAnyhow, OsStrAnyhow};
fn process_user_path(path: &Path) -> anyhow::Result<()> {
let extos = path.extension_anyhow()?;
let ext = extos.to_str_anyhow()?;
process_user_path_with_extension(path, ext)?;
Ok(())
}
fn process_user_path_with_extension(path: &Path, extension: &str) -> anyhow::Result<()> {
todo!("implement path processing of {path:?} for the extension {extension:?}")
}
/*
Now if the user provides a path without an extension, they'll get an
error message with more helpful context:
*/
let res = process_user_path(Path::new("/tmp/no-extension"));
assert!(res.is_err());
let error_message = format!("{:#}", res.err().unwrap());
assert_eq!(
error_message,
r#"while processing path "/tmp/no-extension": missing expected extension"#
);
/*
Unix systems can have non-UTF8 paths:
*/
#[cfg(target_family = "unix")]
fn demonstrate_non_utf8_extension() {
use std::ffi::OsStr;
use std::os::unix::ffi::OsStrExt;
let res = process_user_path(Path::new(OsStr::from_bytes(b"/tmp/non-unicode-extension.wacky-\xf3-extension")));
assert!(res.is_err());
let error_message = format!("{:#}", res.err().unwrap());
assert_eq!(
error_message,
r#"while processing os string "wacky-�-extension": not valid utf8"#,
);
}
#[cfg(target_family = "unix")]
demonstrate_non_utf8_extension();
§Extension Traits
A common pattern is used to extend std types:
- An extension trait is provided named
<std type>Anyhow
, for example: OsStrAnyhow - An impl is provided for the target type, for example
impl OsStrAnyhow for OsStr { … }
- For a subset of methods of the target type, extension trait methods are provided named
<method name>_anyhow
. These methods always return anyhow::Result types, for example:OsStrAnyhow::to_str_anyhow
. - Additionally, some
…_anyhow
methods may be provided to wrap functionality not found directly on the target type. For examplePath::read_to_string
does not exist, but would be a straightforward wrapper forstd::fs::read_to_string
, so this crate providesPathAnyhow::read_to_string_anyhow
By consistently appending _anyhow
to wrapped methods, callers can
unambiguously choose when to use these methods versus the std methods.
§…_anyhow
Methods
These methods convert Option<T>
or Result<T, E>
return types of
the std method into anyhow::Result<T>
, where the anyhow::Error
has context added specific to the std type.
For std methods that return Option<T>
, often a None
return type
isn’t necessarily an “error” per se, but the …_anyhow
methods result
in an error in this case. So these methods should only be used when code
expects Some
results, and if your code should handle None
as a
“non-error”, it can simply use the std method.
§Wrapper types
In some cases, it is necessary to use a “wrapper type” pattern rather than an extension trait, primarily to track extra data used in error contexts. For example the crate::fs wrapper types ReadDir, DirEntry, and Metadata each own a PathBuf in addition to the underlying std::fs type in order to provide paths in error contexts, in order to provide helpful error context for common directory and fs traversal uses.
Wrapper types override some underlying std type methods, rather
than using the …_anyhow
naming convention. They also provide
a std::ops::Deref impl for the underlying std type, so all
non-overridden methods can be called naturally and overridden methods
can be called by explicitly using wrapper.deref().target_method(…)
.
Types using this wrapper-type pattern consistently provide From /
Into implementations where possible for the underlying std types plus
any error context data. For example, for crate::fs::Metadata provides
From / Into impls for (std::fs::Metadata, std::fs::PathBuf)
,
the latter providing error context. There is an exception to this
for Child because it mutates the underlying
std::process::Child upon construction.
Finally, the Output type is an exception to
this wrapper pattern because it provides no methods and exposes all
contents as pub
fields.
§API Coverage
This crate only wraps a small subset of std based on what the author needs in other projects. If you’d like to see more std APIs wrapped, patches are welcome. ;-)
The 0.1.x
version series will add APIs as they are useful and may
change error context strings. The semantics of wrapped functions should
not vary much, but might.
Re-exports§
pub use self::process::CommandAnyhow;
Modules§
- env
- Wrappers for std::env
- fs
- Wrappers for std::fs which provide paths in error contexts
- process
- Wrappers for std::process which provide commands in error contexts
Traits§
- OsStr
Anyhow - Extend std::ffi::OsStr with anyhow methods
- Path
Anyhow - Extend Path with anyhow methods