Skip to main content

config_linter/
config_linter.rs

1//! Example: using duck-diagnostic for a config file linter (YAML/TOML/JSON).
2
3use duck_diagnostic::*;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
6enum ConfigError {
7  InvalidSyntax,
8  MissingRequiredField,
9  InvalidValue,
10  DuplicateKey,
11  // warnings
12  DeprecatedField,
13  UnknownField,
14}
15
16impl DiagnosticCode for ConfigError {
17  fn code(&self) -> &str {
18    match self {
19      Self::InvalidSyntax => "CFG001",
20      Self::MissingRequiredField => "CFG002",
21      Self::InvalidValue => "CFG003",
22      Self::DuplicateKey => "CFG004",
23      Self::DeprecatedField => "CFG-W01",
24      Self::UnknownField => "CFG-W02",
25    }
26  }
27
28  fn severity(&self) -> Severity {
29    match self {
30      Self::DeprecatedField | Self::UnknownField => Severity::Warning,
31      _ => Severity::Error,
32    }
33  }
34}
35
36fn main() {
37  let config = r#"[package]
38name = "my-app"
39version = "1.0"
40edition = "2018"
41authors = ["me"]
42authors = ["you"]
43license = 42"#;
44
45  let mut engine = DiagnosticEngine::<ConfigError>::new();
46
47  engine.emit(
48    Diagnostic::new(ConfigError::DuplicateKey, "duplicate key `authors`")
49      .with_label(Label::primary(
50        Span::new("Cargo.toml", 6, 0, 7),
51        Some("second definition here".into()),
52      ))
53      .with_label(Label::secondary(
54        Span::new("Cargo.toml", 5, 0, 7),
55        Some("first defined here".into()),
56      ))
57      .with_help("remove one of the duplicate entries"),
58  );
59
60  engine.emit(
61    Diagnostic::new(ConfigError::InvalidValue, "expected string for `license`, found integer")
62      .with_label(Label::primary(
63        Span::new("Cargo.toml", 7, 10, 2),
64        Some("expected a string like \"MIT\"".into()),
65      )),
66  );
67
68  engine.emit(
69    Diagnostic::new(ConfigError::DeprecatedField, "`edition = \"2018\"` is outdated").with_label(
70      Label::primary(
71        Span::new("Cargo.toml", 4, 0, 18),
72        Some("consider updating to \"2021\" or \"2024\"".into()),
73      ),
74    ),
75  );
76
77  engine.print_all(config);
78}