1
2use std::fmt;
3
4use castle_input_cursor::{Position, Span};
5use serde::{Deserialize, Serialize};
6
7
8#[derive(Debug, Deserialize, Serialize)]
9pub enum CastleError {
10 IO(Box<str>),
11 AbruptEOF(Box<str>),
12 Syntax(Box<str>, Position),
13 Parser(Box<str>, Span),
14 Other(Box<str>),
15 Schema(Box<str>, Span),
16 Validation(Box<str>),
17 MissingDirective(Box<str>),
18 MissingResolver(Box<str>),
19 Unimplemented,
20}
21
22impl std::error::Error for CastleError {}
23
24impl From<std::io::Error> for CastleError {
25 fn from(err: std::io::Error) -> Self {
26 Self::IO(err.to_string().into())
27 }
28}
29
30impl CastleError {
31 pub fn syntax<Msg, Pos>(msg: Msg, pos: Pos) -> Self
32 where
33 Msg: Into<Box<str>>,
34 Pos: Into<Position>,
35 {
36 Self::Syntax(msg.into(), pos.into())
37 }
38
39 pub fn parse<Msg, S>(msg: Msg, span: S) -> Self
40 where
41 Msg: Into<Box<str>>,
42 S: Into<Span>,
43 {
44 Self::Parser(msg.into(), span.into())
45 }
46}
47
48impl fmt::Display for CastleError {
49 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50 match self {
51 Self::IO(msg) => write!(f, "IO error: {}", msg),
52 Self::AbruptEOF(msg) => write!(f, "Unexpected EOF: {}", msg),
53 Self::Syntax(msg, pos) => write!(f, "Syntax error: {} at {}", msg, pos),
54 Self::Parser(msg, span) => write!(f, "Parser error: {} at {}", msg, span),
55 Self::Other(msg) => write!(f, "Error: {}", msg),
56 Self::Schema(msg, span) => write!(f, "Schema error: {} at {}", msg, span),
57 Self::Validation(msg) => write!(f, "Schema validation error: {}", msg),
58 Self::MissingDirective(msg) => write!(f, "Missing directive: {}", msg),
59 Self::MissingResolver(msg) => write!(f, "Missing resolver: {}", msg),
60 Self::Unimplemented => write!(f, "Unimplemented"),
61 }
62 }
63}
64
65pub trait ExtendedErrorDisplay {
66 fn extended_error(&self, src: &str) -> String;
67}
68
69impl ExtendedErrorDisplay for CastleError {
70 fn extended_error(&self, src: &str) -> String {
71 match self {
72 Self::IO(msg) => msg.to_string(),
73 Self::AbruptEOF(msg) => msg.to_string(),
74 Self::Other(msg) => msg.to_string(),
75 Self::Syntax(msg, pos) => pretty_print_lexer_error(msg, pos, src),
76 Self::Parser(msg, span) => pretty_print_parser_error(msg, span, src),
77 Self::Schema(msg, span) => pretty_print_parser_error(msg, span, src),
78 Self::Validation(msg) => msg.to_string(),
79 Self::MissingDirective(msg) => msg.to_string(),
80 Self::MissingResolver(msg) => msg.to_string(),
81 Self::Unimplemented => "Unimplemented".to_string(),
82 }
83 }
84}
85
86
87fn pretty_print_parser_error(msg: &str, span: &Span, src: &str) -> String {
107 let src_lines: Vec<String> = src.lines().map(|line| line.to_string()).collect();
108 let mut result_lines = vec![];
109
110 result_lines.push(format!("error: {}\n", msg));
111
112 let start_line_index = span.start().line_number() - 1;
113 let end_line_index = span.end().line_number() - 1;
114 let is_multiple_lines = start_line_index != end_line_index;
115
116 for line_index in start_line_index..=end_line_index {
117 result_lines.push(get_line_text(&line_index.to_string(), src_lines.get(line_index as usize).unwrap()));
118 }
119
120 let space_before_arrow = match is_multiple_lines {
121 true => 0,
122 false => span.start().column_number(),
123 } as usize;
124
125 let spaces = " ".repeat(space_before_arrow);
126 let arrows = "^".repeat(span.end().column_number() as usize - space_before_arrow);
127
128 result_lines.push(get_line_text(&"", &format!("{spaces}{arrows} {msg}")));
129
130 for line_index in end_line_index+1..end_line_index+3 {
131 if let Some(line) = src_lines.get(line_index as usize) {
132 result_lines.push(get_line_text(&line_index.to_string(), line));
133 }
134 }
135
136 result_lines.join("\n")
137}
138
139fn get_line_text(index: &str, line: &str) -> String {
140 format!("{:>4} | {}", index, line)
141}
142
143
144fn pretty_print_lexer_error(msg: &str, pos: &Position, src: &str) -> String {
145 let src_lines: Vec<String> = src.lines().map(|line| line.to_string()).collect();
146 let mut result_lines = vec![];
147
148 result_lines.push(format!("ERROR: {}\n", msg));
149
150 let start_line_index = pos.line_number() - 1;
151
152 result_lines.push(get_line_text(&start_line_index.to_string(), src_lines.get(start_line_index as usize).unwrap()));
153
154 let space_before_arrow = " ".repeat(pos.column_number() as usize);
155
156 result_lines.push(get_line_text(&"", &format!("{space_before_arrow}^ {msg}")));
157
158 for line_index in start_line_index+1..start_line_index+3 {
159 if let Some(line) = src_lines.get(line_index as usize) {
160 result_lines.push(get_line_text(&line_index.to_string(), line));
161 }
162 }
163
164 result_lines.join("\n")
165}
166
167
168#[ignore]
169#[test]
170fn test_pretty_print_lexer_error() {
171 let src = r#"
172 type {
173
174 }
175
176 "#;
177 let err = CastleError::syntax("got unexpected string", Position::new(2, 5));
178
179 let result = err.extended_error(src);
180 println!("{}", result);
181}
182
183#[ignore]
184#[test]
185fn test_pretty_print_parser_error() {
186 let src = r#"
187type "sdsdds
188sdsd" {
189 first_name: String
190 last_name: String
191}
192
193 "#;
194 let err = CastleError::parse("got unexpected string", Span::new(Position::new(2, 5), Position::new(3, 5)));
195
196 let result = err.extended_error(src);
197 println!("{}", result);
198}
199
200#[ignore]
201#[test]
202fn test_pretty_print_parser_error_with_one_line() {
203 let src = r#"
204type type {
205 first_name: String
206 last_name: String
207}
208
209 "#;
210 let err = CastleError::parse("got unexpected keyword 'type', but expected identifier", Span::new(Position::new(2, 5), Position::new(2, 5 + 4)));
211
212 let result = err.extended_error(src);
213 println!("{}", result);
214}