codespan_reporting/
diagnostic.rs

1//! Diagnostic data structures.
2
3use alloc::{
4    string::{String, ToString},
5    vec::Vec,
6};
7use core::ops::Range;
8
9#[cfg(feature = "serialization")]
10use serde::{Deserialize, Serialize};
11
12/// A severity level for diagnostic messages.
13///
14/// These are ordered in the following way:
15///
16/// ```rust
17/// use codespan_reporting::diagnostic::Severity;
18///
19/// assert!(Severity::Bug > Severity::Error);
20/// assert!(Severity::Error > Severity::Warning);
21/// assert!(Severity::Warning > Severity::Note);
22/// assert!(Severity::Note > Severity::Help);
23/// ```
24#[derive(Copy, Clone, Hash, Debug, PartialEq, Eq, PartialOrd, Ord)]
25#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
26pub enum Severity {
27    /// A help message.
28    Help,
29    /// A note.
30    Note,
31    /// A warning.
32    Warning,
33    /// An error.
34    Error,
35    /// An unexpected bug.
36    Bug,
37}
38
39#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd)]
40#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
41pub enum LabelStyle {
42    /// Labels that describe the primary cause of a diagnostic.
43    Primary,
44    /// Labels that provide additional context for a diagnostic.
45    Secondary,
46}
47
48/// A label describing an underlined region of code associated with a diagnostic.
49#[derive(Clone, Debug, PartialEq, Eq)]
50#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
51pub struct Label<FileId> {
52    /// The style of the label.
53    pub style: LabelStyle,
54    /// The file that we are labelling.
55    pub file_id: FileId,
56    /// The range in bytes we are going to include in the final snippet.
57    pub range: Range<usize>,
58    /// An optional message to provide some additional information for the
59    /// underlined code. These should not include line breaks.
60    pub message: String,
61}
62
63impl<FileId> Label<FileId> {
64    /// Create a new label.
65    pub fn new(
66        style: LabelStyle,
67        file_id: FileId,
68        range: impl Into<Range<usize>>,
69    ) -> Label<FileId> {
70        Label {
71            style,
72            file_id,
73            range: range.into(),
74            message: String::new(),
75        }
76    }
77
78    /// Create a new label with a style of [`LabelStyle::Primary`].
79    ///
80    /// [`LabelStyle::Primary`]: LabelStyle::Primary
81    pub fn primary(file_id: FileId, range: impl Into<Range<usize>>) -> Label<FileId> {
82        Label::new(LabelStyle::Primary, file_id, range)
83    }
84
85    /// Create a new label with a style of [`LabelStyle::Secondary`].
86    ///
87    /// [`LabelStyle::Secondary`]: LabelStyle::Secondary
88    pub fn secondary(file_id: FileId, range: impl Into<Range<usize>>) -> Label<FileId> {
89        Label::new(LabelStyle::Secondary, file_id, range)
90    }
91
92    /// Add a message to the diagnostic.
93    pub fn with_message(mut self, message: impl ToString) -> Label<FileId> {
94        self.message = message.to_string();
95        self
96    }
97}
98
99/// Represents a diagnostic message that can provide information like errors and
100/// warnings to the user.
101///
102/// The position of a Diagnostic is considered to be the position of the [`Label`] that has the earliest starting position and has the highest style which appears in all the labels of the diagnostic.
103#[derive(Clone, Debug, PartialEq, Eq)]
104#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
105pub struct Diagnostic<FileId> {
106    /// The overall severity of the diagnostic
107    pub severity: Severity,
108    /// An optional code that identifies this diagnostic.
109    pub code: Option<String>,
110    /// The main message associated with this diagnostic.
111    ///
112    /// These should not include line breaks, and in order support the 'short'
113    /// diagnostic display mod, the message should be specific enough to make
114    /// sense on its own, without additional context provided by labels and notes.
115    pub message: String,
116    /// Source labels that describe the cause of the diagnostic.
117    /// The order of the labels inside the vector does not have any meaning.
118    /// The labels are always arranged in the order they appear in the source code.
119    pub labels: Vec<Label<FileId>>,
120    /// Notes that are associated with the primary cause of the diagnostic.
121    /// These can include line breaks for improved formatting.
122    pub notes: Vec<String>,
123}
124
125impl<FileId> Diagnostic<FileId> {
126    /// Create a new diagnostic.
127    pub fn new(severity: Severity) -> Diagnostic<FileId> {
128        Diagnostic {
129            severity,
130            code: None,
131            message: String::new(),
132            labels: Vec::new(),
133            notes: Vec::new(),
134        }
135    }
136
137    /// Create a new diagnostic with a severity of [`Severity::Bug`].
138    ///
139    /// [`Severity::Bug`]: Severity::Bug
140    pub fn bug() -> Diagnostic<FileId> {
141        Diagnostic::new(Severity::Bug)
142    }
143
144    /// Create a new diagnostic with a severity of [`Severity::Error`].
145    ///
146    /// [`Severity::Error`]: Severity::Error
147    pub fn error() -> Diagnostic<FileId> {
148        Diagnostic::new(Severity::Error)
149    }
150
151    /// Create a new diagnostic with a severity of [`Severity::Warning`].
152    ///
153    /// [`Severity::Warning`]: Severity::Warning
154    pub fn warning() -> Diagnostic<FileId> {
155        Diagnostic::new(Severity::Warning)
156    }
157
158    /// Create a new diagnostic with a severity of [`Severity::Note`].
159    ///
160    /// [`Severity::Note`]: Severity::Note
161    pub fn note() -> Diagnostic<FileId> {
162        Diagnostic::new(Severity::Note)
163    }
164
165    /// Create a new diagnostic with a severity of [`Severity::Help`].
166    ///
167    /// [`Severity::Help`]: Severity::Help
168    pub fn help() -> Diagnostic<FileId> {
169        Diagnostic::new(Severity::Help)
170    }
171
172    /// Set the error code of the diagnostic.
173    pub fn with_code(mut self, code: impl ToString) -> Diagnostic<FileId> {
174        self.code = Some(code.to_string());
175        self
176    }
177
178    /// Set the message of the diagnostic.
179    pub fn with_message(mut self, message: impl ToString) -> Diagnostic<FileId> {
180        self.message = message.to_string();
181        self
182    }
183
184    /// Add a label to the diagnostic.
185    pub fn with_label(mut self, label: Label<FileId>) -> Diagnostic<FileId> {
186        self.labels.push(label);
187        self
188    }
189
190    /// Add some labels to the diagnostic.
191    pub fn with_labels(mut self, mut labels: Vec<Label<FileId>>) -> Diagnostic<FileId> {
192        self.labels.append(&mut labels);
193        self
194    }
195
196    /// Add some labels to the diagnostic.
197    pub fn with_labels_iter(
198        mut self,
199        labels: impl IntoIterator<Item = Label<FileId>>,
200    ) -> Diagnostic<FileId> {
201        self.labels.extend(labels);
202        self
203    }
204
205    /// Add a note to the diagnostic.
206    pub fn with_note(mut self, note: impl ToString) -> Diagnostic<FileId> {
207        self.notes.push(note.to_string());
208        self
209    }
210
211    /// Add some notes to the diagnostic.
212    pub fn with_notes(mut self, mut notes: Vec<String>) -> Diagnostic<FileId> {
213        self.notes.append(&mut notes);
214        self
215    }
216
217    /// Add some notes to the diagnostic.
218    pub fn with_notes_iter(
219        mut self,
220        notes: impl IntoIterator<Item = String>,
221    ) -> Diagnostic<FileId> {
222        self.notes.extend(notes);
223        self
224    }
225}