Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using a structure as a command line argument in clap

Tags:

rust

clap

Trying to use a struct within a struct in clap:

use clap::{Args, Parser};
use std::path::PathBuf;

#[derive(Parser, Debug)]
enum Command {
    Foo(Foo),
}

#[derive(Args, Debug)]
struct Foo {
    bar: Option<Bar>,

    path: PathBuf,
}

#[derive(Parser, Clone, Debug)]
struct Bar {
    bla: u8,

    bla_2: String,
}

fn main() {
    let cli = Command::parse();
    println!("cli {:#?}", cli);
}

So I could call the app with the following options: cargo run -- foo bar 42 baz /tmp/a or just cargo run -- foo /tmp/a since the bar argument is optional.

However, currently it does not build:

  --> src/main.rs:11:5
   |
11 |     bar: Option<Bar>,
   |     ^^^ the trait `FromStr` is not implemented for `Bar`
   |

And since the values within Bar have to be space-separated implementing a FromStr would not do the trick anyway.

Is it even possible to do something of this fashion in clap currently?

like image 514
montekki Avatar asked Jun 02 '26 20:06

montekki


1 Answers

There are several problems with your code. The biggest one is:

  • An optional positional item can never come before a required positional argument

This is a problem in your case because your command line looks like this:

cargo run -- <required> [optional] /tmp/a

If you have a required path at the end, there can not be an optional positional argument before that.

Further problems:

  • #[derive(Parser)] should be attached to a struct, not an enum.
  • There should only be one #[derive(Parser)], which represents the entry object of your arguments parser.

I'm unsure how else to help you, except pointing out your problems. If the invocations cargo run -- foo bar 42 baz /tmp/a and cargo run -- foo /tmp/a are non-negotiable, I don't think clap is the right library for you; I think you should parse by hand.

like image 69
Finomnis Avatar answered Jun 04 '26 10:06

Finomnis