Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use StructOpt to parse an argument into a Vec without it being treated as multiple arguments?

Tags:

rust

structopt

I have this code:

#[derive(StructOpt)]
pub struct Opt {
    /// Data stream to send to the device
    #[structopt(help = "Data to send", parse(try_from_str = "parse_hex"))]
    data: Vec<u8>,
}

fn parse_hex(s: &str) -> Result<u8, ParseIntError> {
    u8::from_str_radix(s, 16)
}

This works for myexe AA BB, but I need to take myexe AABB as input.

Is there a way to pass a custom parser to structopt to parse AABB into a Vec<u8>? I need to parse only the second form (no space).

I know I can do it in 2 steps (storing into a String in the struct then parse it, but I like the idea that my Opt has the final type for everything.

I tried a parser like this:

fn parse_hex_string(s: &str) -> Result<Vec<u8>, ParseIntError>

The StructOpt macro panics about type mismatches because it seems to produce a Vec<Vec<u8>>.

like image 683
Geob-o-matic Avatar asked Apr 24 '18 15:04

Geob-o-matic


1 Answers

StructOpt makes the distinction that a Vec<T> will always map to multiple arguments:

Vec<T: FromStr>

list of options or the other positional arguments

.takes_value(true).multiple(true)

That means you need a single type to represent your data. Replace your Vec<u8> with a newtype:

#[derive(Debug)]
struct HexData(Vec<u8>);

#[derive(Debug, StructOpt)]
pub struct Opt {
    /// Data stream to send to the device
    #[structopt(help = "Data to send")]
    data: HexData,
}

This leads to the error:

error[E0277]: the trait bound `HexData: std::str::FromStr` is not satisfied
  --> src/main.rs:16:10
   |
16 | #[derive(StructOpt)]
   |          ^^^^^^^^^ the trait `std::str::FromStr` is not implemented for `HexData`
   |
   = note: required by `std::str::FromStr::from_str`

Let's implement FromStr:

impl FromStr for HexData {
    type Err = hex::FromHexError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        hex::decode(s).map(HexData)
    }
}

And it works:

$ cargo run -- DEADBEEF
HexData([222, 173, 190, 239])

$ cargo run -- ZZZZ
error: Invalid value for '<data>': Invalid character 'Z' at position 0
like image 120
Shepmaster Avatar answered Nov 18 '22 04:11

Shepmaster