1use std::fs::File;
4use std::io::{self, Write};
5use std::path::Path;
6
7use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
8use ratatui::buffer::Buffer;
9use ratatui::layout::{Constraint, Layout, Rect};
10use ratatui::text::{Line, Span};
11use ratatui::widgets::{Block, Borders, Padding, Widget};
12use serde_json::{Map, Value};
13
14use crate::block::Block as FormBlock;
15use crate::field::{Checkbox, Field, Select, TextInput};
16use crate::navigation::FocusManager;
17use crate::style::FormStyle;
18use crate::validation::ValidationError;
19
20#[derive(Debug, Clone, PartialEq, Eq)]
22pub enum FormResult {
23 Submitted,
25 Cancelled,
27 Active,
29}
30
31pub struct Form {
33 title: Option<String>,
34 fields: Vec<Box<dyn Field>>,
35 focus_manager: FocusManager,
36 style: FormStyle,
37 result: FormResult,
38 validation_errors: Vec<ValidationError>,
39}
40
41impl Form {
42 pub fn builder() -> FormBuilder {
44 FormBuilder::new()
45 }
46
47 pub fn result(&self) -> &FormResult {
49 &self.result
50 }
51
52 pub fn is_active(&self) -> bool {
54 self.result == FormResult::Active
55 }
56
57 pub fn handle_input(&mut self, event: KeyEvent) {
59 match event.code {
61 KeyCode::Esc => {
62 self.result = FormResult::Cancelled;
63 return;
64 }
65 KeyCode::Tab => {
66 if event.modifiers.contains(KeyModifiers::SHIFT) {
67 self.focus_manager.focus_previous();
68 } else {
69 self.focus_manager.focus_next();
70 }
71 return;
72 }
73 KeyCode::Enter if self.focus_manager.is_submit_focused() => {
74 self.try_submit();
75 return;
76 }
77 KeyCode::Down => {
78 if !self.delegate_to_focused_field(&event) {
80 self.focus_manager.focus_next();
81 }
82 return;
83 }
84 KeyCode::Up => {
85 if !self.delegate_to_focused_field(&event) {
86 self.focus_manager.focus_previous();
87 }
88 return;
89 }
90 _ => {}
91 }
92
93 self.delegate_to_focused_field(&event);
95 }
96
97 fn delegate_to_focused_field(&mut self, event: &KeyEvent) -> bool {
98 if self.focus_manager.is_submit_focused() {
99 return false;
100 }
101
102 let index = self.focus_manager.current_index();
103 if let Some(field) = self.fields.get_mut(index) {
104 field.handle_input(event)
105 } else {
106 false
107 }
108 }
109
110 fn try_submit(&mut self) {
111 self.validation_errors.clear();
112
113 for field in &self.fields {
114 if let Err(errors) = field.validate() {
115 self.validation_errors.extend(errors);
116 }
117 }
118
119 if self.validation_errors.is_empty() {
120 self.result = FormResult::Submitted;
121 } else {
122 if let Some(error) = self.validation_errors.first() {
124 for (i, field) in self.fields.iter().enumerate() {
125 if field.id() == error.field_id {
126 self.focus_manager.focus_field(i);
127 break;
128 }
129 }
130 }
131 }
132 }
133
134 pub fn to_json(&self) -> Value {
136 let mut map = Map::new();
137
138 for field in &self.fields {
139 map.insert(field.id().to_string(), field.value());
140 }
141
142 Value::Object(map)
143 }
144
145 pub fn write_json(&self, path: impl AsRef<Path>) -> io::Result<()> {
147 let json = self.to_json();
148 let mut file = File::create(path)?;
149 let formatted = serde_json::to_string_pretty(&json)?;
150 file.write_all(formatted.as_bytes())?;
151 Ok(())
152 }
153
154 pub fn validation_errors(&self) -> &[ValidationError] {
156 &self.validation_errors
157 }
158
159 pub fn render(&self, area: Rect, buf: &mut Buffer) {
161 let border_style = if self.focus_manager.is_submit_focused() {
163 self.style.border
164 } else {
165 self.style.border_focused
166 };
167
168 let mut block = Block::default()
169 .borders(Borders::ALL)
170 .border_style(border_style)
171 .padding(Padding::horizontal(1));
172
173 if let Some(ref title) = self.title {
174 block = block.title(Span::styled(title, self.style.title));
175 }
176
177 let inner_area = block.inner(area);
178 block.render(area, buf);
179
180 if inner_area.height < 2 || inner_area.width < 10 {
181 return;
182 }
183
184 let field_count = self.fields.len();
186 let mut constraints = Vec::with_capacity(field_count + 2);
187
188 for field in &self.fields {
189 constraints.push(Constraint::Length(field.height()));
190 }
191 constraints.push(Constraint::Length(1)); constraints.push(Constraint::Length(1)); constraints.push(Constraint::Min(0)); let layout = Layout::vertical(constraints).split(inner_area);
196
197 for (i, field) in self.fields.iter().enumerate() {
199 let is_focused = !self.focus_manager.is_submit_focused()
200 && i == self.focus_manager.current_index();
201 field.render(layout[i], buf, is_focused, &self.style);
202 }
203
204 let submit_idx = field_count + 1;
206 if submit_idx < layout.len() {
207 self.render_submit_button(layout[submit_idx], buf);
208 }
209
210 if !self.validation_errors.is_empty() {
212 let error_count = self.validation_errors.len();
213 let error_msg = if error_count == 1 {
214 "1 validation error".to_string()
215 } else {
216 format!("{} validation errors", error_count)
217 };
218
219 let error_area = Rect {
220 x: inner_area.x,
221 y: inner_area.y + inner_area.height.saturating_sub(1),
222 width: inner_area.width,
223 height: 1,
224 };
225
226 let error_line = Line::from(Span::styled(error_msg, self.style.error));
227 error_line.render(error_area, buf);
228 }
229 }
230
231 fn render_submit_button(&self, area: Rect, buf: &mut Buffer) {
232 let is_focused = self.focus_manager.is_submit_focused();
233 let style = if is_focused {
234 self.style.button_focused
235 } else {
236 self.style.button
237 };
238
239 let text = if is_focused { "[ Submit ]" } else { " Submit " };
240
241 let button_width = text.len() as u16;
243 let x = area.x + (area.width.saturating_sub(button_width)) / 2;
244
245 for (i, c) in text.chars().enumerate() {
246 if x + (i as u16) < area.x + area.width {
247 buf[(x + i as u16, area.y)].set_char(c);
248 buf[(x + i as u16, area.y)].set_style(style);
249 }
250 }
251 }
252}
253
254pub struct FormBuilder {
256 title: Option<String>,
257 fields: Vec<Box<dyn Field>>,
258 style: FormStyle,
259}
260
261impl FormBuilder {
262 pub fn new() -> Self {
264 Self {
265 title: None,
266 fields: Vec::new(),
267 style: FormStyle::default(),
268 }
269 }
270
271 pub fn title(mut self, title: impl Into<String>) -> Self {
273 self.title = Some(title.into());
274 self
275 }
276
277 pub fn style(mut self, style: FormStyle) -> Self {
279 self.style = style;
280 self
281 }
282
283 pub fn text(self, id: impl Into<String>, label: impl Into<String>) -> TextFieldBuilder {
285 TextFieldBuilder::new(self, id.into(), label.into())
286 }
287
288 pub fn select(self, id: impl Into<String>, label: impl Into<String>) -> SelectFieldBuilder {
290 SelectFieldBuilder::new(self, id.into(), label.into())
291 }
292
293 pub fn checkbox(self, id: impl Into<String>, label: impl Into<String>) -> CheckboxFieldBuilder {
295 CheckboxFieldBuilder::new(self, id.into(), label.into())
296 }
297
298 pub fn field(mut self, field: Box<dyn Field>) -> Self {
300 self.fields.push(field);
301 self
302 }
303
304 pub fn block(mut self, block: impl FormBlock) -> Self {
306 for field in block.fields() {
307 self.fields.push(field);
308 }
309 self
310 }
311
312 pub fn build(self) -> Form {
314 let field_count = self.fields.len();
315 Form {
316 title: self.title,
317 fields: self.fields,
318 focus_manager: FocusManager::new(field_count),
319 style: self.style,
320 result: FormResult::Active,
321 validation_errors: Vec::new(),
322 }
323 }
324}
325
326impl Default for FormBuilder {
327 fn default() -> Self {
328 Self::new()
329 }
330}
331
332pub struct TextFieldBuilder {
334 form_builder: FormBuilder,
335 field: TextInput,
336}
337
338impl TextFieldBuilder {
339 fn new(form_builder: FormBuilder, id: String, label: String) -> Self {
340 Self {
341 form_builder,
342 field: TextInput::new(id, label),
343 }
344 }
345
346 pub fn placeholder(mut self, placeholder: impl Into<String>) -> Self {
348 self.field = self.field.placeholder(placeholder);
349 self
350 }
351
352 pub fn required(mut self) -> Self {
354 self.field = self.field.required();
355 self
356 }
357
358 pub fn initial_value(mut self, value: impl Into<String>) -> Self {
360 self.field = self.field.initial_value(value);
361 self
362 }
363
364 pub fn validator(mut self, validator: Box<dyn crate::validation::Validator>) -> Self {
366 self.field = self.field.validator(validator);
367 self
368 }
369
370 pub fn done(mut self) -> FormBuilder {
372 self.form_builder.fields.push(Box::new(self.field));
373 self.form_builder
374 }
375}
376
377pub struct SelectFieldBuilder {
379 form_builder: FormBuilder,
380 field: Select,
381}
382
383impl SelectFieldBuilder {
384 fn new(form_builder: FormBuilder, id: String, label: String) -> Self {
385 Self {
386 form_builder,
387 field: Select::new(id, label),
388 }
389 }
390
391 pub fn option(mut self, value: impl Into<String>, display: impl Into<String>) -> Self {
393 self.field = self.field.option(value, display);
394 self
395 }
396
397 pub fn options(mut self, options: Vec<(impl Into<String>, impl Into<String>)>) -> Self {
399 self.field = self.field.options(options);
400 self
401 }
402
403 pub fn required(mut self) -> Self {
405 self.field = self.field.required();
406 self
407 }
408
409 pub fn initial_value(mut self, value: &str) -> Self {
411 self.field = self.field.initial_value(value);
412 self
413 }
414
415 pub fn done(mut self) -> FormBuilder {
417 self.form_builder.fields.push(Box::new(self.field));
418 self.form_builder
419 }
420}
421
422pub struct CheckboxFieldBuilder {
424 form_builder: FormBuilder,
425 field: Checkbox,
426}
427
428impl CheckboxFieldBuilder {
429 fn new(form_builder: FormBuilder, id: String, label: String) -> Self {
430 Self {
431 form_builder,
432 field: Checkbox::new(id, label),
433 }
434 }
435
436 pub fn checked(mut self, checked: bool) -> Self {
438 self.field = self.field.checked(checked);
439 self
440 }
441
442 pub fn required(mut self) -> Self {
444 self.field = self.field.required();
445 self
446 }
447
448 pub fn done(mut self) -> FormBuilder {
450 self.form_builder.fields.push(Box::new(self.field));
451 self.form_builder
452 }
453}