Support my work ♥

parse_int with clap

When parsing integers from the CLI arguments with clap in a simple main.rs the standard way is to have a specific function for parsing hex values like that:

use clap::Parser;

#[derive(Parser, Debug)]
#[command(name = "parse-int-with-clap", version, author)]
struct Args {
    /// left hand of addition
    #[arg(short = 'a', value_parser = parse_hex_u8)]
    a: u8,
    /// right hand of addition
    #[arg(short = 'b', value_parser = parse_hex_16)]
    b: i16,
}

fn parse_hex_u8(s: &str) -> Result<u8, String> {
    let s = s.trim_start_matches("0x");
    u8::from_str_radix(s, 16).map_err(|e| e.to_string())
}

fn parse_hex_16(s: &str) -> Result<i16, String> {
    let s = s.trim_start_matches("0x");
    i16::from_str_radix(s, 16).map_err(|e| e.to_string())
}

fn main() {
    let args = Args::parse();
    println!("{:?}", args);
    let Args { a, b } = args;

    println!("{} + {} = {}", a, b, a as i16 + b);
    println!("0x{:X} + 0x{:X} = 0x{:X}", a, b, a as i16 + b);
}

This works, but now we have a subtle bug when a user just inputs a decimal like 10 that will be translated to 0x10, however the user did input 0xA.

Getting flexible with parse_int

With parse_int we can simplify that by using a specific variant of the lot:

#[derive(Parser, Debug)]
#[command(name = "parse-int-with-clap", version, author)]
struct Args {
    /// left hand of addition
    #[arg(short = 'a', value_parser = parse_int::parse::<u8>)]
    a: u8,
    /// right hand of addition
    #[arg(short = 'b', value_parser = parse_int::parse::<i16>)]
    b: i16,
}

This saves us from writing out a function per data-type.

In addition now we correctly parse all the input 🎉

links

social