-
Notifications
You must be signed in to change notification settings - Fork 60
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow the context selector struct name to be specified #210
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
You could already specify that no context selector should be generated, using context(false). Now you can specify the name of the context selector, using context(MySelectorTypeName).
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -46,6 +46,38 @@ fn main() { | |||||
} | ||||||
``` | ||||||
|
||||||
## Specifying the generated context selector type's name | ||||||
|
||||||
Sometimes, you may already have a struct with the same name as one of | ||||||
the variants of your error enum. You might also have 2 error enums | ||||||
that share a variant. In those cases, there may be naming collisions | ||||||
with the context selector type(s) generated by Snafu, which by default | ||||||
have the same name as the enum variant. | ||||||
|
||||||
To solve this, you can specify the name of the context selector that | ||||||
Snafu should generate, using `#[snafu(context(MySelectorName))]`. | ||||||
|
||||||
**Example** | ||||||
|
||||||
```rust | ||||||
# use snafu::{Snafu, ResultExt}; | ||||||
# | ||||||
// some struct not related to the error | ||||||
struct Foo; | ||||||
|
||||||
#[derive(Debug, Snafu)] | ||||||
enum Error { | ||||||
#[snafu(context(FooContext))] | ||||||
Foo { | ||||||
source: std::io::Error | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
}, | ||||||
} | ||||||
|
||||||
fn read_file() -> Result<Vec<u8>, Error> { | ||||||
std::fs::read("/some/file/that/doesnt/exist").context(FooContext) | ||||||
} | ||||||
``` | ||||||
|
||||||
## Controlling context | ||||||
|
||||||
Sometimes, an underlying error can only occur in exactly one context | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
// This test is the same as basic.rs but with custom context selectors | ||
|
||
use snafu::{ResultExt, Snafu}; | ||
use std::{ | ||
fs, io, | ||
path::{Path, PathBuf}, | ||
}; | ||
|
||
#[derive(Debug, Snafu)] | ||
enum Error { | ||
#[snafu( | ||
context(OpenConfigContext), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seeing it in use, I wonder if we want to give some more "namespacing". For example: context(name(OpenConfigContext)), Or context(selector(OpenConfigContext)), Or context(selector_name(OpenConfigContext)), |
||
display = r#"("Could not open config file at {}: {}", filename.display(), source)"# | ||
)] | ||
OpenConfig { | ||
filename: PathBuf, | ||
source: io::Error, | ||
}, | ||
#[snafu( | ||
context(SaveConfigContext), | ||
display = r#"("Could not open config file at {}", source)"# | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Feel free to use the modern style in these tests: display("Could not open config file at {}", source) |
||
)] | ||
SaveConfig { source: io::Error }, | ||
#[snafu( | ||
context(InvalidUserContext), | ||
display = r#"("User ID {} is invalid", user_id)"# | ||
)] | ||
Comment on lines
+24
to
+27
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This style is fine, which is why it compiles, but I just find it ugly for whatever reason. FWIW, I tend to split it into two: #[snafu(context(InvalidUserContext))]
#[snafu(display = r#"("User ID {} is invalid", user_id)"#)] |
||
InvalidUser { user_id: i32 }, | ||
#[snafu(context(MissingUserContext), display("No user available"))] | ||
MissingUser, | ||
} | ||
|
||
type Result<T, E = Error> = std::result::Result<T, E>; | ||
|
||
const CONFIG_FILENAME: &str = "/tmp/config"; | ||
|
||
fn example(root: impl AsRef<Path>, user_id: Option<i32>) -> Result<()> { | ||
let root = root.as_ref(); | ||
let filename = &root.join(CONFIG_FILENAME); | ||
|
||
let config = fs::read(filename).context(OpenConfigContext { filename })?; | ||
|
||
let _user_id = match user_id { | ||
None => MissingUserContext.fail()?, | ||
Some(user_id) if user_id != 42 => InvalidUserContext { user_id }.fail()?, | ||
Some(user_id) => user_id, | ||
}; | ||
|
||
fs::write(filename, config).context(SaveConfigContext)?; | ||
|
||
Ok(()) | ||
} | ||
|
||
#[test] | ||
fn implements_error() { | ||
fn check<T: std::error::Error>() {} | ||
check::<Error>(); | ||
example("/some/directory/that/does/not/exist", None).unwrap_err(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The proper (non-code) name of the library is "SNAFU".