1use crossterm::event::{Event, KeyCode, KeyModifiers};
2use tty_interface::{pos, Interface, Position};
3
4use crate::{
5 dependency::DependencyState,
6 device::InputDevice,
7 step::{InputResult, Step},
8 utility::render_segment,
9 Error, Result,
10};
11
12pub struct Form {
39 steps: Vec<Box<dyn Step>>,
40
41 active_step: usize,
43
44 max_step: usize,
46
47 last_height: u16,
49}
50
51impl Default for Form {
52 fn default() -> Self {
54 Self {
55 steps: Vec::new(),
56 active_step: 0,
57 max_step: 0,
58 last_height: 0,
59 }
60 }
61}
62
63impl Form {
64 pub fn new() -> Form {
66 Self::default()
67 }
68
69 pub fn add_step(&mut self, step: Box<dyn Step>) {
71 self.steps.push(step);
72 }
73
74 pub fn execute<D: InputDevice>(
76 mut self,
77 interface: &mut Interface,
78 input_device: &mut D,
79 ) -> Result<String> {
80 let mut dependency_state = DependencyState::new();
81
82 for (step_index, step) in self.steps.iter_mut().enumerate() {
83 step.initialize(&mut dependency_state, step_index);
84 }
85
86 self.render_form(interface, &dependency_state);
87 interface.apply()?;
88
89 loop {
90 interface.set_cursor(None);
91
92 if let Event::Key(key_event) = input_device.read()? {
93 if (KeyModifiers::CONTROL, KeyCode::Char('c'))
94 == (key_event.modifiers, key_event.code)
95 {
96 return self.cancel_form(interface, &dependency_state);
97 }
98
99 if let Some(action) =
100 self.steps[self.active_step].update(&mut dependency_state, key_event)
101 {
102 match action {
103 InputResult::AdvanceForm => {
104 if self.advance() {
105 break;
106 }
107 }
108 InputResult::RetreatForm => {
109 if self.retreat() {
110 return self.cancel_form(interface, &dependency_state);
111 }
112 }
113 }
114 }
115 }
116
117 self.render_form(interface, &dependency_state);
118 interface.apply()?;
119 }
120
121 self.render_form(interface, &dependency_state);
122 interface.apply()?;
123
124 let mut result = String::new();
125
126 for step in self.steps {
127 result.push_str(&step.result(&dependency_state));
128 }
129
130 result = result.trim().to_string();
131
132 Ok(result)
133 }
134
135 fn cancel_form(
137 &mut self,
138 interface: &mut Interface,
139 dependency_state: &DependencyState,
140 ) -> Result<String> {
141 self.active_step = usize::MAX;
142 self.render_form(interface, &dependency_state);
143 interface.apply()?;
144
145 return Err(Error::Canceled);
146 }
147
148 fn advance(&mut self) -> bool {
150 let is_last_step = self.active_step + 1 == self.steps.len();
151 if !is_last_step {
152 self.active_step += 1;
153
154 if self.active_step > self.max_step {
155 self.max_step = self.active_step;
156 }
157 }
158
159 is_last_step
160 }
161
162 fn retreat(&mut self) -> bool {
164 let is_first_step = self.active_step == 0;
165 if !is_first_step {
166 self.active_step -= 1;
167 }
168
169 is_first_step
170 }
171
172 fn render_form(&mut self, interface: &mut Interface, dependency_state: &DependencyState) {
174 for line in 0..self.last_height {
175 interface.clear_line(line);
176 }
177
178 let mut drawer = None;
179 let mut line = 1;
180 for (step_index, step) in self.steps.iter().enumerate() {
181 if step_index > self.max_step {
182 break;
183 }
184
185 let step_height = step.render(
186 interface,
187 dependency_state,
188 pos!(0, line),
189 step_index == self.active_step,
190 );
191
192 line += step_height;
193
194 if step_index == self.active_step {
195 render_segment(interface, pos!(0, 0), step.help());
196 drawer = step.drawer();
197 }
198 }
199
200 if let Some(drawer) = drawer {
201 for item in drawer {
202 render_segment(interface, pos!(0, line), item);
203 line += 1;
204 }
205 }
206
207 self.last_height = line;
208 }
209}