itasahobby
itasahobby

Reputation: 313

How to create nested subcommands with structopt?

Introduction

I currently have subcommands working using structopt, similarly to this other answer. However I would like to have nested subcommands similarly of how docker works:

docker image ls
docker image pull

Current Code

main.rs

mod cli;

use structopt::StructOpt;
use crate::cli::{Opt, Command};

fn main() {

    let opt = Opt::from_args();
    match opt.cmd {
        Command::Subcommand { .. } => {
            // Do something
        }
    }

}

cli/mod.rs

use structopt::StructOpt;

#[derive(StructOpt, Debug)]
pub enum Command {
    Subcommand {
       // args
    }
}

#[derive(StructOpt, Debug)]
pub struct Opt {
    #[structopt(subcommand)]
    pub cmd: Command,
}

What I have tried

Added another struct: main.rs

mod cli;

use structopt::StructOpt;
use crate::cli::{Opt, Resource};

fn main() {

    let opt = Opt::from_args();
    match opt.resource {
        Resource::Command { .. } => {
            // do something
        }
    }

}

cli/mod.rs

use structopt::StructOpt;

#[derive(StructOpt, Debug)]
pub enum Command {
    Subcommand {
        // args
    }
}

#[derive(StructOpt, Debug)]
pub struct Resource {
    #[structopt(subcommand)]
    image: Image
}

#[derive(StructOpt, Debug)]
pub struct Opt {
    #[structopt(subcommand)]
    pub resource: Resource,
}

However I get the following error:

help: use fully-qualified syntax: `<Resource as Trait>::ImageCommand

Upvotes: 2

Views: 502

Answers (1)

battlmonstr
battlmonstr

Reputation: 6300

Since command is described as an enum, a subcommand would be a "sub-enum" (an enum inside an enum variant). In the example below ImageCommand is an enum inside a Command enum variant:

use structopt::StructOpt;

#[derive(StructOpt, Debug)]
pub struct ImagePullOptions {
    pub name: String,
}

#[derive(StructOpt, Debug)]
pub enum ImageCommand {
    #[structopt(name = "ls")]
    List,
    Pull(ImagePullOptions),
}

#[derive(StructOpt, Debug)]
pub enum Command {
    Image(ImageCommand),
}

#[derive(StructOpt, Debug)]
pub struct Options {
    #[structopt(subcommand)]
    pub command: Command,
}

fn main() {
    let options: Options = Options::from_args();
    match options.command {
        Command::Image(image_command) => {
            match image_command {
                ImageCommand::List => println!("listing..."),
                ImageCommand::Pull(pull_options) => println!("pulling {}...", pull_options.name),
            }
        }
    }
}

Test with:

$ cargo run -- image 

USAGE:
    testsubopt image <SUBCOMMAND>

FLAGS:
    -h, --help       Prints help information
    -V, --version    Prints version information

SUBCOMMANDS:
    help    Prints this message or the help of the given subcommand(s)
    ls      
    pull    
$ cargo run -- image pull asd
pulling asd...

Upvotes: 3

Related Questions