💶 Formulaire sous Rust
Un exemple avec egui :
formulaire avec validation simple
⚙️ Dépendances (Cargo.toml) :
💻 Compilation / lancement :
Objectif :
Montrer, côté desktop, la même logique que les exercices PHP/PDO côté serveur : collecter des champs, les valider, afficher des messages d'erreur ciblés, puis "traiter" les données seulement si tout est correct, ici pas de base de données : on se contente d'afficher le résultat validé, mais la structure (validation -> erreurs -> traitement) est la même.
💻 Le code main.rs :
use eframe::egui;
// -----------------------------------------------------------------
// 1. État de l'application.
// - les champs bruts, tels que tapés par l'utilisateur (String)
// - un indicateur pour savoir si on a déjà tenté une validation
// (pour n'afficher les erreurs qu'après un premier essai,
// comme on le ferait en PHP après un $_POST)
// - le résultat, une fois validé (Option : rien tant que ce
// n'est pas valide)
// -----------------------------------------------------------------
struct MonFormulaire {
nom: String,
email: String,
age: String, // saisi en texte, converti/validé ensuite
tentative_soumission: bool,
resultat_valide: Option<Personne>,
}
// La donnée "propre", une fois la validation passée.
// Comparable à ce que vous obtiendriez après un filter_var() /
// intval() côté PHP.
#[derive(Debug)]
struct Personne {
nom: String,
email: String,
age: u8,
}
impl Default for MonFormulaire {
fn default() -> Self {
Self {
nom: String::new(),
email: String::new(),
age: String::new(),
tentative_soumission: false,
resultat_valide: None,
}
}
}
// -----------------------------------------------------------------
// 2. Fonctions de validation, séparées de l'affichage.
// Chacune renvoie soit Ok(valeur_propre), soit Err(message).
// C'est le même principe qu'une fonction PHP valider_email()
// qu'on appellerait avant l'INSERT en base.
// -----------------------------------------------------------------
fn valider_nom(nom: &str) -> Result<String, String> {
let nom = nom.trim();
if nom.is_empty() {
Err("Le nom ne peut pas être vide.".to_string())
} else if nom.chars().count() < 2 {
Err("Le nom doit contenir au moins 2 caractères.".to_string())
} else {
Ok(nom.to_string())
}
}
fn valider_email(email: &str) -> Result<String, String> {
let email = email.trim();
// Validation volontairement simple (pédagogique), pas une regex
// conforme RFC 5322 - on pourrait le mentionner en classe comme
// limite assumée, exactement comme pour vos exercices PHP.
if email.is_empty() {
Err("L'email ne peut pas être vide.".to_string())
} else if !email.contains('@') || !email.contains('.') {
Err("Format d'email invalide (attendu : nom@domaine.ext).".to_string())
} else {
Ok(email.to_string())
}
}
fn valider_age(age: &str) -> Result<u8, String> {
let age = age.trim();
if age.is_empty() {
Err("L'âge ne peut pas être vide.".to_string())
} else {
match age.parse::<u8>() {
Ok(valeur) if valeur >= 6 && valeur <= 120 => Ok(valeur),
Ok(_) => Err("L'âge doit être compris entre 6 et 120.".to_string()),
Err(_) => Err("L'âge doit être un nombre entier.".to_string()),
}
}
}
// -----------------------------------------------------------------
// 3. Le trait eframe::App (API depuis eframe 0.34 : méthode `ui`)
// -----------------------------------------------------------------
impl eframe::App for MonFormulaire {
fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) {
egui::CentralPanel::default().show(ui, |ui| {
ui.heading("Formulaire d'inscription");
ui.label("Tous les champs sont obligatoires.");
ui.separator();
// --- Champ Nom ---
ui.horizontal(|ui| {
ui.label("Nom :");
ui.text_edit_singleline(&mut self.nom);
});
// On n'affiche l'erreur qu'après une tentative de
// soumission, pour ne pas agresser l'utilisateur dès
// qu'il commence à taper.
if self.tentative_soumission {
if let Err(message) = valider_nom(&self.nom) {
ui.colored_label(egui::Color32::RED, message);
}
}
ui.add_space(8.0);
// --- Champ Email ---
ui.horizontal(|ui| {
ui.label("Email :");
ui.text_edit_singleline(&mut self.email);
});
if self.tentative_soumission {
if let Err(message) = valider_email(&self.email) {
ui.colored_label(egui::Color32::RED, message);
}
}
ui.add_space(8.0);
// --- Champ Âge ---
ui.horizontal(|ui| {
ui.label("Âge :");
ui.text_edit_singleline(&mut self.age);
});
if self.tentative_soumission {
if let Err(message) = valider_age(&self.age) {
ui.colored_label(egui::Color32::RED, message);
}
}
ui.add_space(16.0);
ui.separator();
// --- Bouton de soumission ---
if ui.button("Valider").clicked() {
self.tentative_soumission = true;
// On valide chaque champ. Si tous réussissent, on
// construit la Personne ; sinon resultat_valide
// reste à None et les messages d'erreur ci-dessus
// s'affichent au prochain rendu.
let nom = valider_nom(&self.nom);
let email = valider_email(&self.email);
let age = valider_age(&self.age);
match (nom, email, age) {
(Ok(nom), Ok(email), Ok(age)) => {
self.resultat_valide = Some(Personne { nom, email, age });
}
_ => {
self.resultat_valide = None;
}
}
}
// --- Résultat ---
if let Some(personne) = &self.resultat_valide {
ui.add_space(12.0);
ui.separator();
ui.colored_label(egui::Color32::GREEN, "Formulaire valide !");
ui.label(format!("Nom : {}", personne.nom));
ui.label(format!("Email : {}", personne.email));
ui.label(format!("Âge : {}", personne.age));
}
});
}
}
fn main() -> eframe::Result<()> {
let options = eframe::NativeOptions::default();
eframe::run_native(
"Formulaire egui",
options,
Box::new(|_cc| Ok(Box::new(MonFormulaire::default()))),
)
}