qframe/widgets/
form_errors.rs1use crate::runtime::Command;
4
5#[derive(Debug, Clone, Default, PartialEq, Eq)]
22pub struct FormErrors {
23 entries: Vec<(String, String)>,
24}
25
26impl FormErrors {
27 #[must_use]
29 pub fn new() -> Self {
30 Self::default()
31 }
32
33 pub fn set(&mut self, name: impl Into<String>, message: impl Into<String>) {
35 let (name, message) = (name.into(), message.into());
36 match self.entries.iter_mut().find(|(existing, _)| *existing == name) {
37 Some(entry) => entry.1 = message,
38 None => self.entries.push((name, message)),
39 }
40 }
41
42 pub fn check(&mut self, name: impl Into<String>, valid: bool, message: impl Into<String>) {
45 let name = name.into();
46 if valid {
47 self.remove(&name);
48 } else {
49 self.set(name, message);
50 }
51 }
52
53 pub fn remove(&mut self, name: &str) {
55 self.entries.retain(|(existing, _)| existing != name);
56 }
57
58 pub fn clear(&mut self) {
60 self.entries.clear();
61 }
62
63 #[must_use]
65 pub fn get(&self, name: &str) -> Option<&str> {
66 self.entries.iter().find(|(existing, _)| existing == name).map(|(_, message)| message.as_str())
67 }
68
69 #[must_use]
71 pub fn has(&self, name: &str) -> bool {
72 self.get(name).is_some()
73 }
74
75 #[must_use]
77 pub fn is_empty(&self) -> bool {
78 self.entries.is_empty()
79 }
80
81 #[must_use]
83 pub fn len(&self) -> usize {
84 self.entries.len()
85 }
86
87 #[must_use]
89 pub fn first(&self) -> Option<&str> {
90 self.entries.first().map(|(name, _)| name.as_str())
91 }
92
93 pub fn iter(&self) -> impl Iterator<Item = (&str, &str)> {
95 self.entries.iter().map(|(name, message)| (name.as_str(), message.as_str()))
96 }
97
98 #[must_use]
100 pub fn focus_first<Msg: Send + 'static>(&self) -> Command<Msg> {
101 self.first().map_or_else(Command::none, Command::focus)
102 }
103}
104
105#[cfg(test)]
106mod tests {
107 use super::*;
108
109 #[test]
110 fn keeps_order_replaces_and_forgets() {
111 let mut errors = FormErrors::new();
112 errors.set("name", "too short");
113 errors.set("engine", "choose one");
114 errors.set("name", "taken");
115 assert_eq!(errors.iter().collect::<Vec<_>>(), vec![("name", "taken"), ("engine", "choose one")]);
116 errors.check("name", true, "unused");
117 assert_eq!(errors.first(), Some("engine"));
118 assert_eq!(errors.len(), 1);
119 errors.clear();
120 assert!(errors.is_empty() && !errors.has("engine"));
121 }
122}