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("required at least one field")]
49 NoFieldProvided,
50
51 #[error("rich text block should have exactly one element")]
53 RichTextSingleElement,
54}
55
56#[derive(Debug, Clone, PartialEq, Error)]
62pub enum ValidationError {
63 #[error("AcrossFieldsError {0:?}")]
65 AcrossFields(Vec<ValidationErrorKind>),
66
67 #[error("SingleField {{ field: {field:}, errors: {errors:?} }}")]
69 SingleField {
70 field: Cow<'static, str>,
71 errors: Vec<ValidationErrorKind>,
72 },
73}
74
75impl ValidationError {
76 pub(crate) fn new_across_fields(inner: Vec<ValidationErrorKind>) -> Option<Self> {
77 if inner.is_empty() {
78 None
79 } else {
80 Some(Self::AcrossFields(inner))
81 }
82 }
83
84 pub(crate) fn new_single_field(
85 field: &'static str,
86 inner: Vec<ValidationErrorKind>,
87 ) -> Option<Self> {
88 if inner.is_empty() {
89 None
90 } else {
91 Some(Self::SingleField {
92 field: Cow::Borrowed(field),
93 errors: inner,
94 })
95 }
96 }
97
98 pub fn field(&self) -> Option<&str> {
100 if let Self::SingleField { field, .. } = self {
101 Some(field)
102 } else {
103 None
104 }
105 }
106
107 pub fn errors(&self) -> &[ValidationErrorKind] {
109 match self {
110 Self::AcrossFields(errors) => errors,
111 Self::SingleField { errors, .. } => errors,
112 }
113 }
114}
115
116#[derive(Debug, Clone, PartialEq, Error)]
119#[error("Validation Error {{ object: {object:}, errors: {errors:?} }}")]
120pub struct ValidationErrors {
121 pub object: Cow<'static, str>,
123 pub errors: Vec<ValidationError>,
125}
126
127impl ValidationErrors {
128 pub fn object(&self) -> &str {
130 &self.object
131 }
132
133 pub fn errors(&self) -> &[ValidationError] {
135 &self.errors
136 }
137}
138
139#[cfg(test)]
140mod test_helpers {
141 use super::*;
142
143 pub struct ErrorKinds(Vec<ValidationErrorKind>);
144
145 impl<'a> FromIterator<&'a ValidationErrorKind> for ErrorKinds {
146 fn from_iter<T: IntoIterator<Item = &'a ValidationErrorKind>>(iter: T) -> Self {
147 Self(iter.into_iter().copied().collect())
148 }
149 }
150
151 impl ErrorKinds {
152 pub fn includes(&self, error: ValidationErrorKind) -> bool {
153 self.0.contains(&error)
154 }
155 }
156
157 impl ValidationErrors {
158 pub fn field(&self, field: &'static str) -> ErrorKinds {
159 self.filter_errors(|e| e.field().is_some_and(|f| f == field))
160 }
161
162 pub fn across_fields(&self) -> ErrorKinds {
163 self.filter_errors(|e| matches!(e, ValidationError::AcrossFields(_)))
164 }
165
166 fn filter_errors(&self, predicate: impl Fn(&ValidationError) -> bool) -> ErrorKinds {
167 self.errors()
168 .iter()
169 .filter_map(move |e| if predicate(e) { Some(e.errors()) } else { None })
170 .flatten()
171 .collect()
172 }
173 }
174}