Achille 746
ACHILLE746
ExpertisesProcessusRésultatsPortfolioTechnologiesBlogFAQ
Lancer un projet
Accueil›Blog›Rust›Créer des outils CLI professionnels en Rust : clap, indicatif, distribution
🦀RUST

Créer des outils CLI professionnels en Rust : clap, indicatif, distribution

30 JUIN 2026•Par l'équipe Achille 746•9 min de lecture

Rust est devenu le langage de choix pour les outils CLI de nouvelle génération. ripgrep (rg) est 5 à 10x plus rapide que grep. fd remplace find avec une ergonomie moderne. bat remplace cat avec coloration syntaxique. delta améliore git diff. starshipest le prompt de shell le plus rapide disponible. Ce n'est pas un hasard : Rust combine la performance native, un binaire statique sans dépendances, un démarrage instantané et une gestion d'erreurs expressive. Ce guide couvre les outils et patterns pour construire des CLI professionnels — de l'argument parsing à la distribution.

Clap : la référence pour l'argument parsing

clap(Command Line Argument Parser) est la bibliothèque d'argument parsing standard de l'écosystème Rust. Sa version 4.x, avec l'API derive, permet de définir la structure de commandes entière via des annotations sur des structs — le code de parsing est généré automatiquement.

use clap::Parser;

#[derive(Parser)]
#[command(name = "myapp", about = "Mon outil CLI", version)]
struct Cli {
    /// Fichier d'entrée à traiter
    #[arg(short, long, value_name = "FILE")]
    input: PathBuf,

    /// Niveau de verbosité
    #[arg(short, long, action = clap::ArgAction::Count)]
    verbose: u8,

    #[command(subcommand)]
    command: Commands,
}

#[derive(Subcommand)]
enum Commands {
    /// Analyser le fichier
    Analyze { #[arg(long)] strict: bool },
    /// Générer un rapport
    Report { #[arg(short, long)] output: PathBuf },
}

L'API derive de clap génère automatiquement l'aide (--help), la validation des types, les sous-commandes et la complétion shell (bash, zsh, fish, PowerShell viaclap_complete). La doc-string Rust devient automatiquement la description de l'argument dans le --help. Résultat : un CLI professionnel avec validation, aide contextuelle et complétion shell en quelques dizaines de lignes.

Gestion d'erreurs : anyhow et thiserror

La gestion d'erreurs en CLI a deux faces : les erreurs internes (pour le développeur, avec du contexte) et les erreurs utilisateur (courtes, actionnables). anyhowest parfait pour la couche applicative des CLI : il permet d'additionner du contexte sur chaque erreur sans définir une hiérarchie de types complexe.

use anyhow::{Context, Result};

fn process_file(path: &Path) -> Result<()> {
    let content = std::fs::read_to_string(path)
        .with_context(|| format!("impossible de lire {:?}", path))?;
    // ...
    Ok(())
}

fn main() -> Result<()> {
    let cli = Cli::parse();
    process_file(&cli.input)
        .context("échec du traitement")?;
    Ok(())
}

La valeur de retour Result<()> de main avec anyhow affiche automatiquement la chaîne de contexte sur stderr avec Error: ...et un code de sortie 1. Pour les libraries CLI qui exposent leurs propres types d'erreur, thiserror est préférable — il génère des implémentations std::error::Error propres via derive. Pour les patterns avancés de gestion d'erreurs en Rust, ces deux crates se complètent.

indicatif : barres de progression et spinners

Pour les opérations longues, le feedback visuel est essentiel. indicatif est la bibliothèque de référence pour les barres de progression et spinners en Rust, avec un support natif des terminaux modernes (Unicode, couleurs, Unicode width).

use indicatif::{ProgressBar, ProgressStyle};

let pb = ProgressBar::new(total_items);
pb.set_style(ProgressStyle::default_bar()
    .template("{spinner:.green} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {pos}/{len} ({eta})")
    .unwrap());

for item in items {
    process(item);
    pb.inc(1);
}
pb.finish_with_message("terminé");

indicatifsupporte plusieurs barres simultanées (pour les opérations parallèles), les spinners pour les opérations de durée inconnue, et la suppression automatique quand la sortie n'est pas un TTY (utile pour les scripts CI). Son architecture thread-safe permet de l'utiliser avec rayon pour les opérations parallèles avec barre de progression globale.

console et dialoguer : couleurs et interactions

console expose une API simple pour les couleurs, les styles et la détection de terminal :style("Succès").green().bold(). Elle gère automatiquement la désactivation des couleurs quand NO_COLORest défini ou quand la sortie n'est pas un TTY, ce qui est le comportement attendu par les utilisateurs Unix.

dialoguer (construit sur console) fournit des prompts interactifs : confirmation (Confirm), sélection dans une liste (Select, MultiSelect), saisie texte (Input), saisie mot de passe (Password). Ces composants gèrent la navigation clavier, la validation et le rendu propre dans le terminal.

use dialoguer::{Confirm, Select};

let proceed = Confirm::new()
    .with_prompt("Supprimer les 42 fichiers sélectionnés ?")
    .default(false)
    .interact()?;

if proceed {
    let style = Select::new()
        .with_prompt("Format de sortie")
        .items(&["JSON", "CSV", "Tableau"])
        .default(0)
        .interact()?;
    // ...
}

Performance et distribution : le pipeline complet

L'un des avantages les plus sous-estimés de Rust pour les CLI est la distribution. Un binaire Rust compilé en release est statiquement lié (sur Linux avec musl), standalone, et fonctionne sans installation de runtime. L'ensemble de la chaîne :

  • Build release optimisé : cargo build --release avecopt-level = 3 et lto = true dans Cargo.toml. Le link-time optimization (LTO) réduit la taille du binaire de 20 à 40 % et améliore les performances inter-crate.
  • Strip des symboles : strip = truedans le profil release supprime les symboles de debug, réduisant la taille du binaire de 50 à 80 %. Un CLI Rust typique pèse entre 500 Ko et 3 Mo après strip, contre 30 à 100 Mo pour l'équivalent Go ou Electron.
  • Cross-compilation : avec cargo-cross ou les targetsx86_64-unknown-linux-musl, aarch64-apple-darwin,x86_64-pc-windows-gnu, vous produisez des binaires pour Linux, macOS (Intel/ARM) et Windows depuis un même poste.
  • Distribution : cargo install pour les développeurs, GitHub Releases pour les binaires pré-compilés (avec cargo-dist qui automatise le workflow), Homebrew tap pour macOS, packages .deb/.rpm avec cargo-deb / cargo-rpm.

La taille mémoire à l'exécution est également un avantage : un CLI Rust tient généralement dans 10 à 30 Mo de RAM (heap inclus), contre 50 à 200 Mo pour l'équivalent Python ou Node.js chargé avec ses dépendances. Pour les environnements containerisés, cette efficacité se traduit directement en densité plus élevée et coûts inférieurs.

Async dans les CLI : quand et comment

Tous les CLIs n'ont pas besoin d'async. Pour les outils qui traitent des fichiers locaux ou font peu d'I/O réseau, le code synchrone est plus simple et suffisamment rapide. L'async devient pertinent quand le CLI doit effectuer plusieurs opérations réseau concurrentes — télécharger plusieurs fichiers en parallèle, interroger plusieurs APIs simultanément, ou maintenir une connexion WebSocket longue durée. Dans ce cas, ajouter Tokio et reqwest(le client HTTP async de référence) reste cohérent avec l'approche décrite dans notre article sur l'async Rust avec Tokio.

“Le meilleur CLI est celui qu'on oublie — parce qu'il démarre instantanément, ne plante jamais, et fait exactement ce qui est demandé sans surprise. C'est la promesse que Rust tient mieux que n'importe quel autre langage.”

Construire un CLI Rust de qualité production demande peu de code et beaucoup de soin : une interface cohérente, des messages d'erreur actionnables, un feedback visuel adapté et une distribution sans friction pour l'utilisateur final. Ces détails font la différence entre un outil qu'on installe une fois et qu'on oublie, et un outil qu'on recommande. Pour développer des outils CLI Rust sur mesure adaptés à vos workflows internes ou à vos utilisateurs, nous intervenons de la conception à la distribution multi-plateforme.

Article précédentRust async/await et Tokio : comprendre le runtime en profondeurTous les articlesArticle suivantIA pour les entreprises de Nouvelle-Aquitaine : cas d'usage et retours terrain
Partager cet article :Twitter / XLinkedIn

Articles liés

🚀
Pourquoi les entreprises adoptent Rust en 2026
6 min de lecture
→
⚡
Axum et Tokio : construire une API REST haute performance en Rust
10 min de lecture
→
🦀
La gestion d'erreurs en Rust : Result, ? operator et patterns de production
8 min de lecture
→
⚡
Rust async/await et Tokio : comprendre le runtime en profondeur
10 min de lecture
→

Vous avez besoin d'outils CLI Rust sur mesure ?

Nous développons des outils CLI Rust production-ready — argument parsing, progression, cross-compilation et distribution multi-plateforme sans friction.

Discuter de votre projet →