slack_messaging/
errors.rs1use std::borrow::Cow;
2use thiserror::Error;
3
4#[derive(Debug, Clone, Copy, PartialEq, Error)]
6pub enum ValidationErrorKind {
7 #[error("required")]
9 Required,
10
11 #[error("max text length `{0}` characters")]
13 MaxTextLength(usize),
14
15 #[error("max alt text length `{0}` characters")]
17 MaxAltTextLength(usize),
18
19 #[error("min text length `{0}` characters")]
21 MinTextLength(usize),
22
23 #[error("max array length `{0}` items")]
25 MaxArraySize(usize),
26
27 #[error("min array length `{0}` items")]
29 MinArraySize(usize),
30
31 #[error("the array cannot be empty")]
33 EmptyArray,
34
35 #[error("should be in the format `{0}`")]
37 InvalidFormat(&'static str),
38
39 #[error("max value is `{0}")]
41 MaxIntegerValue(i64),
42
43 #[error("min value is `{0}")]
45 MinIntegerValue(i64),
46
47 #[error("value must be greater than zero")]
49 MustBeGreaterThanZero,
50
51 #[error("cannot provide both {0} and {1}")]
53 ExclusiveField(&'static str, &'static str),
54
55 #[error("required either {0} or {1}")]
57 EitherRequired(&'static str, &'static str),
58
59 #[error("at least one of {0}, {1}, {2}, or {3} is required")]
60 AtLeastOneOf4(&'static str, &'static str, &'static str, &'static str),
61
62 #[error("required at least one field")]
64 NoFieldProvided,
65
66 #[error("rich text block should have exactly one element")]
68 RichTextSingleElement,
69
70 #[error("rich text cannot be used for a table header row")]
72 RichTextTableHeader,
73
74 #[error("each series within a chart must have a unique name")]
76 UniqueSeriesName,
77
78 #[error("every data point label in every series must match a value in axis config categories")]
80 DataPointLabelMatching,
81}
82
83#[derive(Debug, Clone, PartialEq, Error)]
89pub enum ValidationError {
90 #[error("AcrossFieldsError {0:?}")]
92 AcrossFields(Vec<ValidationErrorKind>),
93
94 #[error("SingleField {{ field: {field:}, errors: {errors:?} }}")]
96 SingleField {
97 field: Cow<'static, str>,
98 errors: Vec<ValidationErrorKind>,
99 },
100}
101
102impl ValidationError {
103 pub(crate) fn new_across_fields(inner: Vec<ValidationErrorKind>) -> Option<Self> {
104 if inner.is_empty() {
105 None
106 } else {
107 Some(Self::AcrossFields(inner))
108 }
109 }
110
111 pub(crate) fn new_single_field(
112 field: &'static str,
113 inner: Vec<ValidationErrorKind>,
114 ) -> Option<Self> {
115 if inner.is_empty() {
116 None
117 } else {
118 Some(Self::SingleField {
119 field: Cow::Borrowed(field),
120 errors: inner,
121 })
122 }
123 }
124
125 pub fn field(&self) -> Option<&str> {
127 if let Self::SingleField { field, .. } = self {
128 Some(field)
129 } else {
130 None
131 }
132 }
133
134 pub fn errors(&self) -> &[ValidationErrorKind] {
136 match self {
137 Self::AcrossFields(errors) => errors,
138 Self::SingleField { errors, .. } => errors,
139 }
140 }
141}
142
143#[derive(Debug, Clone, PartialEq, Error)]
146#[error("Validation Error {{ object: {object:}, errors: {errors:?} }}")]
147pub struct ValidationErrors {
148 pub object: Cow<'static, str>,
150 pub errors: Vec<ValidationError>,
152}
153
154impl ValidationErrors {
155 pub fn object(&self) -> &str {
157 &self.object
158 }
159
160 pub fn errors(&self) -> &[ValidationError] {
162 &self.errors
163 }
164}
165
166#[cfg(test)]
167mod test_helpers {
168 use super::*;
169
170 pub struct ErrorKinds(Vec<ValidationErrorKind>);
171
172 impl<'a> FromIterator<&'a ValidationErrorKind> for ErrorKinds {
173 fn from_iter<T: IntoIterator<Item = &'a ValidationErrorKind>>(iter: T) -> Self {
174 Self(iter.into_iter().copied().collect())
175 }
176 }
177
178 impl ErrorKinds {
179 pub fn includes(&self, error: ValidationErrorKind) -> bool {
180 self.0.contains(&error)
181 }
182 }
183
184 impl ValidationErrors {
185 pub fn field(&self, field: &'static str) -> ErrorKinds {
186 self.filter_errors(|e| e.field().is_some_and(|f| f == field))
187 }
188
189 pub fn across_fields(&self) -> ErrorKinds {
190 self.filter_errors(|e| matches!(e, ValidationError::AcrossFields(_)))
191 }
192
193 fn filter_errors(&self, predicate: impl Fn(&ValidationError) -> bool) -> ErrorKinds {
194 self.errors()
195 .iter()
196 .filter_map(move |e| if predicate(e) { Some(e.errors()) } else { None })
197 .flatten()
198 .collect()
199 }
200 }
201}