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(in crate::app) store: SnippetStore,
11 pub(in crate::app) form: SnippetForm,
12 pub(in crate::app) pending: Option<(Snippet, Vec<String>)>,
13 pub(in crate::app) output: Option<SnippetOutputState>,
14 pub(in crate::app) param_form: Option<SnippetParamFormState>,
15 pub(in crate::app) pending_terminal: bool,
16 pub(in crate::app) form_baseline: Option<SnippetFormBaseline>,
17 pub(in crate::app) 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 store(&self) -> &SnippetStore {
37 &self.store
38 }
39
40 pub fn store_mut(&mut self) -> &mut SnippetStore {
41 &mut self.store
42 }
43
44 pub fn form(&self) -> &SnippetForm {
45 &self.form
46 }
47
48 pub fn form_mut(&mut self) -> &mut SnippetForm {
49 &mut self.form
50 }
51
52 pub fn output(&self) -> Option<&SnippetOutputState> {
53 self.output.as_ref()
54 }
55
56 pub fn output_mut(&mut self) -> Option<&mut SnippetOutputState> {
57 self.output.as_mut()
58 }
59
60 pub fn set_output(&mut self, output: Option<SnippetOutputState>) {
61 self.output = output;
62 }
63
64 pub fn take_output(&mut self) -> Option<SnippetOutputState> {
65 self.output.take()
66 }
67
68 pub fn param_form(&self) -> Option<&SnippetParamFormState> {
69 self.param_form.as_ref()
70 }
71
72 pub fn param_form_mut(&mut self) -> Option<&mut SnippetParamFormState> {
73 self.param_form.as_mut()
74 }
75
76 pub fn set_param_form(&mut self, param_form: Option<SnippetParamFormState>) {
77 self.param_form = param_form;
78 }
79
80 pub fn pending_delete(&self) -> Option<usize> {
81 self.pending_delete
82 }
83
84 pub fn take_pending_delete(&mut self) -> Option<usize> {
85 self.pending_delete.take()
86 }
87
88 pub fn pending(&self) -> Option<&(Snippet, Vec<String>)> {
89 self.pending.as_ref()
90 }
91
92 pub fn take_pending(&mut self) -> Option<(Snippet, Vec<String>)> {
93 self.pending.take()
94 }
95
96 pub fn set_pending(&mut self, value: Option<(Snippet, Vec<String>)>) {
97 self.pending = value;
98 }
99
100 pub fn pending_terminal(&self) -> bool {
101 self.pending_terminal
102 }
103
104 pub fn set_pending_terminal(&mut self, value: bool) {
105 self.pending_terminal = value;
106 }
107
108 pub fn form_baseline(&self) -> Option<&SnippetFormBaseline> {
109 self.form_baseline.as_ref()
110 }
111
112 pub fn set_form_baseline(&mut self, baseline: Option<SnippetFormBaseline>) {
113 self.form_baseline = baseline;
114 }
115
116 pub fn with_store_loaded() -> Self {
118 Self {
119 store: crate::snippet::SnippetStore::load(),
120 ..Self::default()
121 }
122 }
123
124 pub fn request_delete(&mut self, idx: usize) {
127 self.pending_delete = Some(idx);
128 }
129
130 pub fn cancel_delete(&mut self) {
132 self.pending_delete = None;
133 }
134
135 pub fn close_param_form(&mut self) {
140 self.param_form = None;
141 self.pending_terminal = false;
142 }
143}
144
145#[cfg(test)]
146mod tests {
147 use super::*;
148
149 #[test]
150 fn default_is_empty() {
151 let s = SnippetState::default();
152 assert!(s.pending.is_none());
153 assert!(s.output.is_none());
154 assert!(s.param_form.is_none());
155 assert!(!s.pending_terminal);
156 assert!(s.form_baseline.is_none());
157 assert!(s.pending_delete.is_none());
158 }
159
160 #[test]
161 fn request_delete_sets_pending_delete_to_some_idx() {
162 let mut s = SnippetState::default();
163 s.request_delete(3);
164 assert_eq!(s.pending_delete, Some(3));
165 }
166
167 #[test]
168 fn cancel_delete_clears_pending_delete() {
169 let mut s = SnippetState {
170 pending_delete: Some(2),
171 ..Default::default()
172 };
173 s.cancel_delete();
174 assert!(s.pending_delete.is_none());
175 }
176
177 #[test]
178 fn request_delete_overwrites_existing_pending() {
179 let mut s = SnippetState {
180 pending_delete: Some(1),
181 ..Default::default()
182 };
183 s.request_delete(7);
184 assert_eq!(s.pending_delete, Some(7));
185 }
186
187 #[test]
188 fn close_param_form_clears_param_form_and_pending_terminal() {
189 let mut s = SnippetState {
190 param_form: Some(SnippetParamFormState::new(&[])),
191 pending_terminal: true,
192 ..Default::default()
193 };
194 s.close_param_form();
195 assert!(s.param_form.is_none());
196 assert!(!s.pending_terminal);
197 }
198
199 #[test]
200 fn close_param_form_preserves_pending_output_and_store() {
201 use crate::snippet::Snippet;
202 let mut s = SnippetState {
203 param_form: Some(SnippetParamFormState::new(&[])),
204 pending_terminal: true,
205 pending: Some((
206 Snippet {
207 name: "ls".into(),
208 command: "ls -la".into(),
209 description: String::new(),
210 },
211 vec!["host-a".into()],
212 )),
213 ..Default::default()
214 };
215
216 s.close_param_form();
217
218 assert!(
219 s.pending.is_some(),
220 "pending stays for the consumer to read"
221 );
222 assert!(s.pending_delete.is_none());
223 }
224
225 #[test]
226 fn close_param_form_is_idempotent_when_already_none() {
227 let mut s = SnippetState::default();
228 s.close_param_form();
229 s.close_param_form();
230 assert!(s.param_form.is_none());
231 assert!(!s.pending_terminal);
232 }
233}