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)]
21pub struct FieldError<K = String> {
22 pub key: K,
24 pub kind: FieldErrorKind,
26}
27
28impl<K> FieldError<K> {
29 pub fn required(key: impl Into<K>) -> Self {
31 Self {
32 key: key.into(),
33 kind: FieldErrorKind::Required,
34 }
35 }
36
37 pub fn invalid(key: impl Into<K>, message: impl Into<Text>) -> Self {
39 Self {
40 key: key.into(),
41 kind: FieldErrorKind::Invalid(message.into()),
42 }
43 }
44
45 pub fn message(&self, label: impl Into<Text>) -> Text {
47 match &self.kind {
48 FieldErrorKind::Required => crate::text!("tablo-field-required", label = label),
49 FieldErrorKind::Invalid(message) => message.clone(),
50 }
51 }
52}
53
54pub(crate) fn unreadable(key: &str) -> FieldError {
58 FieldError::invalid(key, Text::message("tablo-field-unreadable"))
59}
60
61#[derive(Debug)]
71pub struct FieldErrors<K = String> {
72 errors: Vec<FieldError<K>>,
73}
74
75impl<K> Default for FieldErrors<K> {
76 fn default() -> Self {
77 Self { errors: Vec::new() }
78 }
79}
80
81impl<K> FieldErrors<K> {
82 pub fn new() -> Self {
84 Self::default()
85 }
86
87 pub fn add(&mut self, key: impl Into<K>, message: impl Into<Text>) {
89 self.errors.push(FieldError::invalid(key, message));
90 }
91
92 pub fn push(&mut self, error: FieldError<K>) {
94 self.errors.push(error);
95 }
96
97 pub fn is_empty(&self) -> bool {
99 self.errors.is_empty()
100 }
101
102 pub fn iter(&self) -> impl Iterator<Item = &FieldError<K>> {
104 self.errors.iter()
105 }
106
107 pub fn extend(&mut self, other: Self) {
109 self.errors.extend(other.errors);
110 }
111}
112
113impl<K> IntoIterator for FieldErrors<K> {
114 type Item = FieldError<K>;
115 type IntoIter = std::vec::IntoIter<FieldError<K>>;
116
117 fn into_iter(self) -> Self::IntoIter {
118 self.errors.into_iter()
119 }
120}
121
122impl FieldErrors {
123 pub fn contains_key(&self, key: &str) -> bool {
125 self.errors.iter().any(|error| error.key == key)
126 }
127
128 pub fn first(&self, key: &str) -> Option<&FieldError> {
130 self.errors.iter().find(|error| error.key == key)
131 }
132
133 pub(crate) fn replace(&mut self, other: Self) {
139 let owned: HashSet<&str> = other
140 .errors
141 .iter()
142 .map(|error| error.key.as_str())
143 .collect();
144 self.errors
145 .retain(|kept| !owned.contains(kept.key.as_str()));
146 self.errors.extend(other.errors);
147 }
148}