use crate::DiagCode;
pub const SYNTAX_NODE_LINE: DiagCode = DiagCode::new("E0101");
pub const SYNTAX_SECTION_HEADER: DiagCode = DiagCode::new("E0102");
pub const STRUCTURE_DUPLICATE_SECTION: DiagCode = DiagCode::new("E0103");
pub const STRUCTURE_MISSING_SECTION: DiagCode = DiagCode::new("E0104");
pub const CORE_UNKNOWN_ELEMENT: DiagCode = DiagCode::new("E0201");
pub const CORE_INCOMPLETE: DiagCode = DiagCode::new("E0202");
pub const CORE_DUPLICATE_ELEMENT: DiagCode = DiagCode::new("E0203");
pub const CORE_CHIP_WITHOUT_TYPE: DiagCode = DiagCode::new("E0204");
pub const CORE_UNKNOWN_CHIP: DiagCode = DiagCode::new("E0205");
pub const CORE_RUNTIME_WITHOUT_TYPE: DiagCode = DiagCode::new("E0206");
pub const CORE_UNKNOWN_RUNTIME: DiagCode = DiagCode::new("E0207");
pub const CORE_CONSOLE_WITHOUT_TYPE: DiagCode = DiagCode::new("E0208");
pub const CORE_UNKNOWN_CONSOLE: DiagCode = DiagCode::new("E0209");
pub const OPTION_UNKNOWN: DiagCode = DiagCode::new("E0301");
pub const OPTION_INVALID_VALUE: DiagCode = DiagCode::new("E0302");
pub const OPTION_DERIVED: DiagCode = DiagCode::new("E0306");
pub const OPTION_REQUIRES: DiagCode = DiagCode::new("E0307");
pub const OPTION_UNAVAILABLE: DiagCode = DiagCode::new("E0308");
pub const OPTION_MUTUALLY_EXCLUSIVE: DiagCode = DiagCode::new("E0309");
pub const OPTION_NO_CONSOLE: DiagCode = DiagCode::new("E0310");
pub const VALUE_WRONG_TYPE: DiagCode = DiagCode::new("E0311");
pub const VALUE_NOT_BOOLEAN: DiagCode = DiagCode::new("E0312");
pub const VALUE_NOT_NUMBER: DiagCode = DiagCode::new("E0313");
pub const VALUE_KEY_KIND: DiagCode = DiagCode::new("E0314");
pub const VALUE_MISSING_ATTRIBUTE: DiagCode = DiagCode::new("E0315");
pub const CLI_ANSWER: DiagCode = DiagCode::new("E0399");
pub const GRAPH_UNKNOWN_PERIPHERAL: DiagCode = DiagCode::new("E0401");
pub const GRAPH_UNKNOWN_COMPONENT: DiagCode = DiagCode::new("E0402");
pub const GRAPH_PERIPHERAL_WITHOUT_ID: DiagCode = DiagCode::new("E0403");
pub const GRAPH_COMPONENT_WITHOUT_ID: DiagCode = DiagCode::new("E0404");
pub const GRAPH_COMPONENT_WITHOUT_LINK: DiagCode = DiagCode::new("E0405");
pub const GRAPH_DEVICE_WITHOUT_ID: DiagCode = DiagCode::new("E0406");
pub const GRAPH_DEVICE_WITHOUT_MAPPING: DiagCode = DiagCode::new("E0407");
pub const GRAPH_INVALID_PIN: DiagCode = DiagCode::new("E0408");
pub const PERIPHERAL_UNSUPPORTED: DiagCode = DiagCode::new("E0501");
pub const GPIO_INVALID_PIN: DiagCode = DiagCode::new("E0502");
pub const GPIO_INVALID_DIRECTION: DiagCode = DiagCode::new("E0503");
pub const I2C_INVALID_ID: DiagCode = DiagCode::new("E0504");
pub const I2C_INVALID_PIN: DiagCode = DiagCode::new("E0505");
pub const I2C_INVALID_FREQUENCY: DiagCode = DiagCode::new("E0506");
pub const SPI_INVALID_ID: DiagCode = DiagCode::new("E0507");
pub const SPI_INVALID_PIN: DiagCode = DiagCode::new("E0508");
pub const SPI_INVALID_MODE: DiagCode = DiagCode::new("E0509");
pub const SPI_INVALID_FREQUENCY: DiagCode = DiagCode::new("E0510");
pub const UART_INVALID_ID: DiagCode = DiagCode::new("E0511");
pub const UART_INVALID_PERIPHERAL: DiagCode = DiagCode::new("E0512");
pub const UART_INVALID_PIN: DiagCode = DiagCode::new("E0513");
pub const COMPONENT_INVALID_LED: DiagCode = DiagCode::new("E0601");
pub const COMPONENT_INVALID_BUTTON: DiagCode = DiagCode::new("E0602");
pub const COMPONENT_INVALID_I2C: DiagCode = DiagCode::new("E0603");
pub const COMPONENT_INVALID_SPI: DiagCode = DiagCode::new("E0604");
pub const COMPONENT_INVALID_UART: DiagCode = DiagCode::new("E0605");
pub const COMPONENT_UNSUPPORTED: DiagCode = DiagCode::new("E0606");
pub const COMPONENT_INVALID_CS: DiagCode = DiagCode::new("E0607");
pub const COMPONENT_UNKNOWN_GPIO: DiagCode = DiagCode::new("E0608");
pub const COMPONENT_WRONG_DIRECTION: DiagCode = DiagCode::new("E0609");
pub const COMPONENT_INVALID_BAUD: DiagCode = DiagCode::new("E0610");
pub const COMPONENT_I2C_BUS_USED: DiagCode = DiagCode::new("E0611");
pub const COMPONENT_UART_USED: DiagCode = DiagCode::new("E0612");
pub const COMPONENT_UNKNOWN_BUS: DiagCode = DiagCode::new("E0613");
pub const COMPONENT_INVALID_ID: DiagCode = DiagCode::new("E0614");
pub const CONFLICT_PIN: DiagCode = DiagCode::new("E0701");
pub const CONFLICT_PIN_USED: DiagCode = DiagCode::new("E0702");
pub const CONFLICT_DIRECTION: DiagCode = DiagCode::new("E0703");
pub const CONFLICT_PERIPHERAL: DiagCode = DiagCode::new("E0704");
pub const CONFLICT_I2C_WIRING: DiagCode = DiagCode::new("E0705");
pub const CONFLICT_SPI_WIRING: DiagCode = DiagCode::new("E0706");
pub const CONFLICT_UART_WIRING: DiagCode = DiagCode::new("E0707");
pub const SERVICE_UNSUPPORTED_DEVICES: DiagCode = DiagCode::new("E0801");
pub const SERVICE_UNSUPPORTED: DiagCode = DiagCode::new("E0802");
pub const SERVICE_UNSUPPORTED_FIELD: DiagCode = DiagCode::new("E0803");
pub const SERVICE_MISSING_SSID: DiagCode = DiagCode::new("E0804");
pub const SERVICE_INVALID_SSID: DiagCode = DiagCode::new("E0805");
pub const SERVICE_INVALID_PASSWORD: DiagCode = DiagCode::new("E0806");
pub const SERVICE_INVALID_VALUE: DiagCode = DiagCode::new("E0807");
pub const SERVICE_DUPLICATE_WIFI: DiagCode = DiagCode::new("E0808");
pub const DRIVER_UNKNOWN: DiagCode = DiagCode::new("E0809");
pub const UNCLASSIFIED: DiagCode = DiagCode::new("E0999");
pub fn for_cli_message(message: &str) -> DiagCode {
if message.starts_with("unknown chip") {
return CORE_UNKNOWN_CHIP;
}
if message.starts_with("unknown option") {
return OPTION_UNKNOWN;
}
if message.starts_with("--option") || message.contains(" --option ") {
return OPTION_INVALID_VALUE;
}
CLI_ANSWER
}
pub fn for_message(message: &str) -> DiagCode {
let exact = match message {
"malformed node line" => SYNTAX_NODE_LINE,
"invalid section header" => SYNTAX_SECTION_HEADER,
"# espforge is incomplete" => CORE_INCOMPLETE,
"<chip> is missing its type" => CORE_CHIP_WITHOUT_TYPE,
"unknown chip" => CORE_UNKNOWN_CHIP,
"<runtime> is missing its type" => CORE_RUNTIME_WITHOUT_TYPE,
"unknown runtime" => CORE_UNKNOWN_RUNTIME,
"<console> is missing its type" => CORE_CONSOLE_WITHOUT_TYPE,
"unknown console" => CORE_UNKNOWN_CONSOLE,
"unknown peripheral" => GRAPH_UNKNOWN_PERIPHERAL,
"unknown component" => GRAPH_UNKNOWN_COMPONENT,
"peripheral is missing an id" => GRAPH_PERIPHERAL_WITHOUT_ID,
"component is missing an id" => GRAPH_COMPONENT_WITHOUT_ID,
"component is missing its controller link" => GRAPH_COMPONENT_WITHOUT_LINK,
"device is missing an id" => GRAPH_DEVICE_WITHOUT_ID,
"device is missing its component mapping" => GRAPH_DEVICE_WITHOUT_MAPPING,
"invalid pin number" => GRAPH_INVALID_PIN,
"unsupported peripheral" => PERIPHERAL_UNSUPPORTED,
"invalid gpio pin" => GPIO_INVALID_PIN,
"invalid gpio direction" => GPIO_INVALID_DIRECTION,
"invalid i2c id" => I2C_INVALID_ID,
"invalid i2c pin" => I2C_INVALID_PIN,
"invalid i2c frequency" => I2C_INVALID_FREQUENCY,
"invalid spi id" => SPI_INVALID_ID,
"invalid spi pin" => SPI_INVALID_PIN,
"invalid spi mode" => SPI_INVALID_MODE,
"invalid spi frequency" => SPI_INVALID_FREQUENCY,
"invalid uart id" => UART_INVALID_ID,
"invalid uart peripheral" => UART_INVALID_PERIPHERAL,
"invalid uart pin" => UART_INVALID_PIN,
"invalid led" => COMPONENT_INVALID_LED,
"invalid button" => COMPONENT_INVALID_BUTTON,
"invalid i2c_component" => COMPONENT_INVALID_I2C,
"invalid spi_component" => COMPONENT_INVALID_SPI,
"invalid uart_component" => COMPONENT_INVALID_UART,
"unsupported component" => COMPONENT_UNSUPPORTED,
"invalid cs" => COMPONENT_INVALID_CS,
"unknown gpio" => COMPONENT_UNKNOWN_GPIO,
"wrong gpio direction" => COMPONENT_WRONG_DIRECTION,
"invalid baud_rate" => COMPONENT_INVALID_BAUD,
"i2c bus already used" => COMPONENT_I2C_BUS_USED,
"uart already used" => COMPONENT_UART_USED,
"pin conflict" => CONFLICT_PIN,
"pin already used" => CONFLICT_PIN_USED,
"pin direction clash" => CONFLICT_DIRECTION,
"peripheral clash" => CONFLICT_PERIPHERAL,
"i2c wiring" => CONFLICT_I2C_WIRING,
"spi wiring" => CONFLICT_SPI_WIRING,
"uart wiring" => CONFLICT_UART_WIRING,
"unsupported devices" => SERVICE_UNSUPPORTED_DEVICES,
"unsupported service" => SERVICE_UNSUPPORTED,
"unsupported wifi field" => SERVICE_UNSUPPORTED_FIELD,
"missing wifi ssid" => SERVICE_MISSING_SSID,
"invalid wifi ssid" => SERVICE_INVALID_SSID,
"invalid wifi password" => SERVICE_INVALID_PASSWORD,
"invalid wifi value" => SERVICE_INVALID_VALUE,
"duplicate wifi" => SERVICE_DUPLICATE_WIFI,
_ => return dynamic_code(message),
};
exact
}
fn dynamic_code(message: &str) -> DiagCode {
if message.starts_with("duplicate section `") {
return STRUCTURE_DUPLICATE_SECTION;
}
if message.starts_with("missing section `") {
return STRUCTURE_MISSING_SECTION;
}
if message.starts_with("unknown element <") {
return CORE_UNKNOWN_ELEMENT;
}
if message.starts_with("duplicate <") {
return CORE_DUPLICATE_ELEMENT;
}
if message.starts_with("unknown option `") {
return OPTION_UNKNOWN;
}
if message.ends_with("has an invalid value") && message.starts_with('`') {
return OPTION_INVALID_VALUE;
}
if message.ends_with("has the wrong type") && message.starts_with('`') {
return VALUE_WRONG_TYPE;
}
if message.ends_with("is not a boolean") && message.starts_with('`') {
return VALUE_NOT_BOOLEAN;
}
if message.ends_with("is not a number") && message.starts_with('`') {
return VALUE_NOT_NUMBER;
}
if message.ends_with("and an object") && message.contains("is both a") {
return VALUE_KEY_KIND;
}
if message.starts_with("missing required attribute `") {
return VALUE_MISSING_ATTRIBUTE;
}
if message.ends_with("cannot be set in <options>") && message.starts_with('`') {
return OPTION_DERIVED;
}
if message.ends_with("is not available for") || message.contains("is not available for ") {
return OPTION_UNAVAILABLE;
}
if message.contains(" requires `") && message.starts_with('`') {
return OPTION_REQUIRES;
}
if message.starts_with("options ") && message.ends_with("are mutually exclusive") {
return OPTION_MUTUALLY_EXCLUSIVE;
}
if message.ends_with("has no USB-Serial-JTAG") {
return OPTION_NO_CONSOLE;
}
if message.starts_with("unknown ") && message.ends_with(" bus") {
return COMPONENT_UNKNOWN_BUS;
}
if message.starts_with("invalid ") && message.ends_with("") {
if message == "invalid gpio" || message == "invalid i2c" || message == "invalid spi" {
return COMPONENT_INVALID_ID;
}
}
UNCLASSIFIED
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn every_known_title_has_its_own_code() {
let titles = [
"malformed node line",
"invalid section header",
"# espforge is incomplete",
"<chip> is missing its type",
"unknown chip",
"<runtime> is missing its type",
"unknown runtime",
"<console> is missing its type",
"unknown console",
"unknown option `wombat`",
"`alloc` has an invalid value",
"`alloc` cannot be set in <options>",
"`wifi` requires `alloc`",
"`wifi` is not available for esp32c3",
"options `a`, `b` are mutually exclusive",
"esp32c3 has no USB-Serial-JTAG",
"`network1.ssid` has the wrong type",
"`x` is not a boolean",
"`x` is not a number",
"`x` is both a string and an object",
"missing required attribute `pin`",
"unknown peripheral",
"unknown component",
"peripheral is missing an id",
"component is missing an id",
"component is missing its controller link",
"device is missing an id",
"device is missing its component mapping",
"invalid pin number",
"unsupported peripheral",
"invalid gpio pin",
"invalid gpio direction",
"invalid i2c id",
"invalid i2c pin",
"invalid i2c frequency",
"invalid spi id",
"invalid spi pin",
"invalid spi mode",
"invalid spi frequency",
"invalid uart id",
"invalid uart peripheral",
"invalid uart pin",
"invalid led",
"invalid button",
"invalid i2c_component",
"invalid spi_component",
"invalid uart_component",
"unsupported component",
"invalid cs",
"unknown gpio",
"wrong gpio direction",
"invalid baud_rate",
"i2c bus already used",
"uart already used",
"unknown i2c bus",
"pin conflict",
"pin already used",
"pin direction clash",
"peripheral clash",
"i2c wiring",
"spi wiring",
"uart wiring",
"unsupported devices",
"unsupported service",
"unsupported wifi field",
"missing wifi ssid",
"invalid wifi ssid",
"invalid wifi password",
"invalid wifi value",
"duplicate wifi",
"duplicate section `# peripherals`",
"missing section `# services`",
"unknown element <pwm> in # espforge",
"duplicate <chip> in # espforge",
];
let mut seen = std::collections::BTreeSet::new();
for title in titles {
let code = for_message(title);
assert_ne!(code, UNCLASSIFIED, "{title} fell through");
assert!(
seen.insert(code.as_str()),
"{title} reuses code {}",
code.as_str()
);
}
}
#[test]
fn an_unknown_title_is_not_silently_given_a_real_code() {
assert_eq!(for_message("a message nobody wrote"), UNCLASSIFIED);
}
}