1use crate::DiagCode;
13
14pub const SYNTAX_NODE_LINE: DiagCode = DiagCode::new("E0101");
16pub const SYNTAX_SECTION_HEADER: DiagCode = DiagCode::new("E0102");
18pub const STRUCTURE_DUPLICATE_SECTION: DiagCode = DiagCode::new("E0103");
20pub const STRUCTURE_MISSING_SECTION: DiagCode = DiagCode::new("E0104");
22
23pub const CORE_UNKNOWN_ELEMENT: DiagCode = DiagCode::new("E0201");
25pub const CORE_INCOMPLETE: DiagCode = DiagCode::new("E0202");
27pub const CORE_DUPLICATE_ELEMENT: DiagCode = DiagCode::new("E0203");
29pub const CORE_CHIP_WITHOUT_TYPE: DiagCode = DiagCode::new("E0204");
31pub const CORE_UNKNOWN_CHIP: DiagCode = DiagCode::new("E0205");
33pub const CORE_RUNTIME_WITHOUT_TYPE: DiagCode = DiagCode::new("E0206");
35pub const CORE_UNKNOWN_RUNTIME: DiagCode = DiagCode::new("E0207");
37pub const CORE_CONSOLE_WITHOUT_TYPE: DiagCode = DiagCode::new("E0208");
39pub const CORE_UNKNOWN_CONSOLE: DiagCode = DiagCode::new("E0209");
41
42pub const OPTION_UNKNOWN: DiagCode = DiagCode::new("E0301");
44pub const OPTION_INVALID_VALUE: DiagCode = DiagCode::new("E0302");
46pub const OPTION_DERIVED: DiagCode = DiagCode::new("E0306");
48pub const OPTION_REQUIRES: DiagCode = DiagCode::new("E0307");
50pub const OPTION_UNAVAILABLE: DiagCode = DiagCode::new("E0308");
52pub const OPTION_MUTUALLY_EXCLUSIVE: DiagCode = DiagCode::new("E0309");
54pub const OPTION_NO_CONSOLE: DiagCode = DiagCode::new("E0310");
56pub const VALUE_WRONG_TYPE: DiagCode = DiagCode::new("E0311");
58pub const VALUE_NOT_BOOLEAN: DiagCode = DiagCode::new("E0312");
60pub const VALUE_NOT_NUMBER: DiagCode = DiagCode::new("E0313");
62pub const VALUE_KEY_KIND: DiagCode = DiagCode::new("E0314");
64pub const VALUE_MISSING_ATTRIBUTE: DiagCode = DiagCode::new("E0315");
66pub const CLI_ANSWER: DiagCode = DiagCode::new("E0399");
68
69pub const GRAPH_UNKNOWN_PERIPHERAL: DiagCode = DiagCode::new("E0401");
71pub const GRAPH_UNKNOWN_COMPONENT: DiagCode = DiagCode::new("E0402");
73pub const GRAPH_PERIPHERAL_WITHOUT_ID: DiagCode = DiagCode::new("E0403");
75pub const GRAPH_COMPONENT_WITHOUT_ID: DiagCode = DiagCode::new("E0404");
77pub const GRAPH_COMPONENT_WITHOUT_LINK: DiagCode = DiagCode::new("E0405");
79pub const GRAPH_DEVICE_WITHOUT_ID: DiagCode = DiagCode::new("E0406");
81pub const GRAPH_DEVICE_WITHOUT_MAPPING: DiagCode = DiagCode::new("E0407");
83pub const GRAPH_INVALID_PIN: DiagCode = DiagCode::new("E0408");
85
86pub const PERIPHERAL_UNSUPPORTED: DiagCode = DiagCode::new("E0501");
88pub const GPIO_INVALID_PIN: DiagCode = DiagCode::new("E0502");
90pub const GPIO_INVALID_DIRECTION: DiagCode = DiagCode::new("E0503");
92pub const I2C_INVALID_ID: DiagCode = DiagCode::new("E0504");
94pub const I2C_INVALID_PIN: DiagCode = DiagCode::new("E0505");
96pub const I2C_INVALID_FREQUENCY: DiagCode = DiagCode::new("E0506");
98pub const SPI_INVALID_ID: DiagCode = DiagCode::new("E0507");
100pub const SPI_INVALID_PIN: DiagCode = DiagCode::new("E0508");
102pub const SPI_INVALID_MODE: DiagCode = DiagCode::new("E0509");
104pub const SPI_INVALID_FREQUENCY: DiagCode = DiagCode::new("E0510");
106pub const UART_INVALID_ID: DiagCode = DiagCode::new("E0511");
108pub const UART_INVALID_PERIPHERAL: DiagCode = DiagCode::new("E0512");
110pub const UART_INVALID_PIN: DiagCode = DiagCode::new("E0513");
112
113pub const COMPONENT_INVALID_LED: DiagCode = DiagCode::new("E0601");
115pub const COMPONENT_INVALID_BUTTON: DiagCode = DiagCode::new("E0602");
117pub const COMPONENT_INVALID_I2C: DiagCode = DiagCode::new("E0603");
119pub const COMPONENT_INVALID_SPI: DiagCode = DiagCode::new("E0604");
121pub const COMPONENT_INVALID_UART: DiagCode = DiagCode::new("E0605");
123pub const COMPONENT_UNSUPPORTED: DiagCode = DiagCode::new("E0606");
125pub const COMPONENT_INVALID_CS: DiagCode = DiagCode::new("E0607");
127pub const COMPONENT_UNKNOWN_GPIO: DiagCode = DiagCode::new("E0608");
129pub const COMPONENT_WRONG_DIRECTION: DiagCode = DiagCode::new("E0609");
131pub const COMPONENT_INVALID_BAUD: DiagCode = DiagCode::new("E0610");
133pub const COMPONENT_I2C_BUS_USED: DiagCode = DiagCode::new("E0611");
135pub const COMPONENT_UART_USED: DiagCode = DiagCode::new("E0612");
137pub const COMPONENT_UNKNOWN_BUS: DiagCode = DiagCode::new("E0613");
139pub const COMPONENT_INVALID_ID: DiagCode = DiagCode::new("E0614");
141
142pub const CONFLICT_PIN: DiagCode = DiagCode::new("E0701");
144pub const CONFLICT_PIN_USED: DiagCode = DiagCode::new("E0702");
146pub const CONFLICT_DIRECTION: DiagCode = DiagCode::new("E0703");
148pub const CONFLICT_PERIPHERAL: DiagCode = DiagCode::new("E0704");
150pub const CONFLICT_I2C_WIRING: DiagCode = DiagCode::new("E0705");
152pub const CONFLICT_SPI_WIRING: DiagCode = DiagCode::new("E0706");
154pub const CONFLICT_UART_WIRING: DiagCode = DiagCode::new("E0707");
156
157pub const SERVICE_UNSUPPORTED_DEVICES: DiagCode = DiagCode::new("E0801");
159pub const SERVICE_UNSUPPORTED: DiagCode = DiagCode::new("E0802");
161pub const SERVICE_UNSUPPORTED_FIELD: DiagCode = DiagCode::new("E0803");
163pub const SERVICE_MISSING_SSID: DiagCode = DiagCode::new("E0804");
165pub const SERVICE_INVALID_SSID: DiagCode = DiagCode::new("E0805");
167pub const SERVICE_INVALID_PASSWORD: DiagCode = DiagCode::new("E0806");
169pub const SERVICE_INVALID_VALUE: DiagCode = DiagCode::new("E0807");
171pub const SERVICE_DUPLICATE_WIFI: DiagCode = DiagCode::new("E0808");
173pub const DRIVER_UNKNOWN: DiagCode = DiagCode::new("E0809");
175
176pub const UNCLASSIFIED: DiagCode = DiagCode::new("E0999");
181
182pub fn for_cli_message(message: &str) -> DiagCode {
187 if message.starts_with("unknown chip") {
188 return CORE_UNKNOWN_CHIP;
189 }
190 if message.starts_with("unknown option") {
191 return OPTION_UNKNOWN;
192 }
193 if message.starts_with("--option") || message.contains(" --option ") {
194 return OPTION_INVALID_VALUE;
195 }
196 CLI_ANSWER
197}
198
199pub fn for_message(message: &str) -> DiagCode {
204 let exact = match message {
207 "malformed node line" => SYNTAX_NODE_LINE,
208 "invalid section header" => SYNTAX_SECTION_HEADER,
209 "# espforge is incomplete" => CORE_INCOMPLETE,
210 "<chip> is missing its type" => CORE_CHIP_WITHOUT_TYPE,
211 "unknown chip" => CORE_UNKNOWN_CHIP,
212 "<runtime> is missing its type" => CORE_RUNTIME_WITHOUT_TYPE,
213 "unknown runtime" => CORE_UNKNOWN_RUNTIME,
214 "<console> is missing its type" => CORE_CONSOLE_WITHOUT_TYPE,
215 "unknown console" => CORE_UNKNOWN_CONSOLE,
216 "unknown peripheral" => GRAPH_UNKNOWN_PERIPHERAL,
217 "unknown component" => GRAPH_UNKNOWN_COMPONENT,
218 "peripheral is missing an id" => GRAPH_PERIPHERAL_WITHOUT_ID,
219 "component is missing an id" => GRAPH_COMPONENT_WITHOUT_ID,
220 "component is missing its controller link" => GRAPH_COMPONENT_WITHOUT_LINK,
221 "device is missing an id" => GRAPH_DEVICE_WITHOUT_ID,
222 "device is missing its component mapping" => GRAPH_DEVICE_WITHOUT_MAPPING,
223 "invalid pin number" => GRAPH_INVALID_PIN,
224 "unsupported peripheral" => PERIPHERAL_UNSUPPORTED,
225 "invalid gpio pin" => GPIO_INVALID_PIN,
226 "invalid gpio direction" => GPIO_INVALID_DIRECTION,
227 "invalid i2c id" => I2C_INVALID_ID,
228 "invalid i2c pin" => I2C_INVALID_PIN,
229 "invalid i2c frequency" => I2C_INVALID_FREQUENCY,
230 "invalid spi id" => SPI_INVALID_ID,
231 "invalid spi pin" => SPI_INVALID_PIN,
232 "invalid spi mode" => SPI_INVALID_MODE,
233 "invalid spi frequency" => SPI_INVALID_FREQUENCY,
234 "invalid uart id" => UART_INVALID_ID,
235 "invalid uart peripheral" => UART_INVALID_PERIPHERAL,
236 "invalid uart pin" => UART_INVALID_PIN,
237 "invalid led" => COMPONENT_INVALID_LED,
238 "invalid button" => COMPONENT_INVALID_BUTTON,
239 "invalid i2c_component" => COMPONENT_INVALID_I2C,
240 "invalid spi_component" => COMPONENT_INVALID_SPI,
241 "invalid uart_component" => COMPONENT_INVALID_UART,
242 "unsupported component" => COMPONENT_UNSUPPORTED,
243 "invalid cs" => COMPONENT_INVALID_CS,
244 "unknown gpio" => COMPONENT_UNKNOWN_GPIO,
245 "wrong gpio direction" => COMPONENT_WRONG_DIRECTION,
246 "invalid baud_rate" => COMPONENT_INVALID_BAUD,
247 "i2c bus already used" => COMPONENT_I2C_BUS_USED,
248 "uart already used" => COMPONENT_UART_USED,
249 "pin conflict" => CONFLICT_PIN,
250 "pin already used" => CONFLICT_PIN_USED,
251 "pin direction clash" => CONFLICT_DIRECTION,
252 "peripheral clash" => CONFLICT_PERIPHERAL,
253 "i2c wiring" => CONFLICT_I2C_WIRING,
254 "spi wiring" => CONFLICT_SPI_WIRING,
255 "uart wiring" => CONFLICT_UART_WIRING,
256 "unsupported devices" => SERVICE_UNSUPPORTED_DEVICES,
257 "unsupported service" => SERVICE_UNSUPPORTED,
258 "unsupported wifi field" => SERVICE_UNSUPPORTED_FIELD,
259 "missing wifi ssid" => SERVICE_MISSING_SSID,
260 "invalid wifi ssid" => SERVICE_INVALID_SSID,
261 "invalid wifi password" => SERVICE_INVALID_PASSWORD,
262 "invalid wifi value" => SERVICE_INVALID_VALUE,
263 "duplicate wifi" => SERVICE_DUPLICATE_WIFI,
264 _ => return dynamic_code(message),
265 };
266 exact
267}
268
269fn dynamic_code(message: &str) -> DiagCode {
271 if message.starts_with("duplicate section `") {
272 return STRUCTURE_DUPLICATE_SECTION;
273 }
274 if message.starts_with("missing section `") {
275 return STRUCTURE_MISSING_SECTION;
276 }
277 if message.starts_with("unknown element <") {
278 return CORE_UNKNOWN_ELEMENT;
279 }
280 if message.starts_with("duplicate <") {
281 return CORE_DUPLICATE_ELEMENT;
282 }
283 if message.starts_with("unknown option `") {
284 return OPTION_UNKNOWN;
285 }
286 if message.ends_with("has an invalid value") && message.starts_with('`') {
287 return OPTION_INVALID_VALUE;
288 }
289 if message.ends_with("has the wrong type") && message.starts_with('`') {
290 return VALUE_WRONG_TYPE;
291 }
292 if message.ends_with("is not a boolean") && message.starts_with('`') {
293 return VALUE_NOT_BOOLEAN;
294 }
295 if message.ends_with("is not a number") && message.starts_with('`') {
296 return VALUE_NOT_NUMBER;
297 }
298 if message.ends_with("and an object") && message.contains("is both a") {
299 return VALUE_KEY_KIND;
300 }
301 if message.starts_with("missing required attribute `") {
302 return VALUE_MISSING_ATTRIBUTE;
303 }
304 if message.ends_with("cannot be set in <options>") && message.starts_with('`') {
305 return OPTION_DERIVED;
306 }
307 if message.ends_with("is not available for") || message.contains("is not available for ") {
308 return OPTION_UNAVAILABLE;
309 }
310 if message.contains(" requires `") && message.starts_with('`') {
311 return OPTION_REQUIRES;
312 }
313 if message.starts_with("options ") && message.ends_with("are mutually exclusive") {
314 return OPTION_MUTUALLY_EXCLUSIVE;
315 }
316 if message.ends_with("has no USB-Serial-JTAG") {
317 return OPTION_NO_CONSOLE;
318 }
319 if message.starts_with("unknown ") && message.ends_with(" bus") {
320 return COMPONENT_UNKNOWN_BUS;
321 }
322 if message.starts_with("invalid ") && message.ends_with("") {
323 if message == "invalid gpio" || message == "invalid i2c" || message == "invalid spi" {
326 return COMPONENT_INVALID_ID;
327 }
328 }
329 UNCLASSIFIED
330}
331
332#[cfg(test)]
333mod tests {
334 use super::*;
335
336 #[test]
337 fn every_known_title_has_its_own_code() {
338 let titles = [
339 "malformed node line",
340 "invalid section header",
341 "# espforge is incomplete",
342 "<chip> is missing its type",
343 "unknown chip",
344 "<runtime> is missing its type",
345 "unknown runtime",
346 "<console> is missing its type",
347 "unknown console",
348 "unknown option `wombat`",
349 "`alloc` has an invalid value",
350 "`alloc` cannot be set in <options>",
351 "`wifi` requires `alloc`",
352 "`wifi` is not available for esp32c3",
353 "options `a`, `b` are mutually exclusive",
354 "esp32c3 has no USB-Serial-JTAG",
355 "`network1.ssid` has the wrong type",
356 "`x` is not a boolean",
357 "`x` is not a number",
358 "`x` is both a string and an object",
359 "missing required attribute `pin`",
360 "unknown peripheral",
361 "unknown component",
362 "peripheral is missing an id",
363 "component is missing an id",
364 "component is missing its controller link",
365 "device is missing an id",
366 "device is missing its component mapping",
367 "invalid pin number",
368 "unsupported peripheral",
369 "invalid gpio pin",
370 "invalid gpio direction",
371 "invalid i2c id",
372 "invalid i2c pin",
373 "invalid i2c frequency",
374 "invalid spi id",
375 "invalid spi pin",
376 "invalid spi mode",
377 "invalid spi frequency",
378 "invalid uart id",
379 "invalid uart peripheral",
380 "invalid uart pin",
381 "invalid led",
382 "invalid button",
383 "invalid i2c_component",
384 "invalid spi_component",
385 "invalid uart_component",
386 "unsupported component",
387 "invalid cs",
388 "unknown gpio",
389 "wrong gpio direction",
390 "invalid baud_rate",
391 "i2c bus already used",
392 "uart already used",
393 "unknown i2c bus",
394 "pin conflict",
395 "pin already used",
396 "pin direction clash",
397 "peripheral clash",
398 "i2c wiring",
399 "spi wiring",
400 "uart wiring",
401 "unsupported devices",
402 "unsupported service",
403 "unsupported wifi field",
404 "missing wifi ssid",
405 "invalid wifi ssid",
406 "invalid wifi password",
407 "invalid wifi value",
408 "duplicate wifi",
409 "duplicate section `# peripherals`",
410 "missing section `# services`",
411 "unknown element <pwm> in # espforge",
412 "duplicate <chip> in # espforge",
413 ];
414
415 let mut seen = std::collections::BTreeSet::new();
416 for title in titles {
417 let code = for_message(title);
418 assert_ne!(code, UNCLASSIFIED, "{title} fell through");
419 assert!(
420 seen.insert(code.as_str()),
421 "{title} reuses code {}",
422 code.as_str()
423 );
424 }
425 }
426
427 #[test]
428 fn an_unknown_title_is_not_silently_given_a_real_code() {
429 assert_eq!(for_message("a message nobody wrote"), UNCLASSIFIED);
430 }
431}