Skip to main content

duck_diagnostic/
diagnostic.rs

1use std::fmt;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4pub enum Severity {
5  Error,
6  Warning,
7  Note,
8  Help,
9}
10
11/// Implement this on your error enum to plug into the diagnostic system.
12///
13/// ```rust
14/// use duck_diagnostic::{DiagnosticCode, Severity};
15///
16/// #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
17/// enum MyError {
18///     SyntaxError,
19///     UnusedImport,
20/// }
21///
22/// impl DiagnosticCode for MyError {
23///     fn code(&self) -> &str {
24///         match self {
25///             Self::SyntaxError  => "E0001",
26///             Self::UnusedImport => "W0001",
27///         }
28///     }
29///     fn severity(&self) -> Severity {
30///         match self {
31///             Self::SyntaxError  => Severity::Error,
32///             Self::UnusedImport => Severity::Warning,
33///         }
34///     }
35/// }
36/// ```
37pub trait DiagnosticCode: fmt::Debug + Clone {
38  fn code(&self) -> &str;
39  fn severity(&self) -> Severity;
40}
41
42#[derive(Debug, Clone, PartialEq, Eq)]
43pub struct Span {
44  pub file: String,
45  pub line: usize,
46  pub column: usize,
47  pub length: usize,
48}
49
50impl Span {
51  pub fn new(file: impl Into<String>, line: usize, column: usize, length: usize) -> Self {
52    Self { file: file.into(), line, column, length }
53  }
54}
55
56#[derive(Debug, Clone, Copy, PartialEq, Eq)]
57pub enum LabelStyle {
58  Primary,
59  Secondary,
60}
61
62#[derive(Debug, Clone)]
63pub struct Label {
64  pub span: Span,
65  pub message: Option<String>,
66  pub style: LabelStyle,
67}
68
69impl Label {
70  pub fn primary(span: Span, message: impl Into<Option<String>>) -> Self {
71    Self { span, message: message.into(), style: LabelStyle::Primary }
72  }
73
74  pub fn secondary(span: Span, message: impl Into<Option<String>>) -> Self {
75    Self { span, message: message.into(), style: LabelStyle::Secondary }
76  }
77}
78
79#[derive(Debug, Clone)]
80pub struct Diagnostic<C: DiagnosticCode> {
81  pub code: C,
82  pub severity: Severity,
83  pub message: String,
84  pub labels: Vec<Label>,
85  pub notes: Vec<String>,
86  pub help: Option<String>,
87}
88
89impl<C: DiagnosticCode> Diagnostic<C> {
90  pub fn new(code: C, message: impl Into<String>) -> Self {
91    let severity = code.severity();
92    Self {
93      code,
94      severity,
95      message: message.into(),
96      labels: Vec::new(),
97      notes: Vec::new(),
98      help: None,
99    }
100  }
101
102  pub fn with_label(mut self, label: Label) -> Self {
103    self.labels.push(label);
104    self
105  }
106
107  pub fn with_note(mut self, note: impl Into<String>) -> Self {
108    self.notes.push(note.into());
109    self
110  }
111
112  pub fn with_help(mut self, help: impl Into<String>) -> Self {
113    self.help = Some(help.into());
114    self
115  }
116}