AGENTS.md — rss-podcast-dl-egui
Application desktop Rust/egui (egui + eframe 0.35) pour télécharger des podcasts depuis des flux RSS. MSRV 1.92.
Commandes
cargo build # build debug
cargo run # lance l'app
cargo test # 98 tests (modèles, storage, parsing, downloader, extraction, UI)
cargo clippy --all-targets # doit passer sans aucun avertissement
Attention : ne pas lancer cargo fmt sur l'ensemble du dépôt — il n'est pas formaté à rustfmt et un fmt global crée un diff de plusieurs centaines de lignes. Suivre le style du fichier voisin ; formater uniquement les fichiers nouveaux ou entièrement réécrits (rustfmt --edition 2021 <fichier>).
Clippy sans warning est exigé : tout nouveau warning doit être corrigé, pas masqué par #[allow] (les seuls #![allow(dead_code)] existants marquent des fonctionnalités prévues par la spec mais pas encore branchées dans l'UI).
Architecture
src/
├── main.rs / app.rs # point d'entrée ; RssPodcastApp (impl eframe::App), chargement initial
├── config/ # modèles + persistance (séparés de l'UI)
│ ├── app_state.rs # AppState : tout l'état mutable de l'app (champs pub)
│ ├── feed.rs / episode.rs / playlist.rs # modèles serde (IDs = UUID v4)
│ ├── storage.rs # save/load JSON dans %APPDATA%/rss-podcast-dl (unix : ~/.config/rss-podcast-dl)
│ ├── settings.rs # settings.json (thème), history.json
│ ├── history.rs # historique des opérations, auto-persisté
│ └── sync_task.rs # SyncTaskManager : threads + mpsc, statut + progression par flux
├── rss/ # I/O réseau et fichiers
│ ├── parser.rs / episode_extractor.rs # récupération + parsing du flux (crate rss)
│ ├── async_sync.rs # synchro asynchrone : émet SyncProgress épisode par épisode
│ ├── downloader.rs # téléchargement atomique : .part puis rename, via reqwest blocking
│ ├── transcode.rs # réencodage .m4a -> .mp3 via ffmpeg (fallback : original conservé)
│ └── audio_metadata.rs # tags ID3 (crate id3)
└── ui/ # rendu pur : fonctions draw_*(state: &mut AppState, ui)
├── feed_list.rs # liste des flux ; barre de progression de synchro par ligne
└── sync_status.rs # indicateur global + draw_progress_bar (réutilisé par feed_list)
Patterns à respecter
- State centralisé : tout état mutable va dans
AppState(struct plat, champspub). Les composants UI sont des fonctions libresdraw_...(&mut AppState, &mut egui::Ui)— pas de widgets à état interne. - Jamais de blocage du thread UI : les opérations réseau/downloads tournent dans des threads détachés et communiquent via
mpsc(SyncMessagedanssync_task.rs). Les messages sont drainés partry_recvau fil des frames ; on ne fait pas derecv()bloquant dans la boucle egui. - Progression de synchro par flux :
async_sync.rsémetSyncMessage::SyncProgress(0.0 → 1.0) au fil des épisodes ;SyncStatus.feed_sync_progressstocke la progression courante.feed_list.rsaffiche une barre sur la ligne de chaque flux concerné. En synchro globale,start_global_syncmarque tous les fluxPending(barre visible sur toutes les lignes) ;complete_global_syncvidefeed_sync_progresspour faire disparaître les barres simultanément. - Téléchargements atomiques : toujours écrire via un fichier
.partpuis renommer. Ne jamais écrire directement le fichier final. - Persistance : sauvegarder le JSON après chaque mutation de feeds/playlists (
storage::save_*_default). Historique auto-persisté viaHistoryManager::set_save_path. - Couleurs : uniquement via la palette
ui::theme::ThemeColors(apply_custom_theme). Pas de couleur en dur dans les composants — les deux thèmes (sombre/clair) doivent rester cohérents. - Feedback utilisateur : erreurs/succès via
NotificationManager(notifications flottantes), jamais depanic!nieprintln!pour une erreur runtime ; tout logging passe par la cratelog. - Erreurs : types dédiés via
thiserror(ex.DownloadError,StorageError), pas deStringnues pour les erreurs internes. - Modèles serde : tout champ sérialisé doit être
Serialize + Deserializeavec#[serde(default)]quand l'ancien format ne l'avait pas — rester tolérant aux configs existantes. - Suppressions : toujours passer par un dialogue de confirmation (
confirm_dialog.rs), jamais en direct.
Conventions de code
- Édition 2021, Rust 2021 idiomatique ; commentaires de doc
///et en-têtes de module//!obligatoires. - Docs/UI en français (README, textes), identifiants et doc-comments en anglais — suivre le fichier voisin.
- Tests unitaires dans chaque module via
#[cfg(test)] mod tests; utilisertempfilepour tout fichier temporaire, ne jamais toucher au vrai répertoire de config de l'utilisateur dans un test. - Pas de nouvelle dépendance sans justification (Cargo.toml est volontairement minimal,
reqwesten mode blocking uniquement).
Notes
spec/contient le plan initial et les tâches — la roadmap du README fait foi pour ce qui est fait/reste à faire.- Les émojis dans les libellés de boutons existent dans l'UI ; ne pas en ajouter dans le code, les commits ou les commentaires.
- Fins de ligne : l'arbre de travail est en LF mais
core.autocrlf=true(gitconfig système Git for Windows). Ne pas fairegit checkout -- <fichiers>pour annuler des changements : les fichiers repasseraient en CRLF et casseraient les outils d'édition. Annuler viagit -c core.autocrlf=false checkout -- <fichiers>puis normaliser en LF si besoin.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
|