Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make an argument optional based on the presence of another one in structopt?

Tags:

rust

structopt

I have a command line tool that has two possible arguments:

  • --version (which will print out the version number and quit)
  • --out (which is the path to some output file into which magic is going to be poured).

If a user passes --version I do not care about --out since I print the version and I am done but if they do not pass --version I want --out to be required.

This is what I have but I wonder if there's any way to do this using only structopt?

It seems like I may end up needing to make all my arguments optional and do all the validation myself...

#![feature(custom_attribute)]
#[macro_use]
extern crate structopt;

use structopt::StructOpt;
use std::path::PathBuf;

#[derive(Debug, StructOpt)]
#[structopt(name = "structopt_playing", about = "Just playing")]
struct Opt {
    #[structopt(short = "v", long = "version")]
    version: bool,

    #[structopt(short = "o", long = "out", parse(from_os_str))]
    output_file: Option<PathBuf>,
}

const VERSION: &'static str = env!("CARGO_PKG_VERSION");

fn main() {

    let opt = Opt::from_args();
    if opt.version {
        println!("Version: {}", VERSION);
        return;
    }

    if !opt.output_file.is_some() {
        println!("Oh, now I feel like I'm alone...you need to pass --out");
        return;
    }

    println!("Now I'm going to need to do something with {:?}", 
        opt.output_file.unwrap());
}
like image 818
kmp Avatar asked Apr 17 '18 12:04

kmp


2 Answers

Use required_unless:

#[derive(Debug, StructOpt)]
#[structopt(name = "structopt_playing", about = "Just playing")]
struct Opt {
    #[structopt(short = "v", long = "version")]
    version: bool,

    #[structopt(short = "o", long = "out", parse(from_os_str), required_unless = "version")]
    //                                                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^
    output_file: Option<PathBuf>,
}
$ ./target/debug/stropt
error: The following required arguments were not provided:
    --out <output_file>

USAGE:
    stropt --out <output_file>

For more information try --help

$ ./target/debug/stropt --out hello
Now I'm going to need to do something with "hello"

$ ./target/debug/stropt --version
Version: 0.1.0

There are a large number of related configuration options provided by Clap:

  • required_unless
  • required_unless_all
  • required_unless_one
  • conflicts_with
  • conflicts_with_all
  • requires
  • requires_if
  • requires_ifs
  • required_if
  • required_ifs
  • requires_all

A side note: you don't need to use #![feature(custom_attribute)] in this code at all.

like image 77
Shepmaster Avatar answered Oct 27 '22 21:10

Shepmaster


I would use subcommand:

#![feature(custom_attribute)]
#[macro_use] extern crate structopt;

use structopt::StructOpt;
use std::path::PathBuf;

#[derive(StructOpt)]
#[structopt(name = "test")]
enum Git {
    #[structopt(name = "--version")]
    Version,
    #[structopt(name = "--out")]
    Fetch(PathBuf),
}
like image 3
Boiethios Avatar answered Oct 27 '22 22:10

Boiethios