Utilidades CLI en Rust: cómo escribir tu primera herramienta en una tarde

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

¿Por qué Rust es la elección ideal para herramientas CLI?

\n\n

¿Alguna vez te has preguntado por qué todas las herramientas DevOps modernas, desde bat hasta ripgrep y fd, están escritas en Rust? No es solo una cuestión de moda. Las utilidades CLI en Rust resultan increíblemente rápidas, seguras y, lo mejor de todo, se pueden compilar en un único binario diminuto sin dependencias. Nada de "¿tienes Java instalado?" o "espera a que Node.js descargue node_modules". Simplemente lo descargas y listo.

\n\n

En este artículo crearemos una utilidad CLI en Rust desde cero. No será un abstracto "¡Hola, Mundo!", sino una herramienta real y útil: un analizador de logs que busca errores, cuenta su cantidad y muestra los resultados en un bonito formato a color. Y sí, lo haremos en una tarde.

\n\n

¿Qué necesitamos?

\n\n

Para empezar necesitamos lo mínimo:

\n
    \n
  • Rust (instálalo a través de rustup si aún no lo tienes)
  • \n
  • Cualquier editor de código (VS Code con el plugin rust-analyzer es ideal)
  • \n
  • Un par de horas de tiempo libre
  • \n
\n\n

Si ya has escrito en Python o Go, Rust te parecerá inusual pero lógico. Lo principal es no tener miedo al compilador. No es malo, solo es cuidadoso.

\n\n

Paso 1: Creamos el proyecto

\n\n

Abrimos la terminal y escribimos:

\n
cargo new log-scanner\ncd log-scanner
\n\n

Cargo es nuestra navaja suiza. Ha creado la estructura básica con Cargo.toml (archivo de dependencias) y src/main.rs (punto de entrada). Ya puedes ejecutar cargo run y ver "Hello, world!". Pero iremos más allá.

\n\n

Abrimos Cargo.toml y añadimos las dependencias que convertirán nuestro código en una auténtica utilidad CLI en Rust:

\n
[dependencies]\nclap = { version = "4.5", features = ["derive"] }\ncolored = "2.1"\nanyhow = "1.0"
\n\n
    \n
  • clap — análisis de argumentos de línea de comandos (imprescindible)
  • \n
  • colored — salida a color en la terminal
  • \n
  • anyhow — manejo cómodo de errores
  • \n
\n\n

Paso 2: Definimos la estructura de argumentos

\n\n

Lo más genial de clap son las macros derive. Simplemente describes la estructura y la magia ocurre sola. Así es como se ve:

\n\n
use clap::Parser;\n\n#[derive(Parser)]\n#[command(name = "log-scanner")]\n#[command(about = "Escanea archivos de log y busca errores")]\nstruct Cli {\n    /// Ruta al archivo de log\n    file: String,\n\n    /// Cadena a buscar (por defecto "ERROR")\n    #[arg(short, long, default_value = "ERROR")]\n    pattern: String,\n\n    /// Mostrar solo la cantidad de líneas encontradas\n    #[arg(short, long)]\n    count: bool,\n}
\n\n

Observa: los comentarios con tres barras /// se convierten automáticamente en sugerencias en --help. Ejecuta cargo run -- --help y verás una ayuda generada ordenadamente. Esta es una de las razones por las que crear CLI en Rust es tan agradable: la documentación se escribe sola.

\n\n

Paso 3: Escribimos la lógica de búsqueda

\n\n

Ahora viene lo interesante: leer el archivo y buscar coincidencias. Rust es famoso por su trabajo seguro con archivos y memoria, pero en la práctica es simplemente cómodo:

\n\n
use std::fs::File;\nuse std::io::{BufRead, BufReader};\nuse colored::*;\nuse anyhow::Result;\n\nfn scan_log(file_path: &str, pattern: &str, count_only: bool) -> Result<()> {\n    let file = File::open(file_path)?;\n    let reader = BufReader::new(file);\n    let mut total_matches = 0;\n\n    for (line_num, line) in reader.lines().enumerate() {\n        let line = line?;\n        if line.contains(pattern) {\n            total_matches += 1;\n            if !count_only {\n                println!(\n                    "{}:{} {}",\n                    format!("{:>4}", line_num + 1).blue(),\n                    patt                

Blogs

Book Recommendations