1mod location;23use crate::{error::Error, EvaluationState, LocError};4pub use location::*;5use std::path::{Path, PathBuf};678pub enum PathResolver {9 10 FileName,11 12 Absolute,13 14 Relative(PathBuf),15}1617impl PathResolver {18 pub fn resolve(&self, from: &Path) -> String {19 match self {20 Self::FileName => from.file_name().unwrap().to_string_lossy().into_owned(),21 Self::Absolute => from.to_string_lossy().into_owned(),22 Self::Relative(base) => {23 if from.is_relative() {24 return from.to_string_lossy().into_owned();25 }26 pathdiff::diff_paths(from, base)27 .unwrap()28 .to_string_lossy()29 .into_owned()30 }31 }32 }33}343536pub trait TraceFormat {37 fn write_trace(38 &self,39 out: &mut dyn std::fmt::Write,40 evaluation_state: &EvaluationState,41 error: &LocError,42 ) -> Result<(), std::fmt::Error>;43 44 45 46 47 48 49 50}5152fn print_code_location(53 out: &mut impl std::fmt::Write,54 start: &CodeLocation,55 end: &CodeLocation,56) -> Result<(), std::fmt::Error> {57 if start.line == end.line {58 if start.column == end.column {59 write!(out, "{}:{}", start.line, end.column - 1)?;60 } else {61 write!(out, "{}:{}-{}", start.line, start.column - 1, end.column)?;62 }63 } else {64 write!(65 out,66 "{}:{}-{}:{}",67 start.line,68 end.column.saturating_sub(1),69 start.line,70 end.column71 )?;72 }73 Ok(())74}757677pub struct CompactFormat {78 pub resolver: PathResolver,79 pub padding: usize,80}8182impl TraceFormat for CompactFormat {83 fn write_trace(84 &self,85 out: &mut dyn std::fmt::Write,86 evaluation_state: &EvaluationState,87 error: &LocError,88 ) -> Result<(), std::fmt::Error> {89 write!(out, "{}", error.error())?;90 if let Error::ImportSyntaxError {91 path,92 source_code,93 error,94 } = error.error()95 {96 writeln!(out)?;97 use std::fmt::Write;98 let mut n = self.resolver.resolve(path);99 let mut offset = error.location.offset;100 let is_eof = if offset >= source_code.len() {101 offset = source_code.len().saturating_sub(1);102 true103 } else {104 false105 };106 let mut location = offset_to_location(source_code, &[offset])107 .into_iter()108 .next()109 .unwrap();110 if is_eof {111 location.column += 1;112 }113114 write!(n, ":").unwrap();115 print_code_location(&mut n, &location, &location).unwrap();116 write!(out, "{:<p$}{}", "", n, p = self.padding,)?;117 }118 let file_names = error119 .trace()120 .0121 .iter()122 .map(|el| &el.location)123 .map(|location| {124 use std::fmt::Write;125 #[allow(clippy::option_if_let_else)]126 if let Some(location) = location {127 let mut resolved_path = self.resolver.resolve(&location.0);128 129 let location = evaluation_state130 .map_source_locations(&location.0, &[location.1, location.2]);131 write!(resolved_path, ":").unwrap();132 print_code_location(&mut resolved_path, &location[0], &location[1]).unwrap();133 write!(resolved_path, ":").unwrap();134 Some(resolved_path)135 } else {136 None137 }138 })139 .collect::<Vec<_>>();140 let align = file_names141 .iter()142 .flatten()143 .map(|e| e.len())144 .max()145 .unwrap_or(0);146 for (el, file) in error.trace().0.iter().zip(file_names) {147 writeln!(out)?;148 if let Some(file) = file {149 write!(150 out,151 "{:<p$}{:<w$} {}",152 "",153 file,154 el.desc,155 p = self.padding,156 w = align157 )?;158 } else {159 write!(out, "{:<p$}{}", "", el.desc, p = self.padding,)?;160 }161 }162 Ok(())163 }164}165166pub struct JsFormat;167impl TraceFormat for JsFormat {168 fn write_trace(169 &self,170 out: &mut dyn std::fmt::Write,171 evaluation_state: &EvaluationState,172 error: &LocError,173 ) -> Result<(), std::fmt::Error> {174 write!(out, "{}", error.error())?;175 for item in error.trace().0.iter() {176 writeln!(out)?;177 let desc = &item.desc;178 if let Some(source) = &item.location {179 let start_end =180 evaluation_state.map_source_locations(&source.0, &[source.1, source.2]);181182 write!(183 out,184 " at {} ({}:{}:{})",185 desc,186 source.0.to_str().unwrap(),187 start_end[0].line,188 start_end[0].column,189 )?;190 } else {191 write!(out, " during {}", desc)?;192 }193 }194 Ok(())195 }196}197198199#[cfg(feature = "explaining-traces")]200pub struct ExplainingFormat {201 pub resolver: PathResolver,202}203#[cfg(feature = "explaining-traces")]204impl TraceFormat for ExplainingFormat {205 fn write_trace(206 &self,207 out: &mut dyn std::fmt::Write,208 evaluation_state: &EvaluationState,209 error: &LocError,210 ) -> Result<(), std::fmt::Error> {211 write!(out, "{}", error.error())?;212 if let Error::ImportSyntaxError {213 path,214 source_code,215 error,216 } = error.error()217 {218 writeln!(out)?;219 let offset = error.location.offset;220 let location = offset_to_location(source_code, &[offset])221 .into_iter()222 .next()223 .unwrap();224 let mut end_location = location.clone();225 end_location.offset += 1;226227 self.print_snippet(228 out,229 source_code,230 path,231 &location,232 &end_location,233 "syntax error",234 )?;235 }236 let trace = &error.trace();237 for item in trace.0.iter() {238 writeln!(out)?;239 let desc = &item.desc;240 if let Some(source) = &item.location {241 let start_end =242 evaluation_state.map_source_locations(&source.0, &[source.1, source.2]);243 self.print_snippet(244 out,245 &evaluation_state.get_source(&source.0).unwrap(),246 &source.0,247 &start_end[0],248 &start_end[1],249 desc,250 )?;251 } else {252 write!(out, "{}", desc)?;253 }254 }255 Ok(())256 }257}258259impl ExplainingFormat {260 fn print_snippet(261 &self,262 out: &mut dyn std::fmt::Write,263 source: &str,264 origin: &Path,265 start: &CodeLocation,266 end: &CodeLocation,267 desc: &str,268 ) -> Result<(), std::fmt::Error> {269 use annotate_snippets::{270 display_list::{DisplayList, FormatOptions},271 snippet::{AnnotationType, Slice, Snippet, SourceAnnotation},272 };273274 let source_fragment: String = source275 .chars()276 .skip(start.line_start_offset)277 .take(end.line_end_offset - end.line_start_offset)278 .collect();279280 let origin = self.resolver.resolve(origin);281 let snippet = Snippet {282 opt: FormatOptions {283 color: true,284 ..Default::default()285 },286 title: None,287 footer: vec![],288 slices: vec![Slice {289 source: &source_fragment,290 line_start: start.line,291 origin: Some(&origin),292 fold: false,293 annotations: vec![SourceAnnotation {294 label: desc,295 annotation_type: AnnotationType::Error,296 range: (297 start.offset - start.line_start_offset,298 (end.offset - start.line_start_offset).min(source_fragment.len()),299 ),300 }],301 }],302 };303304 let dl = DisplayList::from(snippet);305 write!(out, "{}", dl)?;306307 Ok(())308 }309}