metrique_writer_core/
validate.rs1use std::fmt;
5
6#[derive(Clone)]
13pub struct ValidationError(Vec<String>);
14
15impl ValidationError {
16 pub fn builder() -> ValidationErrorBuilder {
20 ValidationErrorBuilder::default()
21 }
22
23 pub fn extend(&mut self, other: Self) {
25 self.0.extend(other.0);
26 }
27
28 pub fn for_field(mut self, name: &str) -> Self {
30 for err in self.0.iter_mut() {
31 *err = format!("for `{name}`: {err}");
32 }
33 self
34 }
35
36 pub fn invalid(reason: impl Into<String>) -> Self {
38 Self(vec![reason.into()])
39 }
40}
41
42impl fmt::Debug for ValidationError {
43 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44 f.debug_list().entries(&self.0).finish()
45 }
46}
47
48impl fmt::Display for ValidationError {
49 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50 f.write_str(&self.0.join(", "))
51 }
52}
53
54impl std::error::Error for ValidationError {}
55
56#[derive(Debug, Clone, Default)]
60pub struct ValidationErrorBuilder(Vec<String>);
61
62impl ValidationErrorBuilder {
63 pub fn build(self) -> Result<(), ValidationError> {
66 if self.0.is_empty() {
67 Ok(())
68 } else {
69 Err(ValidationError(self.0))
70 }
71 }
72
73 pub fn invalid(mut self, reason: impl Into<String>) -> Self {
78 self.invalid_mut(reason);
79 self
80 }
81
82 pub fn invalid_mut(&mut self, reason: impl Into<String>) -> &mut Self {
84 self.0.push(reason.into());
85 self
86 }
87
88 pub fn extend(mut self, error: ValidationError) -> Self {
90 self.extend_mut(error);
91 self
92 }
93
94 pub fn extend_mut(&mut self, error: ValidationError) -> &mut Self {
96 self.0.extend(error.0);
97 self
98 }
99}
100
101#[cfg(test)]
102mod tests {
103 use crate::validate::ValidationError;
104
105 #[test]
106 fn record_invalid() {
107 assert_contains(
108 &ValidationError::invalid("custom message"),
109 "custom message",
110 );
111 assert_contains(
112 &ValidationError::builder()
113 .invalid("custom message")
114 .build()
115 .unwrap_err(),
116 "custom message",
117 );
118
119 let multiple = ValidationError::builder()
120 .invalid("first")
121 .invalid("second")
122 .build()
123 .unwrap_err();
124 assert_contains(&multiple, "first");
125 assert_contains(&multiple, "second");
126 }
127
128 #[test]
129 fn extendable() {
130 let mut extended = ValidationError::invalid("first");
131 extended.extend(ValidationError::invalid("second"));
132 assert_contains(&extended, "first");
133 assert_contains(&extended, "second");
134
135 let extended = ValidationError::builder()
136 .invalid("first")
137 .extend(ValidationError::invalid("second"))
138 .build()
139 .unwrap_err();
140 assert_contains(&extended, "first");
141 assert_contains(&extended, "second");
142 }
143
144 #[test]
145 fn add_field_context() {
146 let contextualized = ValidationError::invalid("custom message").for_field("my_field");
147 assert_contains(&contextualized, "custom message");
148 assert_contains(&contextualized, "my_field");
149 }
150
151 #[test]
152 fn build_returns_ok_if_no_errors() {
153 assert!(ValidationError::builder().build().is_ok());
154 }
155
156 fn assert_contains(error: &ValidationError, s: &str) {
157 assert!(format!("{}", error).contains(s));
158 assert!(format!("{:?}", error).contains(s));
159 }
160}