mnemo/tui/actions.rs
1//! Actions de la TUI et accès à la base isolé derrière un trait.
2//!
3//! [`Action`] décrit toutes les intentions issues du clavier (le mapping touche
4//! -> action vit dans [`crate::tui::events`]). [`TuiBackend`] isole les effets
5//! de bord (suppression avec sauvegarde, rechargement) afin que la logique de
6//! [`crate::tui::app::TuiApp`] reste testable sans base réelle.
7
8use anyhow::{bail, Result};
9
10use crate::db::{self, CommandRecord, SearchFilter};
11use crate::{backup, config};
12
13/// Intention utilisateur résolue à partir d'un événement clavier.
14#[derive(Debug, Clone, PartialEq, Eq)]
15pub enum Action {
16 /// Quitter la TUI sans sélectionner.
17 Quit,
18 /// Sélectionner la commande courante puis quitter (l'imprime).
19 Select,
20 /// Élément précédent.
21 Up,
22 /// Élément suivant.
23 Down,
24 /// Page précédente.
25 PageUp,
26 /// Page suivante.
27 PageDown,
28 /// Premier élément.
29 Home,
30 /// Dernier élément.
31 End,
32 /// Effacer le dernier caractère de la requête.
33 Backspace,
34 /// Ajouter un caractère à la requête.
35 Input(char),
36 /// Ouvrir/fermer l'aide.
37 ToggleHelp,
38 /// Ouvrir/fermer le panneau de filtres.
39 ToggleFilters,
40 /// Basculer le focus liste/détails.
41 ToggleDetailsFocus,
42 /// Donner le focus à la recherche (saisie).
43 FocusSearch,
44 /// Recharger les résultats depuis la base.
45 Refresh,
46 /// Copier/émettre la commande sélectionnée.
47 Copy,
48 /// Exporter les résultats filtrés vers un fichier JSON.
49 ExportResults,
50 /// Demander la suppression de la commande sélectionnée.
51 RequestDelete,
52 /// Confirmer la suppression.
53 ConfirmYes,
54 /// Annuler la suppression.
55 ConfirmNo,
56 /// Filtrer par le projet de la sélection.
57 FilterProjectFromSelection,
58 /// Filtrer par la branche de la sélection.
59 FilterBranchFromSelection,
60 /// Filtrer par le répertoire de la sélection.
61 FilterCwdFromSelection,
62 /// Filtrer par le projet courant (dossier de lancement).
63 FilterProjectCurrent,
64 /// Filtrer par la branche courante (dossier de lancement).
65 FilterBranchCurrent,
66 /// Faire défiler le filtre de statut (tous/succès/échecs).
67 CycleStatusFilter,
68 /// Vider tous les filtres.
69 ClearFilters,
70 /// Aucune action.
71 None,
72}
73
74/// Effets de bord de la TUI sur la base de données.
75pub trait TuiBackend {
76 /// Crée une sauvegarde puis supprime la commande `id`.
77 ///
78 /// **Contrat de sécurité** : si la sauvegarde échoue, la suppression ne doit
79 /// pas avoir lieu et la méthode renvoie `Err`.
80 fn backup_and_delete(&mut self, id: i64) -> Result<()>;
81
82 /// Recharge l'ensemble des commandes (jusqu'à la limite de configuration).
83 fn reload(&mut self) -> Result<Vec<CommandRecord>>;
84}
85
86/// Backend réel : ouvre la base locale et applique les opérations.
87pub struct DbBackend {
88 conn: rusqlite::Connection,
89 limit: usize,
90}
91
92impl DbBackend {
93 /// Ouvre la base locale et prépare le backend.
94 pub fn open(limit: usize) -> Result<Self> {
95 let conn = db::open(&config::db_path()?)?;
96 Ok(Self { conn, limit })
97 }
98}
99
100impl TuiBackend for DbBackend {
101 fn backup_and_delete(&mut self, id: i64) -> Result<()> {
102 // Sauvegarde AVANT toute modification : en cas d'échec, on n'altère rien.
103 let info = backup::create_backup(None)?;
104 let _ = info;
105 let n = db::delete_command(&self.conn, id)?;
106 if n == 0 {
107 bail!("commande #{id} introuvable");
108 }
109 Ok(())
110 }
111
112 fn reload(&mut self) -> Result<Vec<CommandRecord>> {
113 db::fetch_filtered(&self.conn, &SearchFilter::default(), self.limit)
114 }
115}