clap es la librería de parsing de argumentos más usada en el ecosistema Rust. Su Derive API permite definir toda la interfaz de línea de comandos como una struct anotada: clap genera el parser, la ayuda con --help, los mensajes de error y la validación de tipos sin que escribas una sola línea de lógica de parsing.
Instalación
// Cargo.toml
[dependencies]
clap = { version = "4", features = ["derive"] }
CLI básica con argumentos y opciones
use clap::Parser;
use std::path::PathBuf;
/// Herramienta de procesamiento de ficheros
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Cli {
/// Fichero de entrada (obligatorio)
fichero: PathBuf,
/// Número de líneas a mostrar
#[arg(short = 'n', long, default_value_t = 10)]
lineas: usize,
/// Modo verbose
#[arg(short, long)]
verbose: bool,
/// Patrones a excluir (se puede repetir: -x foo -x bar)
#[arg(short = 'x', long = "excluir")]
excluir: Vec<String>,
}
fn main() {
let cli = Cli::parse();
println!("Fichero: {:?}", cli.fichero);
println!("Líneas: {}", cli.lineas);
println!("Verbose: {}", cli.verbose);
println!("Excluir: {:?}", cli.excluir);
if cli.verbose {
println!("[DEBUG] procesando...");
}
}
Subcomandos con enum
use clap::{Parser, Subcommand};
use std::path::PathBuf;
#[derive(Parser)]
#[command(author, version, about)]
struct Cli {
/// Nivel de detalle (0-3)
#[arg(short, long, default_value_t = 0)]
verbosity: u8,
#[command(subcommand)]
comando: Comando,
}
#[derive(Subcommand)]
enum Comando {
/// Añadir un elemento
Add {
nombre: String,
#[arg(short, long)]
etiquetas: Vec<String>,
},
/// Listar elementos
List {
#[arg(short, long)]
todos: bool,
#[arg(short, long)]
formato: Option<String>,
},
/// Eliminar por ID
Remove {
/// IDs a eliminar
ids: Vec<u32>,
/// No pedir confirmación
#[arg(short, long)]
forzar: bool,
},
}
fn main() {
let cli = Cli::parse();
match cli.comando {
Comando::Add { nombre, etiquetas } => {
println!("Añadiendo '{nombre}' con etiquetas: {etiquetas:?}");
}
Comando::List { todos, formato } => {
let fmt = formato.unwrap_or_else(|| "tabla".into());
println!("Listando (todos={todos}, formato={fmt})");
}
Comando::Remove { ids, forzar } => {
if !forzar {
println!("¿Eliminar {ids:?}? (usa --forzar para confirmar)");
} else {
println!("Eliminando: {ids:?}");
}
}
}
}
Validación de tipos automática
use clap::Parser;
use std::net::SocketAddr;
#[derive(Parser)]
struct Servidor {
/// Dirección de escucha
#[arg(short, long, default_value = "127.0.0.1:8080")]
direccion: SocketAddr,
/// Número de workers (1-16)
#[arg(short, long, default_value_t = 4, value_parser = clap::value_parser!(u8).range(1..=16))]
workers: u8,
/// Fichero de configuración
#[arg(short, long, value_name = "RUTA")]
config: Option<std::path::PathBuf>,
}
fn main() {
let srv = Servidor::parse();
println!("Escuchando en: {}", srv.direccion);
println!("Workers: {}", srv.workers);
}
Variables de entorno como fuente alternativa
use clap::Parser;
#[derive(Parser)]
struct Config {
/// Token de API (o variable APP_TOKEN)
#[arg(long, env = "APP_TOKEN")]
token: String,
/// URL de la base de datos (o variable DATABASE_URL)
#[arg(long, env = "DATABASE_URL", default_value = "postgres://localhost/dev")]
database_url: String,
}
fn main() {
// Primero mira el argumento --token; si no, usa APP_TOKEN del entorno
let config = Config::parse();
println!("Token: {}...", &config.token[..4.min(config.token.len())]);
}
Resumen
- Deriva
Parseren una struct para definir toda la CLI; clap genera el código de parsing. - Los campos sin
#[arg(...)]son argumentos posicionales; conshort/longson opciones. Vec<T>acepta el argumento múltiples veces;Option<T>hace el argumento opcional.- Los subcomandos se modelan como un
enumderivandoSubcommand. - clap valida el tipo automáticamente:
SocketAddr,PathBuf, rangos numéricos, etc. env = "VAR"acepta variables de entorno como alternativa a los argumentos.
