tablo_core/values/
error.rs1use std::collections::HashSet;
4
5use crate::Text;
6
7#[derive(Debug, Clone, PartialEq, Eq)]
9#[non_exhaustive]
10pub enum FieldErrorKind {
11 Required,
14 Invalid(Text),
16}
17
18#[derive(Debug, Clone, PartialEq, Eq)]
21#[non_exhaustive]
22pub struct FieldError<K = String> {
23 pub key: K,
25 pub kind: FieldErrorKind,
27}
28
29impl<K> FieldError<K> {
30 pub fn required(key: impl Into<K>) -> Self {
32 Self {
33 key: key.into(),
34 kind: FieldErrorKind::Required,
35 }
36 }
37
38 pub fn invalid(key: impl Into<K>, message: impl Into<Text>) -> Self {
40 Self {
41 key: key.into(),
42 kind: FieldErrorKind::Invalid(message.into()),
43 }
44 }
45
46 pub fn message(&self, label: impl Into<Text>) -> Text {
48 match &self.kind {
49 FieldErrorKind::Required => crate::text!("tablo-field-required", label = label),
50 FieldErrorKind::Invalid(message) => message.clone(),
51 }
52 }
53}
54
55pub(crate) fn unreadable(key: &str) -> FieldError {
59 FieldError::invalid(key, Text::message("tablo-field-unreadable"))
60}
61
62#[derive(Debug, Clone)]
72pub struct FieldErrors<K = String> {
73 errors: Vec<FieldError<K>>,
74}
75
76impl<K> Default for FieldErrors<K> {
77 fn default() -> Self {
78 Self { errors: Vec::new() }
79 }
80}
81
82impl<K> FieldErrors<K> {
83 pub fn new() -> Self {
85 Self::default()
86 }
87
88 pub fn add(&mut self, key: impl Into<K>, message: impl Into<Text>) {
90 self.errors.push(FieldError::invalid(key, message));
91 }
92
93 pub fn push(&mut self, error: FieldError<K>) {
95 self.errors.push(error);
96 }
97
98 pub fn is_empty(&self) -> bool {
100 self.errors.is_empty()
101 }
102
103 pub fn iter(&self) -> impl Iterator<Item = &FieldError<K>> {
105 self.errors.iter()
106 }
107
108 pub fn extend(&mut self, other: Self) {
110 self.errors.extend(other.errors);
111 }
112
113 pub(crate) fn keyed(self, key: fn(K) -> &'static str) -> FieldErrors {
115 let mut keyed = FieldErrors::new();
116 for error in self {
117 keyed.push(FieldError {
118 key: key(error.key).to_string(),
119 kind: error.kind,
120 });
121 }
122 keyed
123 }
124}
125
126impl<K> IntoIterator for FieldErrors<K> {
127 type Item = FieldError<K>;
128 type IntoIter = std::vec::IntoIter<FieldError<K>>;
129
130 fn into_iter(self) -> Self::IntoIter {
131 self.errors.into_iter()
132 }
133}
134
135impl FieldErrors {
136 pub fn contains_key(&self, key: &str) -> bool {
138 self.errors.iter().any(|error| error.key == key)
139 }
140
141 pub fn first(&self, key: &str) -> Option<&FieldError> {
143 self.errors.iter().find(|error| error.key == key)
144 }
145
146 pub(crate) fn replace(&mut self, other: Self) {
152 let owned: HashSet<&str> = other
153 .errors
154 .iter()
155 .map(|error| error.key.as_str())
156 .collect();
157 self.errors
158 .retain(|kept| !owned.contains(kept.key.as_str()));
159 self.errors.extend(other.errors);
160 }
161}