Arnavion / derive-error-chain

A Macros 1.1 implementation of https://crates.io/crates/error-chain
19 stars 1 forks source link

Add `const("some string literal with {parameters}")` as a shorthand for `display()` #7

Closed Arnavion closed 6 years ago

Arnavion commented 7 years ago

As a workaround, one can use a closure instead of a function path:

#[derive(Debug, error_chain)]
pub enum ErrorKind {
    Msg(String),

    #[error_chain(custom, display = "http_status_display")]
    HttpStatus(u32),
}

fn http_status_display(f: &mut ::std::fmt::Formatter, e: &u32) -> ::std::fmt::Result {
    write!(f, "http request returned an unsuccessful status code: {}", e)
}

can be written as

#[derive(Debug, error_chain)]
pub enum ErrorKind {
    Msg(String),

    #[error_chain(custom)]
    #[error_chain(display = r#"(|f: &mut ::std::fmt::Formatter, e| write!(f, "http request returned an unsuccessful status code: {}", e))"#)]
    HttpStatus(u32),
}
Arnavion commented 7 years ago

Will be possible with upcoming #![feature(proc_macro)] enhancements (syn_0_12 branch).

Arnavion commented 7 years ago

The format string for tuple variants should use positional parameters:

#[error_chain(custom, display = const("http request returned an unsuccessful status code: {0}"))]
HttpStatus(u32),

// Expands to `write!(f, "http request returned an unsuccessful status code: {0}", field0)`

... and named parameters for struct variants:

#[error_chain(custom, display = const("http request returned an unsuccessful status code: {code}"))]
HttpStatus { code: u32 },

// Expands to `write!(f, "http request returned an unsuccessful status code: {code}", code=code)`

The generated code will pass all tuple members / struct fields to write!. Since write! doesn't allow unused parameters, the string will need to ignore unused parameters explicitly, eg

#[error_chain(custom, display = const("http request returned an unsuccessful status code: {0}{1:.0}"))]
HttpStatus(u32, String),
#[error_chain(custom, display = const("http request returned an unsuccessful status code: {code}{some_other_field:.0}"))]
HttpStatus { code: u32, some_other_field: String },

Ref: https://stackoverflow.com/questions/41841400/allow-unused-named-arguments-in-rusts-format-family

Edit: The zero-with trick doesn't work with numeric types. Possibly use syntex_fmt_macros to parse the string instead.

Arnavion commented 7 years ago

This is implemented in the syn_0_12 branch, but the requirement of #[feature(proc_macro)] and syn v0.12 means this is probably not going to be in v0.11.0

Arnavion commented 6 years ago

That branch implements:

#[error_chain(display = const("Custom's display: {code}"))]
Custom { code: u32, },

failure has an interesting alternative:

#[fail(display = "Custom's display: {}", code)]
Custom { code: u32, },

The upside of failure's syntax is that it works on stable, ie without #![feature(proc_macro)]

The downside is that derive-error-chain's way is more flexible, since the user explicitly opts in to the format string behavior with const() and can use a lambda / dedicated function expression otherwise. At any rate #[error_chain(display = "foo")] would be ambiguous since it currently indicates that foo is a function that should be called with the variant fields as parameters.

It's also not clear to me whether failure supports tuple variants with that syntax.