1use std::fmt;
4
5#[derive(Clone, Copy, Debug, PartialEq, Eq)]
7pub struct Span {
8 pub start: usize,
9 pub end: usize,
10}
11
12impl Span {
13 pub fn new(start: usize, end: usize) -> Self {
14 Span { start, end }
15 }
16
17 pub fn merge(a: Span, b: Span) -> Span {
18 Span { start: a.start.min(b.start), end: a.end.max(b.end) }
19 }
20}
21
22#[derive(Clone, Copy, Debug, PartialEq, Eq)]
25pub enum ErrorKind {
26 Parse,
27 Rank,
28 Length,
29 Shape,
30 Domain,
31 Type,
32 Value,
33 Nan,
38 NotYet,
40 Language,
42 Sandbox,
45 Limit,
47 Internal,
48}
49
50impl ErrorKind {
51 pub fn label(self) -> &'static str {
52 match self {
53 ErrorKind::Parse => "parse error",
54 ErrorKind::Rank => "rank error",
55 ErrorKind::Length => "length error",
56 ErrorKind::Shape => "shape error",
57 ErrorKind::Domain => "domain error",
58 ErrorKind::Type => "type error",
59 ErrorKind::Value => "value error",
60 ErrorKind::Nan => "NaN error",
61 ErrorKind::NotYet => "not supported yet",
62 ErrorKind::Language => "not in the language",
63 ErrorKind::Sandbox => "closed by the sandbox",
64 ErrorKind::Limit => "limit error",
65 ErrorKind::Internal => "internal error",
66 }
67 }
68}
69
70#[derive(Clone, Debug)]
71pub struct Error {
72 pub kind: ErrorKind,
73 pub msg: String,
74 pub span: Option<Span>,
75 pub notes: Vec<String>,
76}
77
78pub type Result<T> = std::result::Result<T, Error>;
79
80impl Error {
81 pub fn new(kind: ErrorKind, msg: impl Into<String>, span: Option<Span>) -> Self {
82 Error { kind, msg: msg.into(), span, notes: Vec::new() }
83 }
84
85 pub fn parse(msg: impl Into<String>, span: Span) -> Self {
86 Self::new(ErrorKind::Parse, msg, Some(span))
87 }
88
89 pub fn not_yet(what: impl fmt::Display, span: Span) -> Self {
90 Self::new(ErrorKind::NotYet, format!("{what} is not supported yet"), Some(span))
91 }
92
93 pub fn language(msg: impl Into<String>, span: Span) -> Self {
94 Self::new(ErrorKind::Language, msg, Some(span))
95 }
96
97 pub fn sandbox(msg: impl Into<String>, span: Span) -> Self {
101 Self::new(ErrorKind::Sandbox, msg, Some(span))
102 }
103
104 pub fn domain(msg: impl Into<String>, span: Span) -> Self {
105 Self::new(ErrorKind::Domain, msg, Some(span))
106 }
107
108 pub fn nan(msg: impl Into<String>, span: Span) -> Self {
112 Self::new(ErrorKind::Nan, msg, Some(span))
113 }
114
115 pub fn internal(msg: impl Into<String>) -> Self {
116 Self::new(ErrorKind::Internal, msg, None)
117 }
118
119 pub fn note(mut self, note: impl Into<String>) -> Self {
120 self.notes.push(note.into());
121 self
122 }
123
124 pub fn render(&self, src: &str) -> String {
126 let mut out = format!("{}: {}", self.kind.label(), self.msg);
127 if let Some(span) = self.span && let Some((line, col_start, col_len)) = locate(src, span) {
128 out.push_str("\n ");
129 out.push_str(line);
130 out.push_str("\n ");
131 out.push_str(&" ".repeat(col_start));
132 out.push_str(&"^".repeat(col_len.max(1)));
133 }
134 for n in &self.notes {
135 out.push_str("\nnote: ");
136 out.push_str(n);
137 }
138 out
139 }
140}
141
142impl fmt::Display for Error {
143 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
144 write!(f, "{}: {}", self.kind.label(), self.msg)?;
145 for n in &self.notes {
146 write!(f, "\nnote: {n}")?;
147 }
148 Ok(())
149 }
150}
151
152impl std::error::Error for Error {}
153
154fn locate(src: &str, span: Span) -> Option<(&str, usize, usize)> {
157 if span.start > src.len() || !src.is_char_boundary(span.start) {
160 return None;
161 }
162 let line_start = src[..span.start].rfind('\n').map(|i| i + 1).unwrap_or(0);
163 let line_end = src[span.start..].find('\n').map(|i| span.start + i).unwrap_or(src.len());
164 let line = &src[line_start..line_end];
165 let col_start = src[line_start..span.start].chars().count();
166 let span_end = span.end.min(line_end).max(span.start);
167 let col_len = if src.is_char_boundary(span_end) {
168 src[span.start..span_end].chars().count()
169 } else {
170 1
171 };
172 Some((line, col_start, col_len))
173}