use crate::field::Field;
use crate::form::Form;
pub type Validator = Box<dyn Fn(&str) -> Result<(), String> + Send>;
pub fn validate_field(field: &mut Field) -> bool {
let (text, validator, meta) = match field {
Field::SingleLineText(f) => {
let text = f.editor.buffer().as_string();
(text, &f.validator, &mut f.meta)
}
Field::MultiLineText(f) => {
let text = f.editor.buffer().as_string();
(text, &f.validator, &mut f.meta)
}
Field::Select(_) | Field::Checkbox(_) | Field::Submit(_) => return true,
};
match validator {
Some(v) => match v(&text) {
Ok(()) => {
meta.error = None;
true
}
Err(msg) => {
meta.error = Some(msg);
false
}
},
None => {
meta.error = None;
true
}
}
}
impl Form {
pub fn validate_focused(&mut self) -> bool {
let idx = self.focused();
match self.fields.get_mut(idx) {
Some(field) => validate_field(field),
None => true,
}
}
pub fn validate_all(&mut self) -> Result<(), Vec<(usize, String)>> {
let mut errors = Vec::new();
for (idx, field) in self.fields.iter_mut().enumerate() {
if !validate_field(field)
&& let Some(msg) = field.meta().error.clone()
{
errors.push((idx, msg));
}
}
if errors.is_empty() {
Ok(())
} else {
Err(errors)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::field::{FieldMeta, SubmitField, TextFieldEditor};
use hjkl_engine::{Input, Key};
fn key(c: char) -> Input {
Input {
key: Key::Char(c),
..Input::default()
}
}
#[test]
fn empty_validator_blocks_and_populates_error() {
let mut name = TextFieldEditor::with_meta(FieldMeta::new("Name").required(true), 1);
name.validator = Some(Box::new(|s: &str| {
if s.is_empty() {
Err("required".into())
} else {
Ok(())
}
}));
let mut form = Form::new()
.with_field(Field::SingleLineText(name))
.with_field(Field::Submit(SubmitField::new(FieldMeta::new("Sub"))));
let res = form.validate_all();
assert!(res.is_err());
let errs = res.unwrap_err();
assert_eq!(errs.len(), 1);
assert_eq!(errs[0].0, 0);
assert_eq!(form.fields[0].meta().error.as_deref(), Some("required"));
}
#[test]
fn blur_validate_runs_on_focus_change() {
let mut name = TextFieldEditor::with_meta(FieldMeta::new("Name").required(true), 1);
name.validator = Some(Box::new(|s: &str| {
if s.is_empty() {
Err("required".into())
} else {
Ok(())
}
}));
let mut form = Form::new()
.with_field(Field::SingleLineText(name))
.with_field(Field::Submit(SubmitField::new(FieldMeta::new("Sub"))));
form.handle_input(key('j'));
assert_eq!(form.fields[0].meta().error.as_deref(), Some("required"));
}
}