Pourquoi Rust est-il le choix idéal pour les outils CLI ?
\n\nAvez-vous déjà réfléchi à la raison pour laquelle tous les outils DevOps modernes — de bat à ripgrep et fd — sont écrits en Rust ? Il ne s'agit pas seulement d'une mode. Les utilitaires CLI en Rust sont incroyablement rapides, sécurisés et, ce qui est le plus agréable, ils peuvent être compilés en un seul petit binaire sans dépendances. Plus de "est-ce que Java est installé ?" ou "attendez que Node.js télécharge node_modules". Il suffit de télécharger — et c'est prêt.
Dans cet article, nous allons créer un utilitaire CLI en Rust à partir de zéro. Ce ne sera pas un abstrait "Hello, World!", mais un outil réellement utile — un analyseur de logs qui recherche les erreurs, compte leur nombre et affiche les résultats dans un beau format coloré. Et oui, nous le ferons en une soirée.
\n\nDe quoi avons-nous besoin ?
\n\nPour commencer, il faut un minimum :
\n- \n
- Rust (installez-le via
rustupsi ce n'est pas déjà fait) \n - N'importe quel éditeur de code (VS Code avec le plugin rust-analyzer est idéal) \n
- Quelques heures de temps libre \n
Si vous avez déjà programmé en Python ou Go, Rust vous semblera inhabituel mais logique. L'essentiel est de ne pas avoir peur du compilateur. Il n'est pas méchant, il est simplement attentionné.
\n\nÉtape 1 : Création du projet
\n\nOuvrez le terminal et tapez :
\ncargo new log-scanner\ncd log-scanner\n\nCargo est notre couteau suisse. Il a créé une structure de base avec Cargo.toml (fichier de dépendances) et src/main.rs (point d'entrée). Vous pouvez déjà exécuter cargo run et voir "Hello, world!". Mais nous allons plus loin.
Ouvrez Cargo.toml et ajoutez les dépendances qui transformeront notre code en un véritable utilitaire CLI en Rust :
[dependencies]\nclap = { version = "4.5", features = ["derive"] }\ncolored = "2.1"\nanyhow = "1.0"\n\n- \n
- clap — analyse des arguments de la ligne de commande (indispensable) \n
- colored — sortie colorée dans le terminal \n
- anyhow — gestion pratique des erreurs \n
Étape 2 : Définition de la structure des arguments
\n\nCe qu'il y a de plus génial avec clap, ce sont les macros derive. Vous décrivez simplement la structure, et la magie opère. Voici à quoi cela ressemble :
use clap::Parser;\n\n#[derive(Parser)]\n#[command(name = "log-scanner")]\n#[command(about = "Scanne les fichiers de log et recherche les erreurs")]\nstruct Cli {\n /// Chemin vers le fichier de log\n file: String,\n\n /// Chaîne à rechercher (par défaut "ERROR")\n #[arg(short, long, default_value = "ERROR")]\n pattern: String,\n\n /// Afficher uniquement le nombre de lignes trouvées\n #[arg(short, long)]\n count: bool,\n}\n\nRemarquez : les commentaires avec trois barres obliques /// deviennent automatiquement des indications dans --help. Exécutez cargo run -- --help — et vous verrez une aide soigneusement générée. C'est l'une des raisons pour lesquelles la création d'outils CLI en Rust est si agréable : la documentation s'écrit d'elle-même.
Étape 3 : Écriture de la logique de recherche
\n\nMaintenant, la partie la plus intéressante — la lecture du fichier et la recherche de correspondances. Rust est réputé pour sa gestion sécurisée des fichiers et de la mémoire, mais en pratique, c'est simplement pratique :
\n\nuse 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