murarth / gumdrop

Rust option parser with custom derive support

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Add validator support

RazrFalcon opened this issue · comments

Like:

fn my_func(value: &str) -> Option<u32> {} // or Result

#[options(help = "Sets the target DPI", meta = "DPI", default = "96", validate=my_func)]
dpi: Option<u32>,

The code generated by gumdrop uses the standard FromStr trait to generate field values. You can implement this on a custom type to get the functionality you want (in conjunction with a manual Default implementation as outlined in #12):

#[derive(Default, Options)]
struct Options {
    dpi: Dpi,

    // ...
}

struct Dpi(u32);

impl FromStr for Dpi {
    // This could be a custom error type, but I'm being terse here.
    type Err = &'static str;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let n = s.parse().map_err(|_| "invalid number")?;

        if is_valid_dpi(n) {
            Ok(Dpi(n))
        } else {
            Err("invalid dpi value")
        }
    }
}

Hm... It's an interesting idea, but I think that it's missed from docs.

The main gumdrop documentation does mention FromStr, but only once, in passing. So, that's fair.

An example with a custom FromStr implementation could be used to highlight this capability.

I still think that a function pointer will lead to a smaller and cleaner code, but it's fine for now.

You can close it.

The meaning of a named function in an option attribute is not obvious, though. If you're visually scanning over the code and you see it, what do you expect the signature of the function to be? fn(T) -> Result<T, Error>? fn(&T) -> Result<(), Error>? fn(&str) -> Result<T, Error>? None of these are intuitively the obvious choice. However, FromStr is a standard trait and makes argument value parsing an intuitively obvious one-step process.

And again, structopt doesn't think that this a problem.

https://docs.rs/structopt/0.2.10/structopt/#custom-string-parsers

Allowing different types of parsing functions seems like a reasonable solution. I'll see what I can do.

I've added an implementation for custom parsers using the parse(...) attribute, similar to structopt. Let me know if it works as expected.

Excellent! But it still requires FromStr for a type. I'm using:

parse(try_from_str = "parse_indent")
// and
fn parse_indent(s: &str) -> Result<svgdom::Indent, &'static str> {}

The test code shows an example of parsing types that do not implement FromStr: https://github.com/murarth/gumdrop/blob/master/tests/options.rs#L853-L891

You mean fn parse_bar(s: &str) -> Result<Bar, <u32 as FromStr>::Err> { s.parse().map(Bar) }? That's what I'm using.

Do'h! That was my mistake. I've set parse only for one type, not both.

Thanks a lot! It works as expected.