1use crate::field::Field;
4use crate::form::Form;
5
6pub type Validator = Box<dyn Fn(&str) -> Result<(), String> + Send>;
9
10pub fn validate_field(field: &mut Field) -> bool {
14 let (text, validator, meta) = match field {
15 Field::SingleLineText(f) => {
16 let text = f.editor.buffer().as_string();
17 (text, &f.validator, &mut f.meta)
18 }
19 Field::MultiLineText(f) => {
20 let text = f.editor.buffer().as_string();
21 (text, &f.validator, &mut f.meta)
22 }
23 Field::Select(_) | Field::Checkbox(_) | Field::Submit(_) => return true,
25 };
26 match validator {
27 Some(v) => match v(&text) {
28 Ok(()) => {
29 meta.error = None;
30 true
31 }
32 Err(msg) => {
33 meta.error = Some(msg);
34 false
35 }
36 },
37 None => {
38 meta.error = None;
39 true
40 }
41 }
42}
43
44impl Form {
45 pub fn validate_focused(&mut self) -> bool {
48 let idx = self.focused();
49 match self.fields.get_mut(idx) {
50 Some(field) => validate_field(field),
51 None => true,
52 }
53 }
54
55 pub fn validate_all(&mut self) -> Result<(), Vec<(usize, String)>> {
59 let mut errors = Vec::new();
60 for (idx, field) in self.fields.iter_mut().enumerate() {
61 if !validate_field(field)
62 && let Some(msg) = field.meta().error.clone()
63 {
64 errors.push((idx, msg));
65 }
66 }
67 if errors.is_empty() {
68 Ok(())
69 } else {
70 Err(errors)
71 }
72 }
73}
74
75#[cfg(test)]
76mod tests {
77 use super::*;
78 use crate::field::{FieldMeta, SubmitField, TextFieldEditor};
79 use hjkl_engine::{Input, Key};
80
81 fn key(c: char) -> Input {
82 Input {
83 key: Key::Char(c),
84 ..Input::default()
85 }
86 }
87
88 #[test]
89 fn empty_validator_blocks_and_populates_error() {
90 let mut name = TextFieldEditor::with_meta(FieldMeta::new("Name").required(true), 1);
91 name.validator = Some(Box::new(|s: &str| {
92 if s.is_empty() {
93 Err("required".into())
94 } else {
95 Ok(())
96 }
97 }));
98 let mut form = Form::new()
99 .with_field(Field::SingleLineText(name))
100 .with_field(Field::Submit(SubmitField::new(FieldMeta::new("Sub"))));
101 let res = form.validate_all();
102 assert!(res.is_err());
103 let errs = res.unwrap_err();
104 assert_eq!(errs.len(), 1);
105 assert_eq!(errs[0].0, 0);
106 assert_eq!(form.fields[0].meta().error.as_deref(), Some("required"));
107 }
108
109 #[test]
110 fn blur_validate_runs_on_focus_change() {
111 let mut name = TextFieldEditor::with_meta(FieldMeta::new("Name").required(true), 1);
112 name.validator = Some(Box::new(|s: &str| {
113 if s.is_empty() {
114 Err("required".into())
115 } else {
116 Ok(())
117 }
118 }));
119 let mut form = Form::new()
120 .with_field(Field::SingleLineText(name))
121 .with_field(Field::Submit(SubmitField::new(FieldMeta::new("Sub"))));
122 form.handle_input(key('j'));
124 assert_eq!(form.fields[0].meta().error.as_deref(), Some("required"));
125 }
126}