swamp_script_error_report/
lib.rs1pub mod analyze;
6mod dep;
7pub mod loader;
8pub mod parse;
9pub mod prelude;
10pub mod runtime;
11pub mod script_resolve;
12pub mod semantic;
13
14use eira::{Color, Kind, Pos, PosSpan, SourceLines};
15use std::fmt::Display;
16use std::io;
17use std::io::{Write, stderr};
18use std::path::Path;
19use swamp_script_analyzer::prelude::Error;
20use swamp_script_dep_loader::{DepLoaderError, DependencyError};
21
22use swamp_script_eval_loader::LoaderErr;
23use swamp_script_node::Span;
24use swamp_script_source_map::{FileId, SourceMap};
25
26#[derive(Debug)]
27pub enum ScriptResolveError {
28 AnalyzerError(Error),
29 DepLoaderError(DepLoaderError),
30 DependencyError(DependencyError),
31 LoaderError(LoaderErr),
32}
33
34impl From<DependencyError> for ScriptResolveError {
35 fn from(err: DependencyError) -> Self {
36 Self::DependencyError(err)
37 }
38}
39
40impl From<Error> for ScriptResolveError {
41 fn from(err: Error) -> Self {
42 Self::AnalyzerError(err)
43 }
44}
45
46impl From<DepLoaderError> for ScriptResolveError {
47 fn from(err: DepLoaderError) -> Self {
48 Self::DepLoaderError(err)
49 }
50}
51
52impl From<LoaderErr> for ScriptResolveError {
53 fn from(err: LoaderErr) -> Self {
54 Self::LoaderError(err)
55 }
56}
57
58pub struct SourceLinesWrap<'a> {
61 pub file_id: FileId,
62 pub source_map: &'a SourceMap,
63}
64
65impl<'a> SourceLines for SourceLinesWrap<'a> {
66 fn get_line(&self, line_number: usize) -> Option<&str> {
67 self.source_map.get_source_line(self.file_id, line_number)
68 }
69}
70
71pub struct Report<C> {
72 config: Builder<C>,
73}
74
75impl<C: Display + Clone> Report<C> {
76 pub fn build(kind: Kind, code: C, error_name: &str, primary_span: &Span) -> Builder<C> {
77 Builder {
78 primary_span: primary_span.clone(),
79 kind,
80 error_code: code,
81 error_name: error_name.to_string(),
82 labels: vec![],
83 note: None,
84 error_module: String::new(),
85 }
86 }
87
88 pub const fn new(config: Builder<C>) -> Self {
89 Self { config }
90 }
91
92 pub fn print(
95 &self,
96 source_map: &SourceMap,
97 current_dir: &Path,
98 mut writer: impl Write,
99 ) -> io::Result<()> {
100 let header = eira::Header {
101 header_kind: self.config.kind,
102 code: self.config.error_code.clone(),
103 code_prefix: self.config.error_module.clone(),
104 message: self.config.error_name.clone(),
105 };
106 header.write(&mut writer)?;
107 let primary_span = &self.config.primary_span;
108 if primary_span.file_id == 0 {
109 eprintln!("{}", format!("header {} {}", header.message, header.code));
110 }
111 let (row, col) =
112 source_map.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) =
127 source_map.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 if let Some(found_note) = &self.config.note {
155 let header = eira::Header {
156 header_kind: Kind::Note,
157 code: 100,
158 code_prefix: String::new(),
159 message: found_note.to_string(),
160 };
161 header.write(&mut writer)?;
162 }
163
164 Ok(())
165 }
166}
167
168pub struct Label {
169 pub span: Span,
170 pub description: String,
171}
172
173pub struct Builder<C> {
174 pub primary_span: Span,
175 pub kind: Kind,
176 pub error_code: C,
177 pub error_name: String,
178 pub error_module: String,
179 pub labels: Vec<Label>,
180 pub note: Option<String>,
181}
182
183impl<C: Display + Clone> Builder<C> {
184 #[must_use]
185 pub fn with_label(mut self, label: &str, span: Span) -> Self {
186 let l = Label {
187 span,
188 description: label.to_string(),
189 };
190
191 self.labels.push(l);
192 self
193 }
194
195 #[must_use]
196 pub fn with_note(mut self, note: &str) -> Self {
197 self.note = Some(note.to_string());
198 self
199 }
200
201 pub const fn build(self) -> Report<C> {
202 Report::new(self)
203 }
204}
205
206pub fn build_and_print(builder: Builder<usize>, source_map: &SourceMap, current_dir: &Path) {
207 let report = builder.build();
208 report.print(source_map, current_dir, stderr()).unwrap();
209}