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