1#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub enum DiagnosticSeverity {
9 Error,
10 Warning,
11}
12
13#[derive(Debug, Clone, PartialEq, Eq)]
15pub struct ParseError {
16 pub message: String,
18 pub offset: Option<usize>,
20 pub line: Option<u32>,
22 pub column: Option<usize>,
24 pub length: Option<usize>,
26 pub severity: Option<DiagnosticSeverity>,
28 pub code: Option<String>,
30 pub expected: Option<String>,
32 pub found: Option<String>,
34 pub suggestion: Option<String>,
36}
37
38impl ParseError {
39 pub fn new(message: impl Into<String>) -> Self {
40 Self {
41 message: message.into(),
42 offset: None,
43 line: None,
44 column: None,
45 length: None,
46 severity: None,
47 code: None,
48 expected: None,
49 found: None,
50 suggestion: None,
51 }
52 }
53
54 pub fn with_offset(mut self, offset: usize) -> Self {
55 self.offset = Some(offset);
56 self
57 }
58
59 pub fn with_location(mut self, offset: usize, line: u32, column: usize) -> Self {
61 self.offset = Some(offset);
62 self.line = Some(line);
63 self.column = Some(column);
64 self
65 }
66
67 pub fn with_length(mut self, length: usize) -> Self {
68 self.length = Some(length);
69 self
70 }
71
72 pub fn with_severity(mut self, severity: DiagnosticSeverity) -> Self {
73 self.severity = Some(severity);
74 self
75 }
76
77 pub fn with_code(mut self, code: impl Into<String>) -> Self {
78 self.code = Some(code.into());
79 self
80 }
81
82 pub fn with_expected(mut self, expected: impl Into<String>) -> Self {
83 self.expected = Some(expected.into());
84 self
85 }
86
87 pub fn with_found(mut self, found: impl Into<String>) -> Self {
88 self.found = Some(found.into());
89 self
90 }
91
92 pub fn with_suggestion(mut self, suggestion: impl Into<String>) -> Self {
93 self.suggestion = Some(suggestion.into());
94 self
95 }
96
97 pub fn to_lsp_range(&self) -> Option<(u32, u32, u32, u32)> {
100 let (line, column) = (self.line?, self.column?);
101 let len = self.length.unwrap_or(1);
102 let start_line = line.saturating_sub(1);
103 let start_char = column.saturating_sub(1);
104 let end_line = start_line;
105 let end_char = start_char.saturating_add(len);
106 Some((start_line, start_char as u32, end_line, end_char as u32))
107 }
108}
109
110impl std::fmt::Display for ParseError {
111 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
112 let base = self
113 .expected
114 .as_deref()
115 .map(|e| format!("expected {e}"))
116 .unwrap_or_else(|| self.message.clone());
117 let mut msg = base;
118 if let Some(ref found) = self.found {
119 if !msg.contains("(found ") {
120 msg.push_str(&format!(" (found '{found}')"));
121 }
122 }
123 if let Some(ref suggestion) = self.suggestion {
124 msg.push_str(&format!(" {suggestion}"));
125 }
126 match (self.offset, self.line, self.column) {
127 (Some(_), Some(line), Some(col)) => write!(f, "{msg} at line {line}, column {col}"),
128 (Some(off), _, _) => write!(f, "{msg} at offset {off}"),
129 _ => write!(f, "{msg}"),
130 }
131 }
132}
133
134impl std::error::Error for ParseError {}