swamp_error_report/
lib.rs1pub mod analyze;
6mod dep;
7pub mod loader;
8pub mod parse;
9pub mod prelude;
10pub mod script_resolve;
11pub mod semantic;
12
13use eira::{Color, Kind, Pos, PosSpan, SourceLines};
14use std::fmt::Display;
15use std::io;
16use std::io::{Write, stderr};
17use std::path::Path;
18use swamp_dep_loader::{DepLoaderError, DependencyError};
19use swamp_program_analyzer::LoaderErr;
20
21use source_map_cache::{FileId, SourceMap};
22use source_map_node::Span;
23use swamp_semantic::err;
24
25#[derive(Debug)]
26pub enum ScriptResolveError {
27 AnalyzerError(err::Error),
28 DepLoaderError(DepLoaderError),
29 DependencyError(DependencyError),
30 LoaderError(LoaderErr),
31}
32
33impl From<DependencyError> for ScriptResolveError {
34 fn from(err: DependencyError) -> Self {
35 Self::DependencyError(err)
36 }
37}
38
39impl From<err::Error> for ScriptResolveError {
40 fn from(err: err::Error) -> Self {
41 Self::AnalyzerError(err)
42 }
43}
44
45impl From<DepLoaderError> for ScriptResolveError {
46 fn from(err: DepLoaderError) -> Self {
47 Self::DepLoaderError(err)
48 }
49}
50
51impl From<LoaderErr> for ScriptResolveError {
52 fn from(err: LoaderErr) -> Self {
53 Self::LoaderError(err)
54 }
55}
56
57pub struct SourceLinesWrap<'a> {
60 pub file_id: FileId,
61 pub source_map: &'a SourceMap,
62}
63
64impl SourceLines for SourceLinesWrap<'_> {
65 fn get_line(&self, line_number: usize) -> Option<&str> {
66 self.source_map.get_source_line(self.file_id, line_number)
67 }
68}
69
70pub struct Report<C> {
71 config: Builder<C>,
72}
73
74impl<C: Display + Clone> Report<C> {
75 pub fn build(kind: Kind, code: C, error_name: &str, primary_span: &Span) -> Builder<C> {
76 Builder {
77 primary_span: primary_span.clone(),
78 kind,
79 error_code: code,
80 error_name: error_name.to_string(),
81 labels: vec![],
82 note: None,
83 error_module: String::new(),
84 }
85 }
86
87 pub const fn new(config: Builder<C>) -> Self {
88 Self { config }
89 }
90
91 pub fn print(
94 &self,
95 source_map: &SourceMap,
96 current_dir: &Path,
97 mut writer: impl Write,
98 ) -> io::Result<()> {
99 let header = eira::Header {
100 header_kind: self.config.kind,
101 code: self.config.error_code.clone(),
102 code_prefix: self.config.error_module.clone(),
103 message: self.config.error_name.clone(),
104 };
105 header.write(&mut writer)?;
106 let primary_span = &self.config.primary_span;
107 if primary_span.file_id == 0 {
108 eprintln!("{}", format!("header {} {}", header.message, header.code));
109 }
110 if primary_span.file_id != 0 {
111 let (row, col) = source_map
112 .get_span_location_utf8(primary_span.file_id, primary_span.offset as usize);
113 let filename = source_map.get_relative_path_to(primary_span.file_id, current_dir)?;
114
115 eira::FileSpanMessage::write(
116 filename.to_str().unwrap(),
117 &PosSpan {
118 pos: Pos { x: col, y: row },
119 length: primary_span.length as usize,
120 },
121 &mut writer,
122 )?;
123
124 let mut source_file_section = eira::SourceFileSection::new();
125 for label in &self.config.labels {
126 let (row, col) = source_map
127 .get_span_location_utf8(label.span.file_id, label.span.offset as usize);
128
129 source_file_section.labels.push(eira::Label {
130 start: Pos { x: col, y: row },
131 character_count: label.span.length as usize,
132 text: label.description.clone(),
133 color: Color::default(),
134 });
135 }
136
137 if self.config.labels.is_empty() {
138 source_file_section.labels.push(eira::Label {
139 start: Pos { x: col, y: row },
140 character_count: primary_span.length as usize,
141 text: self.config.error_name.clone(),
142 color: Color::default(),
143 });
144 }
145
146 source_file_section.layout();
147
148 let source_line_wrap = SourceLinesWrap {
149 file_id: primary_span.file_id,
150 source_map,
151 };
152 source_file_section.draw(&source_line_wrap, &mut writer)?;
153 }
154
155 if let Some(found_note) = &self.config.note {
156 let header = eira::Header {
157 header_kind: Kind::Note,
158 code: 100,
159 code_prefix: String::new(),
160 message: found_note.to_string(),
161 };
162 header.write(&mut writer)?;
163 }
164
165 Ok(())
166 }
167}
168
169pub struct Label {
170 pub span: Span,
171 pub description: String,
172}
173
174pub struct Builder<C> {
175 pub primary_span: Span,
176 pub kind: Kind,
177 pub error_code: C,
178 pub error_name: String,
179 pub error_module: String,
180 pub labels: Vec<Label>,
181 pub note: Option<String>,
182}
183
184impl<C: Display + Clone> Builder<C> {
185 #[must_use]
186 pub fn with_label(mut self, label: &str, span: Span) -> Self {
187 let l = Label {
188 span,
189 description: label.to_string(),
190 };
191
192 self.labels.push(l);
193 self
194 }
195
196 #[must_use]
197 pub fn with_note(mut self, note: &str) -> Self {
198 self.note = Some(note.to_string());
199 self
200 }
201
202 pub const fn build(self) -> Report<C> {
203 Report::new(self)
204 }
205}
206
207pub fn build_and_print(builder: Builder<usize>, source_map: &SourceMap, current_dir: &Path) {
208 let report = builder.build();
209 report.print(source_map, current_dir, stderr()).unwrap();
210}