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("the array cannot be empty")]
25 EmptyArray,
26
27 #[error("should be in the format `{0}`")]
29 InvalidFormat(&'static str),
30
31 #[error("max value is `{0}")]
33 MaxIntegerValue(i64),
34
35 #[error("min value is `{0}")]
37 MinIntegerValue(i64),
38
39 #[error("cannot provide both {0} and {1}")]
41 ExclusiveField(&'static str, &'static str),
42
43 #[error("required either {0} or {1}")]
45 EitherRequired(&'static str, &'static str),
46
47 #[error("at least one of {0}, {1}, {2}, or {3} is required")]
48 AtLeastOneOf4(&'static str, &'static str, &'static str, &'static str),
49
50 #[error("required at least one field")]
52 NoFieldProvided,
53
54 #[error("rich text block should have exactly one element")]
56 RichTextSingleElement,
57}
58
59#[derive(Debug, Clone, PartialEq, Error)]
65pub enum ValidationError {
66 #[error("AcrossFieldsError {0:?}")]
68 AcrossFields(Vec<ValidationErrorKind>),
69
70 #[error("SingleField {{ field: {field:}, errors: {errors:?} }}")]
72 SingleField {
73 field: Cow<'static, str>,
74 errors: Vec<ValidationErrorKind>,
75 },
76}
77
78impl ValidationError {
79 pub(crate) fn new_across_fields(inner: Vec<ValidationErrorKind>) -> Option<Self> {
80 if inner.is_empty() {
81 None
82 } else {
83 Some(Self::AcrossFields(inner))
84 }
85 }
86
87 pub(crate) fn new_single_field(
88 field: &'static str,
89 inner: Vec<ValidationErrorKind>,
90 ) -> Option<Self> {
91 if inner.is_empty() {
92 None
93 } else {
94 Some(Self::SingleField {
95 field: Cow::Borrowed(field),
96 errors: inner,
97 })
98 }
99 }
100
101 pub fn field(&self) -> Option<&str> {
103 if let Self::SingleField { field, .. } = self {
104 Some(field)
105 } else {
106 None
107 }
108 }
109
110 pub fn errors(&self) -> &[ValidationErrorKind] {
112 match self {
113 Self::AcrossFields(errors) => errors,
114 Self::SingleField { errors, .. } => errors,
115 }
116 }
117}
118
119#[derive(Debug, Clone, PartialEq, Error)]
122#[error("Validation Error {{ object: {object:}, errors: {errors:?} }}")]
123pub struct ValidationErrors {
124 pub object: Cow<'static, str>,
126 pub errors: Vec<ValidationError>,
128}
129
130impl ValidationErrors {
131 pub fn object(&self) -> &str {
133 &self.object
134 }
135
136 pub fn errors(&self) -> &[ValidationError] {
138 &self.errors
139 }
140}
141
142#[cfg(test)]
143mod test_helpers {
144 use super::*;
145
146 pub struct ErrorKinds(Vec<ValidationErrorKind>);
147
148 impl<'a> FromIterator<&'a ValidationErrorKind> for ErrorKinds {
149 fn from_iter<T: IntoIterator<Item = &'a ValidationErrorKind>>(iter: T) -> Self {
150 Self(iter.into_iter().copied().collect())
151 }
152 }
153
154 impl ErrorKinds {
155 pub fn includes(&self, error: ValidationErrorKind) -> bool {
156 self.0.contains(&error)
157 }
158 }
159
160 impl ValidationErrors {
161 pub fn field(&self, field: &'static str) -> ErrorKinds {
162 self.filter_errors(|e| e.field().is_some_and(|f| f == field))
163 }
164
165 pub fn across_fields(&self) -> ErrorKinds {
166 self.filter_errors(|e| matches!(e, ValidationError::AcrossFields(_)))
167 }
168
169 fn filter_errors(&self, predicate: impl Fn(&ValidationError) -> bool) -> ErrorKinds {
170 self.errors()
171 .iter()
172 .filter_map(move |e| if predicate(e) { Some(e.errors()) } else { None })
173 .flatten()
174 .collect()
175 }
176 }
177}