Dans cette dernière unité, vous allez assembler tous les concepts vus dans le cours pour construire un utilitaire système complet : un programme qui surveille le presse-papier, détecte les numéros de référence documentaire grâce aux regex, les reformate selon différents formats et peut être étendu avec une interface utilisateur. Vous découvrirez aussi l’organisation d’un projet Rust en modules, et comment choisir les bonnes crates pour compléter votre utilitaire.
Architecture de l’utilitaire
L’utilitaire se décompose en plusieurs responsabilités distinctes, chacune isolée dans son propre module :
ref-detector/
├── Cargo.toml
└── src/
├── main.rs # Point d'entrée, boucle événementielle
├── detecteur.rs # Logique de détection (regex)
├── formateur.rs # Logique de reformatage
└── clipboard.rs # Surveillance du presse-papier
[package]
name = "ref-detector"
version = "0.1.0"
edition = "2021"
[dependencies]
regex = "1"
once_cell = "1"
arboard = "3" # accès au presse-papier cross-platform
Le module détecteur
On centralise toute la logique de détection dans un module dédié. Les regex sont compilées une seule fois grâce à Lazy :
// src/detecteur.rs
use once_cell::sync::Lazy;
use regex::Regex;
static RE_REF: Lazy<Regex> = Lazy::new(|| {
Regex::new(r"(?P<prefixe>[A-Z]{2,6})-(?P<annee>d{4})-(?P<numero>d{3,6})").unwrap()
});
#[derive(Debug, Clone)]
pub struct Reference {
pub texte_original: String,
pub prefixe: String,
pub annee: u16,
pub numero: u32,
}
pub fn detecter_references(texte: &str) -> Vec<Reference> {
RE_REF.captures_iter(texte).map(|caps| Reference {
texte_original: caps[0].to_string(),
prefixe: caps["prefixe"].to_string(),
annee: caps["annee"].parse().unwrap_or(0),
numero: caps["numero"].parse().unwrap_or(0),
}).collect()
}
Le module formateur
On définit un enum pour les différents formats de sortie, permettant à l’utilisateur de cycler entre eux :
// src/formateur.rs
use crate::detecteur::Reference;
#[derive(Debug, Clone, PartialEq)]
pub enum FormatSortie {
Original, // DOC-2024-042
Crochet, // [DOC] N°042/2024
Url, // doc-2024-042
Humain, // Document 42 (2024)
}
impl FormatSortie {
pub fn suivant(&self) -> Self {
match self {
FormatSortie::Original => FormatSortie::Crochet,
FormatSortie::Crochet => FormatSortie::Url,
FormatSortie::Url => FormatSortie::Humain,
FormatSortie::Humain => FormatSortie::Original,
}
}
}
pub fn formater(r: &Reference, format: &FormatSortie) -> String {
match format {
FormatSortie::Original => r.texte_original.clone(),
FormatSortie::Crochet => format!("[{}] N°{:03}/{}", r.prefixe, r.numero, r.annee),
FormatSortie::Url => format!("{}-{}-{:03}", r.prefixe.to_lowercase(), r.annee, r.numero),
FormatSortie::Humain => format!("{} n°{} ({})", r.prefixe, r.numero, r.annee),
}
}
Assembler le tout dans main.rs
// src/main.rs
mod detecteur;
mod formateur;
use arboard::Clipboard;
use formateur::FormatSortie;
use std::time::Duration;
use std::thread;
fn main() {
let mut clipboard = Clipboard::new().expect("Impossible d'accéder au presse-papier");
let mut dernier_texte = String::new();
let mut format_actuel = FormatSortie::Original;
println!("Utilitaire de détection de références démarré...");
loop {
thread::sleep(Duration::from_millis(300));
let texte_courant = match clipboard.get_text() {
Ok(t) => t,
Err(_) => continue,
};
if texte_courant != dernier_texte {
dernier_texte = texte_courant.clone();
let references = detecteur::detecter_references(&texte_courant);
if !references.is_empty() {
println!("n=== {} référence(s) détectée(s) ===", references.len());
for r in &references {
let formate = formateur::formater(r, &format_actuel);
println!(" {} → {}", r.texte_original, formate);
}
}
}
}
}
Pistes d’extension
Votre utilitaire peut évoluer dans plusieurs directions : ajouter une interface graphique légère avec la crate egui ou iced, intégrer des raccourcis clavier globaux avec global-hotkey, écrire les références détectées dans un log fichier, ou même créer une API HTTP avec axum pour exposer les données à d’autres applications. Rust dispose d’un écosystème riche pour chaque besoin.
- Organisez votre code en modules (
mod nom;) pour séparer les responsabilités - Déclarez publics les éléments avec
pubpour les exposer hors du module - Utilisez des enums pour représenter des états discrets (formats, modes) et cyclez avec
match - L’écosystème Cargo (crates.io) offre des solutions pour chaque besoin système
📚 Formation : Rust : Développer des utilitaires système

-
Add a note