1use crate::form::Form;
4
5pub type SubmitFn = Box<dyn FnOnce() -> SubmitOutcome + Send>;
7
8#[derive(Debug, Clone)]
10pub enum SubmitOutcome {
11 Ok,
12 Err(String),
13}
14
15impl Form {
16 pub fn try_submit(&mut self) -> Option<SubmitOutcome> {
22 if self.validate_all().is_err() {
23 self.bump_dirty();
24 return None;
25 }
26 Some(match self.submit.take() {
27 Some(submit) => submit(),
28 None => SubmitOutcome::Ok,
29 })
30 }
31}
32
33#[cfg(test)]
34mod tests {
35 use super::*;
36 use crate::field::{Field, FieldMeta, SubmitField, TextFieldEditor};
37 use std::sync::Arc;
38 use std::sync::atomic::{AtomicUsize, Ordering};
39
40 #[test]
41 fn successful_submit_consumes_fn() {
42 let count = Arc::new(AtomicUsize::new(0));
43 let c2 = count.clone();
44 let mut form = Form::new()
45 .with_field(Field::Submit(SubmitField::new(FieldMeta::new("Sub"))))
46 .with_submit(Box::new(move || {
47 c2.fetch_add(1, Ordering::SeqCst);
48 SubmitOutcome::Ok
49 }));
50 let r = form.try_submit();
51 assert!(matches!(r, Some(SubmitOutcome::Ok)));
52 assert_eq!(count.load(Ordering::SeqCst), 1);
53 let r2 = form.try_submit();
56 assert!(matches!(r2, Some(SubmitOutcome::Ok)));
57 assert_eq!(count.load(Ordering::SeqCst), 1);
58 }
59
60 #[test]
61 fn try_submit_blocked_by_validator() {
62 let count = Arc::new(AtomicUsize::new(0));
63 let c2 = count.clone();
64 let mut name = TextFieldEditor::with_meta(FieldMeta::new("Name").required(true), 1);
65 name.validator = Some(Box::new(|s: &str| {
66 if s.is_empty() {
67 Err("required".into())
68 } else {
69 Ok(())
70 }
71 }));
72 let mut form = Form::new()
73 .with_field(Field::SingleLineText(name))
74 .with_field(Field::Submit(SubmitField::new(FieldMeta::new("Sub"))))
75 .with_submit(Box::new(move || {
76 c2.fetch_add(1, Ordering::SeqCst);
77 SubmitOutcome::Ok
78 }));
79 let r = form.try_submit();
80 assert!(r.is_none());
81 assert_eq!(count.load(Ordering::SeqCst), 0);
82 }
83}