Skip to main content

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}