Skip to main content

purple_ssh/app/
form_state.rs

1use crate::app::FormBaseline;
2use crate::app::forms::HostForm;
3use crate::app::tag_state::BulkTagEditorState;
4
5/// Host-form and bulk-tag editor state grouped off the `App` god-struct.
6/// Holds the add/edit host form, its dirty-check baseline, the bulk-tag
7/// editor, the last-apply snapshot used by `u` to revert bulk-tag changes
8/// and the pending-discard confirmation flag. Pure state container.
9pub struct FormState {
10    pub(crate) host: HostForm,
11    pub(crate) host_baseline: Option<FormBaseline>,
12    pub(crate) bulk_tag_editor: BulkTagEditorState,
13    /// Snapshot of the last bulk tag apply, used by `u` to revert the
14    /// operation even though `undo_stack` only holds deleted hosts. Holds
15    /// `(alias, previous_tags)` pairs so restore is idempotent. Cleared
16    /// after a successful undo or on the next mutation.
17    pub(crate) bulk_tag_undo: Option<Vec<(String, Vec<String>)>>,
18    /// When true, the Esc key shows a "Discard changes?" dialog instead of
19    /// closing the open host form. Mutate via `request_discard_confirm`
20    /// and `dismiss_discard_confirm`; read via `is_discard_pending`.
21    discard_pending: bool,
22}
23
24impl FormState {
25    /// Arm the "Discard changes?" Esc dialog. Called when Esc fires on a
26    /// form that has unsaved edits relative to its baseline.
27    pub fn request_discard_confirm(&mut self) {
28        self.discard_pending = true;
29    }
30
31    /// Clear the pending discard flag. Called on dialog dismiss, on
32    /// successful save, and on form close.
33    pub fn dismiss_discard_confirm(&mut self) {
34        self.discard_pending = false;
35    }
36
37    /// True when Esc should show the "Discard changes?" dialog instead of
38    /// closing the form.
39    pub fn is_discard_pending(&self) -> bool {
40        self.discard_pending
41    }
42}
43
44impl Default for FormState {
45    fn default() -> Self {
46        Self {
47            host: HostForm::new(),
48            host_baseline: None,
49            bulk_tag_editor: BulkTagEditorState::default(),
50            bulk_tag_undo: None,
51            discard_pending: false,
52        }
53    }
54}
55
56#[cfg(test)]
57mod tests {
58    use super::*;
59
60    #[test]
61    fn default_is_empty() {
62        let s = FormState::default();
63        assert!(!s.is_discard_pending());
64        assert!(s.bulk_tag_undo.is_none());
65        assert!(s.host_baseline.is_none());
66        assert!(s.bulk_tag_editor.rows.is_empty());
67    }
68
69    #[test]
70    fn discard_confirm_lifecycle() {
71        let mut s = FormState::default();
72        assert!(!s.is_discard_pending());
73        s.request_discard_confirm();
74        assert!(s.is_discard_pending());
75        s.dismiss_discard_confirm();
76        assert!(!s.is_discard_pending());
77    }
78}