1use miette::{LabeledSpan, Severity, miette};
5use std::fmt;
6use thiserror::Error;
7
8pub type PTBResult<T> = Result<T, PTBError>;
9
10#[derive(Debug, Clone, PartialEq, Eq, Copy)]
12pub struct Span {
13 pub start: usize,
14 pub end: usize,
15}
16
17pub struct Spanned<T> {
19 pub span: Span,
20 pub value: T,
21}
22
23#[derive(Debug, Clone, Error)]
25#[error("{message}")]
26pub struct PTBError {
27 pub message: String,
28 pub span: Span,
29 pub help: Option<String>,
30 pub severity: Severity,
31}
32
33#[macro_export]
34macro_rules! sp {
35 (_, $value:pat) => {
36 $crate::client_ptb::error::Spanned { value: $value, .. }
37 };
38 ($loc:pat, _) => {
39 $crate::client_ptb::error::Spanned { span: $loc, .. }
40 };
41 ($loc:pat, $value:pat) => {
42 $crate::client_ptb::error::Spanned {
43 span: $loc,
44 value: $value,
45 }
46 };
47}
48
49#[macro_export]
50macro_rules! error {
51 ($l:expr, $($arg:tt)*) => {
52 return Err($crate::err!($l, $($arg)*))
53 };
54 ($l:expr => help: { $($h:expr),* }, $($arg:tt)*) => {
55 return Err($crate::err!($l => help: { $($h),* }, $($arg)*))
56 };
57}
58
59#[macro_export]
60macro_rules! err {
61 ($l:expr, $($arg:tt)*) => {
62 $crate::client_ptb::error::PTBError {
63 message: format!($($arg)*),
64 span: $l,
65 help: None,
66 severity: miette::Severity::Error,
67 }
68 };
69 ($l:expr => help: { $($h:expr),* }, $($arg:tt)*) => {
70 $crate::client_ptb::error::PTBError {
71 message: format!($($arg)*),
72 span: $l,
73 help: Some(format!($($h),*)),
74 severity: miette::Severity::Error,
75 }
76 };
77}
78
79pub use sp;
80
81impl PTBError {
82 pub fn with_help(self, help: String) -> Self {
84 let PTBError {
85 message,
86 span,
87 help: _,
88 severity,
89 } = self;
90 PTBError {
91 message,
92 span,
93 help: Some(help),
94 severity,
95 }
96 }
97}
98
99impl Span {
100 pub fn wrap<T>(self, value: T) -> Spanned<T> {
102 Spanned { span: self, value }
103 }
104
105 pub fn widen(self, other: Span) -> Span {
108 Span {
109 start: self.start.min(other.start),
110 end: self.end.max(other.end),
111 }
112 }
113
114 pub fn widen_opt(self, other: Option<Span>) -> Span {
116 other.map_or(self, |other| self.widen(other))
117 }
118
119 pub fn eof_span() -> Span {
121 Self {
122 start: usize::MAX,
123 end: usize::MAX,
124 }
125 }
126}
127
128impl<T> Spanned<T> {
129 pub fn map<U>(self, f: impl FnOnce(T) -> U) -> Spanned<U> {
131 Spanned {
132 span: self.span,
133 value: f(self.value),
134 }
135 }
136
137 pub fn widen<U>(self, other: Spanned<U>) -> Spanned<T> {
140 self.widen_span(other.span)
141 }
142
143 pub fn widen_span(self, other: Span) -> Spanned<T> {
146 Spanned {
147 span: self.span.widen(other),
148 value: self.value,
149 }
150 }
151}
152
153impl<T: fmt::Debug> fmt::Debug for Spanned<T> {
154 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
155 f.debug_struct("Spanned")
156 .field("span", &self.span)
157 .field("value", &self.value)
158 .finish()
159 }
160}
161
162impl<T: Clone> Clone for Spanned<T> {
163 fn clone(&self) -> Self {
164 Spanned {
165 span: self.span,
166 value: self.value.clone(),
167 }
168 }
169}
170
171impl<T: Copy> Copy for Spanned<T> {}
172
173fn build_error_report(file_string: &str, error: PTBError) -> miette::Report {
174 let PTBError {
175 span,
176 message,
177 help,
178 severity,
179 } = error;
180 let clamp = |x: usize| x.min(file_string.len() - 1);
181 let label = LabeledSpan::at(clamp(span.start)..clamp(span.end), message.clone());
182 let error_string = match severity {
183 Severity::Advice => "Advice found when processing PTB".to_string(),
184 Severity::Warning => "Warning when processing PTB".to_string(),
185 Severity::Error => "Error when processing PTB".to_string(),
186 };
187 match help {
188 Some(help_msg) => miette!(labels = vec![label], help = help_msg, "{}", error_string),
189 None => miette!(
190 labels = vec![label],
191 severity = severity,
192 "{}",
193 error_string
194 ),
195 }
196 .with_source_code(file_string.to_string())
197}
198
199pub fn build_error_reports(source_string: &str, errors: Vec<PTBError>) -> Vec<miette::Report> {
200 errors
201 .into_iter()
202 .map(|e| build_error_report(source_string, e))
203 .collect()
204}