1use jrsonnet_evaluator::{trace::CodeLocation, EvaluationState, LocError};2use std::path::PathBuf;345pub enum PathResolver {6 7 FileName,8 9 Absolute,10 11 Relative(PathBuf),12}1314impl PathResolver {15 pub fn resolve(&self, from: &PathBuf) -> String {16 match self {17 PathResolver::FileName => from.file_name().unwrap().to_string_lossy().into_owned(),18 PathResolver::Absolute => from.to_string_lossy().into_owned(),19 PathResolver::Relative(base) => {20 if from.is_relative() {21 return from.to_string_lossy().into_owned();22 }23 pathdiff::diff_paths(from, base)24 .unwrap()25 .to_string_lossy()26 .into_owned()27 }28 }29 }30}313233pub trait TraceFormat {34 fn write_trace(35 &self,36 out: &mut dyn std::io::Write,37 evaluation_state: &EvaluationState,38 error: &LocError,39 ) -> Result<(), std::io::Error>;40 fn print_trace(41 &self,42 evaluation_state: &EvaluationState,43 error: &LocError,44 ) -> Result<(), std::io::Error> {45 self.write_trace(&mut std::io::stdout(), evaluation_state, error)46 }47}4849fn print_code_location(50 out: &mut impl std::fmt::Write,51 start: &CodeLocation,52 end: &CodeLocation,53) -> Result<(), std::fmt::Error> {54 if start.line == end.line {55 if start.column == end.column {56 write!(out, "{}:{}", start.line, end.column - 1)?;57 } else {58 write!(out, "{}:{}-{}", start.line, start.column - 1, end.column)?;59 }60 } else {61 write!(62 out,63 "{}:{}-{}:{}",64 start.line,65 end.column - 1,66 start.line,67 end.column68 )?;69 }70 Ok(())71}727374pub struct CompactFormat {75 pub resolver: PathResolver,76}7778impl TraceFormat for CompactFormat {79 fn write_trace(80 &self,81 out: &mut dyn std::io::Write,82 evaluation_state: &EvaluationState,83 error: &LocError,84 ) -> Result<(), std::io::Error> {85 writeln!(out, "{:?}", error.0)?;86 let file_names = (error.1)87 .088 .iter()89 .map(|el| {90 let resolved_path = self.resolver.resolve(&(el.0).0);91 92 let location =93 evaluation_state.map_source_locations(&(el.0).0, &[(el.0).1, (el.0).2]);94 (resolved_path, location)95 })96 .map(|(mut n, location)| {97 use std::fmt::Write;98 write!(n, ":").unwrap();99 print_code_location(&mut n, &location[0], &location[1]).unwrap();100 n101 })102 .collect::<Vec<_>>();103 let align = file_names.iter().map(|e| e.len()).max().unwrap_or(0);104 for (i, (el, file)) in (error.1).0.iter().zip(file_names).enumerate() {105 if i != 0 {106 writeln!(out)?;107 }108 write!(out, "{:<w$}: {}", file, el.1, w = align)?;109 }110 Ok(())111 }112}113114115pub struct ExplainingFormat {116 pub resolver: PathResolver,117}118impl TraceFormat for ExplainingFormat {119 fn write_trace(120 &self,121 out: &mut dyn std::io::Write,122 evaluation_state: &EvaluationState,123 error: &LocError,124 ) -> Result<(), std::io::Error> {125 use annotate_snippets::{126 display_list::{DisplayList, FormatOptions},127 snippet::{AnnotationType, Slice, Snippet, SourceAnnotation},128 };129 writeln!(out, "{:?}", error.0)?;130 let trace = &error.1;131 for item in trace.0.iter() {132 let desc = &item.1;133 let source = item.0.clone();134 let start_end = evaluation_state.map_source_locations(&source.0, &[source.1, source.2]);135136 let source_fragment: String = evaluation_state137 .get_source(&source.0)138 .unwrap()139 .chars()140 .skip(start_end[0].line_start_offset)141 .take(start_end[1].line_end_offset - start_end[0].line_start_offset)142 .collect();143144 let origin = self.resolver.resolve(&source.0);145 let snippet = Snippet {146 opt: FormatOptions {147 color: true,148 ..Default::default()149 },150 title: None,151 footer: vec![],152 slices: vec![Slice {153 source: &source_fragment,154 line_start: start_end[0].line,155 origin: Some(&origin),156 fold: false,157 annotations: vec![SourceAnnotation {158 label: desc,159 annotation_type: AnnotationType::Error,160 range: (161 source.1 - start_end[0].line_start_offset,162 source.2 - start_end[0].line_start_offset,163 ),164 }],165 }],166 };167168 let dl = DisplayList::from(snippet);169 writeln!(out, "{}", dl)?;170 }171 Ok(())172 }173}