1mod location;23use std::path::{Path, PathBuf};45pub use location::*;67use crate::{error::Error, EvaluationState, LocError};8910pub enum PathResolver {11 12 FileName,13 14 Absolute,15 16 Relative(PathBuf),17}1819impl PathResolver {20 pub fn resolve(&self, from: &Path) -> String {21 match self {22 Self::FileName => from.file_name().unwrap().to_string_lossy().into_owned(),23 Self::Absolute => from.to_string_lossy().into_owned(),24 Self::Relative(base) => {25 if from.is_relative() {26 return from.to_string_lossy().into_owned();27 }28 pathdiff::diff_paths(from, base)29 .unwrap()30 .to_string_lossy()31 .into_owned()32 }33 }34 }35}363738pub trait TraceFormat {39 fn write_trace(40 &self,41 out: &mut dyn std::fmt::Write,42 evaluation_state: &EvaluationState,43 error: &LocError,44 ) -> Result<(), std::fmt::Error>;45 46 47 48 49 50 51 52}5354fn print_code_location(55 out: &mut impl std::fmt::Write,56 start: &CodeLocation,57 end: &CodeLocation,58) -> Result<(), std::fmt::Error> {59 if start.line == end.line {60 if start.column == end.column {61 write!(out, "{}:{}", start.line, end.column - 1)?;62 } else {63 write!(out, "{}:{}-{}", start.line, start.column - 1, end.column)?;64 }65 } else {66 write!(67 out,68 "{}:{}-{}:{}",69 start.line,70 end.column.saturating_sub(1),71 start.line,72 end.column73 )?;74 }75 Ok(())76}777879pub struct CompactFormat {80 pub resolver: PathResolver,81 pub padding: usize,82}8384impl TraceFormat for CompactFormat {85 fn write_trace(86 &self,87 out: &mut dyn std::fmt::Write,88 evaluation_state: &EvaluationState,89 error: &LocError,90 ) -> Result<(), std::fmt::Error> {91 write!(out, "{}", error.error())?;92 if let Error::ImportSyntaxError {93 path,94 source_code,95 error,96 } = error.error()97 {98 writeln!(out)?;99 use std::fmt::Write;100 let mut n = self.resolver.resolve(path);101 let mut offset = error.location.offset;102 let is_eof = if offset >= source_code.len() {103 offset = source_code.len().saturating_sub(1);104 true105 } else {106 false107 };108 let mut location = offset_to_location(source_code, &[offset])109 .into_iter()110 .next()111 .unwrap();112 if is_eof {113 location.column += 1;114 }115116 write!(n, ":").unwrap();117 print_code_location(&mut n, &location, &location).unwrap();118 write!(out, "{:<p$}{}", "", n, p = self.padding,)?;119 }120 let file_names = error121 .trace()122 .0123 .iter()124 .map(|el| &el.location)125 .map(|location| {126 use std::fmt::Write;127 #[allow(clippy::option_if_let_else)]128 if let Some(location) = location {129 let mut resolved_path = self.resolver.resolve(&location.0);130 131 let location = evaluation_state132 .map_source_locations(&location.0, &[location.1, location.2]);133 write!(resolved_path, ":").unwrap();134 print_code_location(&mut resolved_path, &location[0], &location[1]).unwrap();135 write!(resolved_path, ":").unwrap();136 Some(resolved_path)137 } else {138 None139 }140 })141 .collect::<Vec<_>>();142 let align = file_names143 .iter()144 .flatten()145 .map(|e| e.len())146 .max()147 .unwrap_or(0);148 for (el, file) in error.trace().0.iter().zip(file_names) {149 writeln!(out)?;150 if let Some(file) = file {151 write!(152 out,153 "{:<p$}{:<w$} {}",154 "",155 file,156 el.desc,157 p = self.padding,158 w = align159 )?;160 } else {161 write!(out, "{:<p$}{}", "", el.desc, p = self.padding,)?;162 }163 }164 Ok(())165 }166}167168pub struct JsFormat;169impl TraceFormat for JsFormat {170 fn write_trace(171 &self,172 out: &mut dyn std::fmt::Write,173 evaluation_state: &EvaluationState,174 error: &LocError,175 ) -> Result<(), std::fmt::Error> {176 write!(out, "{}", error.error())?;177 for item in error.trace().0.iter() {178 writeln!(out)?;179 let desc = &item.desc;180 if let Some(source) = &item.location {181 let start_end =182 evaluation_state.map_source_locations(&source.0, &[source.1, source.2]);183184 write!(185 out,186 " at {} ({}:{}:{})",187 desc,188 source.0.to_str().unwrap(),189 start_end[0].line,190 start_end[0].column,191 )?;192 } else {193 write!(out, " during {}", desc)?;194 }195 }196 Ok(())197 }198}199200201#[cfg(feature = "explaining-traces")]202pub struct ExplainingFormat {203 pub resolver: PathResolver,204}205#[cfg(feature = "explaining-traces")]206impl TraceFormat for ExplainingFormat {207 fn write_trace(208 &self,209 out: &mut dyn std::fmt::Write,210 evaluation_state: &EvaluationState,211 error: &LocError,212 ) -> Result<(), std::fmt::Error> {213 write!(out, "{}", error.error())?;214 if let Error::ImportSyntaxError {215 path,216 source_code,217 error,218 } = error.error()219 {220 writeln!(out)?;221 let offset = error.location.offset;222 let location = offset_to_location(source_code, &[offset])223 .into_iter()224 .next()225 .unwrap();226 let mut end_location = location.clone();227 end_location.offset += 1;228229 self.print_snippet(230 out,231 source_code,232 path,233 &location,234 &end_location,235 "syntax error",236 )?;237 }238 let trace = &error.trace();239 for item in trace.0.iter() {240 writeln!(out)?;241 let desc = &item.desc;242 if let Some(source) = &item.location {243 let start_end =244 evaluation_state.map_source_locations(&source.0, &[source.1, source.2]);245 self.print_snippet(246 out,247 &evaluation_state.get_source(&source.0).unwrap(),248 &source.0,249 &start_end[0],250 &start_end[1],251 desc,252 )?;253 } else {254 write!(out, "{}", desc)?;255 }256 }257 Ok(())258 }259}260261impl ExplainingFormat {262 fn print_snippet(263 &self,264 out: &mut dyn std::fmt::Write,265 source: &str,266 origin: &Path,267 start: &CodeLocation,268 end: &CodeLocation,269 desc: &str,270 ) -> Result<(), std::fmt::Error> {271 use annotate_snippets::{272 display_list::{DisplayList, FormatOptions},273 snippet::{AnnotationType, Slice, Snippet, SourceAnnotation},274 };275276 let source_fragment: String = source277 .chars()278 .skip(start.line_start_offset)279 .take(end.line_end_offset - end.line_start_offset)280 .collect();281282 let origin = self.resolver.resolve(origin);283 let snippet = Snippet {284 opt: FormatOptions {285 color: true,286 ..Default::default()287 },288 title: None,289 footer: vec![],290 slices: vec![Slice {291 source: &source_fragment,292 line_start: start.line,293 origin: Some(&origin),294 fold: false,295 annotations: vec![SourceAnnotation {296 label: desc,297 annotation_type: AnnotationType::Error,298 range: (299 start.offset - start.line_start_offset,300 (end.offset - start.line_start_offset).min(source_fragment.len()),301 ),302 }],303 }],304 };305306 let dl = DisplayList::from(snippet);307 write!(out, "{}", dl)?;308309 Ok(())310 }311}