google_cloud_gax/error/
binding.rs1#[derive(thiserror::Error, Debug, PartialEq)]
33pub struct BindingError {
34 pub paths: Vec<PathMismatch>,
37}
38
39#[derive(Debug, Default, PartialEq)]
46pub struct PathMismatch {
47 pub subs: Vec<SubstitutionMismatch>,
49}
50
51#[derive(Debug, PartialEq)]
55#[allow(clippy::exhaustive_enums)]
56pub enum SubstitutionFail {
57 Unset,
59 UnsetExpecting(&'static str),
61 MismatchExpecting(String, &'static str),
68}
69
70#[derive(Debug, PartialEq)]
74pub struct SubstitutionMismatch {
75 pub field_name: &'static str,
79 pub problem: SubstitutionFail,
81}
82
83impl std::fmt::Display for SubstitutionMismatch {
84 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
85 match &self.problem {
86 SubstitutionFail::Unset => {
87 write!(f, "field `{}` needs to be set.", self.field_name)
88 }
89 SubstitutionFail::UnsetExpecting(expected) => {
90 write!(
91 f,
92 "field `{}` needs to be set and match the template: '{}'",
93 self.field_name, expected
94 )
95 }
96 SubstitutionFail::MismatchExpecting(actual, expected) => {
97 write!(
98 f,
99 "field `{}` should match the template: '{}'; found: '{}'",
100 self.field_name, expected, actual
101 )
102 }
103 }
104 }
105}
106
107impl std::fmt::Display for PathMismatch {
108 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
109 for (i, sub) in self.subs.iter().enumerate() {
110 if i != 0 {
111 write!(f, " AND ")?;
112 }
113 write!(f, "{sub}")?;
114 }
115 Ok(())
116 }
117}
118
119impl std::fmt::Display for BindingError {
120 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
121 if self.paths.len() == 1 {
122 return write!(f, "{}", self.paths[0]);
123 }
124 write!(f, "at least one of the conditions must be met: ")?;
125 for (i, sub) in self.paths.iter().enumerate() {
126 if i != 0 {
127 write!(f, " OR ")?;
128 }
129 write!(f, "({}) {}", i + 1, sub)?;
130 }
131 Ok(())
132 }
133}
134
135#[cfg(test)]
136mod tests {
137 use super::*;
138
139 #[test]
140 fn fmt_path_mismatch() {
141 let pm = PathMismatch {
142 subs: vec![
143 SubstitutionMismatch {
144 field_name: "parent",
145 problem: SubstitutionFail::MismatchExpecting(
146 "project-id-only".to_string(),
147 "projects/*",
148 ),
149 },
150 SubstitutionMismatch {
151 field_name: "location",
152 problem: SubstitutionFail::UnsetExpecting("locations/*"),
153 },
154 SubstitutionMismatch {
155 field_name: "id",
156 problem: SubstitutionFail::Unset,
157 },
158 ],
159 };
160
161 let fmt = format!("{pm}");
162 let clauses: Vec<&str> = fmt.split(" AND ").collect();
163 assert!(clauses.len() == 3, "{fmt}");
164 let c0 = clauses[0];
165 assert!(
166 c0.contains("parent")
167 && !c0.contains("needs to be set")
168 && c0.contains("should match")
169 && c0.contains("projects/*")
170 && c0.contains("found")
171 && c0.contains("project-id-only"),
172 "{c0}"
173 );
174 let c1 = clauses[1];
175 assert!(
176 c1.contains("location")
177 && c1.contains("needs to be set")
178 && c1.contains("locations/*")
179 && !c1.contains("found"),
180 "{c1}"
181 );
182 let c2 = clauses[2];
183 assert!(
184 c2.contains("id") && c2.contains("needs to be set") && !c2.contains("found"),
185 "{c2}"
186 );
187 }
188
189 #[test]
190 fn fmt_binding_error() {
191 let e = BindingError {
192 paths: vec![
193 PathMismatch {
194 subs: vec![SubstitutionMismatch {
195 field_name: "parent",
196 problem: SubstitutionFail::MismatchExpecting(
197 "project-id-only".to_string(),
198 "projects/*",
199 ),
200 }],
201 },
202 PathMismatch {
203 subs: vec![SubstitutionMismatch {
204 field_name: "location",
205 problem: SubstitutionFail::UnsetExpecting("locations/*"),
206 }],
207 },
208 PathMismatch {
209 subs: vec![SubstitutionMismatch {
210 field_name: "id",
211 problem: SubstitutionFail::Unset,
212 }],
213 },
214 ],
215 };
216 let fmt = format!("{e}");
217 assert!(fmt.contains("one of the conditions must be met"), "{fmt}");
218 let clauses: Vec<&str> = fmt.split(" OR ").collect();
219 assert!(clauses.len() == 3, "{fmt}");
220 let c0 = clauses[0];
221 assert!(
222 c0.contains("(1)")
223 && c0.contains("parent")
224 && c0.contains("should match")
225 && c0.contains("projects/*")
226 && c0.contains("project-id-only"),
227 "{c0}"
228 );
229 let c1 = clauses[1];
230 assert!(
231 c1.contains("(2)") && c1.contains("location") && c1.contains("locations/*"),
232 "{c1}"
233 );
234 let c2 = clauses[2];
235 assert!(
236 c2.contains("(3)") && c2.contains("id") && c2.contains("needs to be set"),
237 "{c2}"
238 );
239 }
240
241 #[test]
242 fn fmt_binding_error_one_path() {
243 let e = BindingError {
244 paths: vec![PathMismatch {
245 subs: vec![SubstitutionMismatch {
246 field_name: "parent",
247 problem: SubstitutionFail::MismatchExpecting(
248 "project-id-only".to_string(),
249 "projects/*",
250 ),
251 }],
252 }],
253 };
254 let fmt = format!("{e}");
255 assert!(
256 !fmt.contains("one of the conditions must be met") && !fmt.contains(" OR "),
257 "{fmt}"
258 );
259 assert!(
260 fmt.contains("parent")
261 && fmt.contains("should match")
262 && fmt.contains("projects/*")
263 && fmt.contains("project-id-only"),
264 "{fmt}"
265 );
266 }
267}