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