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