purple_ssh/app/
snippet_state.rs1use crate::app::SnippetFormBaseline;
2use crate::app::forms::{SnippetForm, SnippetOutputState, SnippetParamFormState};
3use crate::snippet::{Snippet, SnippetStore};
4
5pub struct SnippetState {
10 pub store: SnippetStore,
11 pub form: SnippetForm,
12 pub pending: Option<(Snippet, Vec<String>)>,
13 pub output: Option<SnippetOutputState>,
14 pub param_form: Option<SnippetParamFormState>,
15 pub pending_terminal: bool,
16 pub form_baseline: Option<SnippetFormBaseline>,
17 pub pending_delete: Option<usize>,
18}
19
20impl Default for SnippetState {
21 fn default() -> Self {
22 Self {
23 store: SnippetStore::default(),
24 form: SnippetForm::new(),
25 pending: None,
26 output: None,
27 param_form: None,
28 pending_terminal: false,
29 form_baseline: None,
30 pending_delete: None,
31 }
32 }
33}
34
35impl SnippetState {
36 pub fn with_store_loaded() -> Self {
38 Self {
39 store: crate::snippet::SnippetStore::load(),
40 ..Self::default()
41 }
42 }
43
44 pub fn request_delete(&mut self, idx: usize) {
47 self.pending_delete = Some(idx);
48 }
49
50 pub fn cancel_delete(&mut self) {
52 self.pending_delete = None;
53 }
54
55 pub fn close_param_form(&mut self) {
60 self.param_form = None;
61 self.pending_terminal = false;
62 }
63}
64
65#[cfg(test)]
66mod tests {
67 use super::*;
68
69 #[test]
70 fn default_is_empty() {
71 let s = SnippetState::default();
72 assert!(s.pending.is_none());
73 assert!(s.output.is_none());
74 assert!(s.param_form.is_none());
75 assert!(!s.pending_terminal);
76 assert!(s.form_baseline.is_none());
77 assert!(s.pending_delete.is_none());
78 }
79
80 #[test]
81 fn request_delete_sets_pending_delete_to_some_idx() {
82 let mut s = SnippetState::default();
83 s.request_delete(3);
84 assert_eq!(s.pending_delete, Some(3));
85 }
86
87 #[test]
88 fn cancel_delete_clears_pending_delete() {
89 let mut s = SnippetState {
90 pending_delete: Some(2),
91 ..Default::default()
92 };
93 s.cancel_delete();
94 assert!(s.pending_delete.is_none());
95 }
96
97 #[test]
98 fn request_delete_overwrites_existing_pending() {
99 let mut s = SnippetState {
100 pending_delete: Some(1),
101 ..Default::default()
102 };
103 s.request_delete(7);
104 assert_eq!(s.pending_delete, Some(7));
105 }
106
107 #[test]
108 fn close_param_form_clears_param_form_and_pending_terminal() {
109 let mut s = SnippetState {
110 param_form: Some(SnippetParamFormState::new(&[])),
111 pending_terminal: true,
112 ..Default::default()
113 };
114 s.close_param_form();
115 assert!(s.param_form.is_none());
116 assert!(!s.pending_terminal);
117 }
118
119 #[test]
120 fn close_param_form_preserves_pending_output_and_store() {
121 use crate::snippet::Snippet;
122 let mut s = SnippetState {
123 param_form: Some(SnippetParamFormState::new(&[])),
124 pending_terminal: true,
125 pending: Some((
126 Snippet {
127 name: "ls".into(),
128 command: "ls -la".into(),
129 description: String::new(),
130 },
131 vec!["host-a".into()],
132 )),
133 ..Default::default()
134 };
135
136 s.close_param_form();
137
138 assert!(
139 s.pending.is_some(),
140 "pending stays for the consumer to read"
141 );
142 assert!(s.pending_delete.is_none());
143 }
144
145 #[test]
146 fn close_param_form_is_idempotent_when_already_none() {
147 let mut s = SnippetState::default();
148 s.close_param_form();
149 s.close_param_form();
150 assert!(s.param_form.is_none());
151 assert!(!s.pending_terminal);
152 }
153}