duck-diagnostic 0.1.0

Generic diagnostic engine for building rich error reporting into any tool
Documentation
  • Coverage
  • 4.92%
    3 out of 61 items documented1 out of 28 items with examples
  • Size
  • Source code size: 82.13 kB This is the summed size of all the files inside the crates.io package for this release.
  • Documentation size: 4.98 MB This is the summed size of all files generated by rustdoc for all configured targets
  • Ø build duration
  • this release: 21s Average build duration of successful builds.
  • all releases: 21s Average build duration of successful builds in releases after 2024-10-23.
  • Links
  • Homepage
  • Repository
  • crates.io
  • Dependencies
  • Versions
  • Owners
  • wildduck2

Quick start

[dependencies]
duck-diagnostic = "0.1"
use duck_diagnostic::*;

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum MyError {
    SyntaxError,
    UnusedImport,
}

impl DiagnosticCode for MyError {
    fn code(&self) -> &str {
        match self {
            Self::SyntaxError  => "E0001",
            Self::UnusedImport => "W0001",
        }
    }
    fn severity(&self) -> Severity {
        match self {
            Self::SyntaxError  => Severity::Error,
            Self::UnusedImport => Severity::Warning,
        }
    }
}

fn main() {
    let source = "let x = ;";
    let mut engine = DiagnosticEngine::<MyError>::new();

    engine.emit(
        Diagnostic::new(MyError::SyntaxError, "unexpected `;`")
            .with_label(Label::primary(
                Span::new("main.lang", 1, 8, 1),
                Some("expected expression before `;`".into()),
            ))
            .with_help("try `let x = <value>;`"),
    );

    engine.print_all(source);
}

Output:

error: [E0001]: unexpected `;`
  --> main.lang:1:8
   |
 1 | let x = ;
   |         ^ expected expression before `;`
   |
   = help: try `let x = <value>;`

How it works

You define an enum with your error codes and implement DiagnosticCode on it. That's it. The engine handles collecting, counting, and rendering.

DiagnosticEngine<C>       collects diagnostics, tracks counts, renders output
  Diagnostic<C>           single error/warning with labels, notes, help
    C: DiagnosticCode     your enum
    Label                 points at source code (span + message + style)
      Span                file + line + column + length
    notes: Vec<String>
    help: Option<String>

DiagnosticCode trait

pub trait DiagnosticCode: fmt::Debug + Clone {
    fn code(&self) -> &str;
    fn severity(&self) -> Severity;
}

API

Span

Span::new(file, line, column, length)

Just a location. Not tied to any lexer or parser.

Label

Label::primary(span, message)    // ^^^^ main error site
Label::secondary(span, message)  // ---- related context

Diagnostic

Diagnostic::new(code, message)
    .with_label(label)
    .with_note(note)
    .with_help(help)

Builder methods take impl Into<String>, so both &str and String work.

DiagnosticEngine

let mut engine = DiagnosticEngine::<MyError>::new();

engine.emit(diagnostic);
engine.emit_errors(vec![...]);          // batch emit
engine.emit_warnings(vec![...]);
engine.extend(other_engine);           // merge two engines

engine.has_errors();
engine.has_warnings();
engine.error_count();
engine.warning_count();

engine.print_all(source_code);          // colored terminal output
engine.format_all(source_code);         // colored string (no print)
engine.format_all_plain(source_code);   // plain text for logs/CI

engine.get_diagnostics();               // &[Diagnostic<C>]
engine.get_errors();                    // Vec<&Diagnostic<C>>
engine.get_warnings();
engine.len();
engine.is_empty();
engine.clear();

Examples

Compiler - scanner/parser/semantic errors: examples/compiler.rs

SQL engine - unknown columns, division by zero, missing indexes: examples/sql_engine.rs

Config linter - duplicate keys, invalid values, deprecated fields: examples/config_linter.rs

API validator - missing fields, bad formats, deprecated endpoints: examples/api_validator.rs

All at once: cargo run --example demo

cargo run --example compiler
cargo run --example sql_engine
cargo run --example config_linter
cargo run --example api_validator

Contributing

See CONTRIBUTING.md for guidelines.

Security

See SECURITY.md for reporting vulnerabilities.

License

MIT - Copyright (c) 2024 @gentleduck