How to Use Enums in Rust CLI Applications with clap (Command & Option Mapping Guide)


Overview
While getting more comfortable with Rust, I decided to try writing a simple CLI.
I looked for a library that could elegantly handle commands and found clap.
This post is a quick note on how to map CLI commands and options to enums using clap
.
Base Cargo.toml
configuration:
[package]
name = "cli-with-clap"
version = "0.1.0"
edition = "2021"
[dependencies]
clap = { version = "4.5.23", features = ["derive"] }
Getting CLI Commands as Strings
Here’s a basic example where we receive a CLI command as a string:
use clap::Parser;
#[derive(Parser, Debug)]
struct Cli {
command: String,
}
fn main() {
let cli = Cli::parse();
println!("{:?}", cli);
}
cargo run hoge
Cli { command: "hoge" }
Adding Options like --hoge
Using #[arg()]
, you can add options to the command:
use clap::Parser;
#[derive(Parser, Debug)]
struct Cli {
command: String,
#[arg(short, long, default_value_t = String::from(""))]
mojiretu: String,
#[arg(long)]
maybe_mojiretu: Option<String>,
#[arg(short, long, default_value_t = 0)]
suuji: u32,
#[arg(short, long)]
flag: bool,
}
fn main() {
let cli = Cli::parse();
println!("{:?}", cli);
}
Mapping CLI Commands to Enums
To map commands to enums, use Subcommand
:
use clap::{Parser, Subcommand};
#[derive(Parser)]
struct Cli {
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
Add { task: String },
Remove { task: String },
List,
}
fn main() {
let cli = Cli::parse();
match cli.command {
Commands::Add { task } => println!("Add task: {}", task),
Commands::Remove { task } => println!("Remove task: {}", task),
Commands::List => println!("List tasks"),
}
}
cargo run list
List tasks
cargo run add hoge
Add task: hoge
Explicit Subcommand Name Mapping with command(name="")
use clap::{Parser, Subcommand};
#[derive(Parser, Debug)]
struct Cli {
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand, Debug)]
enum Commands {
#[command(name = "hogefuga")]
FooBar,
}
fn main() {
let cli = Cli::parse();
println!("{:?}", cli);
}
cargo run hogefuga
Cli { command: FooBar }
Change Subcommand Case Mapping with rename_all
By default, subcommand names use kebab-case. You can change this with rename_all
:
use clap::{Parser, Subcommand};
#[derive(Parser, Debug)]
struct Cli {
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand, Debug)]
#[command(rename_all = "snake_case")]
enum Commands {
FooBar,
}
fn main() {
let cli = Cli::parse();
println!("{:?}", cli);
}
cargo run foo_bar
Cli { command: FooBar }
Getting CLI Options as Enums
To map CLI options to enums, use ValueEnum
:
use clap::{Parser, ValueEnum};
#[derive(Parser, Debug)]
struct Cli {
#[arg(short, long)]
log_level: Level,
}
#[derive(Debug, Clone, ValueEnum)]
enum Level {
Debug,
Info,
Error,
}
fn main() {
let cli = Cli::parse();
println!("{:?}", cli);
}
cargo run -- --log-level info
Cli { log_level: Info }
Conclusion
Achieved everything without much hassle
clap
+derive
is powerfulEnum-based commands make Rust's pattern matching extremely ergonomic
This article is an English translation of the original post published on Zenn: https://zenn.dev/ara_ta3/articles/rust-cli-with-clap-bind-enum.
Subscribe to my newsletter
Read articles from ara-ta3 directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
