swamp_error_report/
lib.rs

1/*
2 * Copyright (c) Peter Bjorklund. All rights reserved. https://github.com/swamp/swamp
3 * Licensed under the MIT License. See LICENSE in the project root for license information.
4 */
5pub 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
57// -------------------------------------------
58
59pub 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    /// # Errors
92    ///
93    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}