Introduction
Rust est devenu le langage de référence pour les outils en ligne de commande grâce à sa sécurité mémoire et ses performances. Dans ce tutoriel intermédiaire, nous allons construire une CLI qui analyse des fichiers texte et génère des statistiques. Vous apprendrez à structurer un projet, gérer les arguments avec clap, implémenter une logique métier robuste et écrire des tests. Ce guide vous permettra de produire du code prêt pour la production.
Prérequis
- Rust 1.75+ et Cargo installés
- Connaissances de base en ownership et traits
- Terminal et éditeur de code (VS Code recommandé)
Initialisation du projet
cargo new text-stats --bin
cd text-stats
cargo add clap --features deriveNous initialisons un projet binaire et ajoutons clap pour le parsing d'arguments. Cela pose les fondations d'une CLI structurée et maintenable.
Définition des arguments
use clap::Parser;
#[derive(Parser)]
#[command(name = "text-stats")]
struct Args {
#[arg(short, long)]
file: String,
}
fn main() {
let args = Args::parse();
println!("Analyse du fichier : {}", args.file);
}Le derive macro de clap génère automatiquement le parsing. Cela évite le code boilerplate et rend les arguments type-safe.
Lecture et analyse du fichier
use std::fs;
pub fn analyze_file(path: &str) -> Result<(usize, usize), Box<dyn std::error::Error>> {
let content = fs::read_to_string(path)?;
let word_count = content.split_whitespace().count();
let char_count = content.chars().count();
Ok((word_count, char_count))
}Cette fonction lit le fichier et calcule les statistiques. L'utilisation de Result permet une gestion propre des erreurs d'E/S.
Intégration dans main
use clap::Parser;
mod stats;
#[derive(Parser)]
#[command(name = "text-stats")]
struct Args {
#[arg(short, long)]
file: String,
}
fn main() {
let args = Args::parse();
match stats::analyze_file(&args.file) {
Ok((words, chars)) => println!("Mots: {}, Caractères: {}", words, chars),
Err(e) => eprintln!("Erreur: {}", e),
}
}Nous connectons le parsing des arguments à la logique d'analyse. Le pattern match garantit que toutes les erreurs sont traitées explicitement.
Ajout des tests unitaires
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_analyze_empty() {
let result = analyze_file("tests/empty.txt");
assert!(result.is_ok());
}
}Les tests sont placés dans le même fichier pour une meilleure lisibilité. Ils vérifient le comportement sur des cas limites comme un fichier vide.
Bonnes pratiques
- Utilisez toujours Result pour les opérations faillibles
- Structurez votre code en modules dès que possible
- Ajoutez des tests pour chaque fonction publique
- Préférez les erreurs custom avec thiserror pour les projets plus gros
Erreurs courantes à éviter
- Ignorer les erreurs avec unwrap() en production
- Oublier de déclarer les modules avec mod
- Ne pas gérer les cas de fichiers inexistants
- Utiliser des chaînes brutes au lieu de types forts pour les chemins
Pour aller plus loin
Découvrez nos formations avancées sur Rust et les architectures logicielles : https://learni-group.com/formations