clap en Rust: crear CLIs con argumentos, opciones y subcomandos usando Derive API

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 Parser en una struct para definir toda la CLI; clap genera el código de parsing.
  • Los campos sin #[arg(...)] son argumentos posicionales; con short/long son opciones.
  • Vec<T> acepta el argumento múltiples veces; Option<T> hace el argumento opcional.
  • Los subcomandos se modelan como un enum derivando Subcommand.
  • clap valida el tipo automáticamente: SocketAddr, PathBuf, rangos numéricos, etc.
  • env = "VAR" acepta variables de entorno como alternativa a los argumentos.

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP