1use std::fmt;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum Severity {
13 Error,
15 Warning,
17}
18
19impl fmt::Display for Severity {
20 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
21 match self {
22 Severity::Error => f.write_str("error"),
23 Severity::Warning => f.write_str("warning"),
24 }
25 }
26}
27
28#[derive(Debug, Clone, PartialEq, Eq)]
30pub struct Diagnostic {
31 pub rule: &'static str,
32 pub severity: Severity,
33 pub message: String,
34 pub pos: Option<(u32, u32)>,
35 pub file: Option<String>,
36}
37
38impl Diagnostic {
39 pub fn new(
40 rule: &'static str,
41 severity: Severity,
42 pos: Option<(u32, u32)>,
43 message: impl Into<String>,
44 ) -> Self {
45 Self {
46 rule,
47 severity,
48 message: message.into(),
49 pos,
50 file: None,
51 }
52 }
53
54 pub fn in_file(mut self, file: Option<String>) -> Self {
57 self.file = file;
58 self
59 }
60
61 pub fn error(rule: &'static str, pos: Option<(u32, u32)>, message: impl Into<String>) -> Self {
62 Self::new(rule, Severity::Error, pos, message)
63 }
64
65 pub fn warning(
66 rule: &'static str,
67 pos: Option<(u32, u32)>,
68 message: impl Into<String>,
69 ) -> Self {
70 Self::new(rule, Severity::Warning, pos, message)
71 }
72
73 pub fn line(&self) -> Option<u32> {
75 self.pos.map(|(line, _)| line)
76 }
77
78 pub fn column(&self) -> Option<u32> {
80 self.pos.map(|(_, col)| col)
81 }
82}
83
84impl fmt::Display for Diagnostic {
85 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86 let sev = self.severity;
87 let rule = self.rule;
88 let msg = &self.message;
89 match (&self.file, self.pos) {
90 (None, Some((line, col))) => write!(f, "{sev} [{rule}] {line}:{col}: {msg}"),
91 (None, None) => write!(f, "{sev} [{rule}]: {msg}"),
92 (Some(path), Some((line, col))) => {
93 write!(f, "{sev} [{rule}] {path}:{line}:{col}: {msg}")
94 }
95 (Some(path), None) => write!(f, "{sev} [{rule}] {path}: {msg}"),
96 }
97 }
98}