1use jrsonnet_evaluator::{2 trace::{offset_to_location, CodeLocation},3 EvaluationState, LocError,4};5use std::path::PathBuf;678pub enum PathResolver {9 10 FileName,11 12 Absolute,13 14 Relative(PathBuf),15}1617impl PathResolver {18 pub fn resolve(&self, from: &PathBuf) -> String {19 match self {20 PathResolver::FileName => from.file_name().unwrap().to_string_lossy().into_owned(),21 PathResolver::Absolute => from.to_string_lossy().into_owned(),22 PathResolver::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::io::Write,40 evaluation_state: &EvaluationState,41 error: &LocError,42 ) -> Result<(), std::io::Error>;43 fn print_trace(44 &self,45 evaluation_state: &EvaluationState,46 error: &LocError,47 ) -> Result<(), std::io::Error> {48 self.write_trace(&mut std::io::stdout(), evaluation_state, error)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 - 1,69 start.line,70 end.column71 )?;72 }73 Ok(())74}757677pub struct CompactFormat {78 pub resolver: PathResolver,79}8081impl TraceFormat for CompactFormat {82 fn write_trace(83 &self,84 out: &mut dyn std::io::Write,85 evaluation_state: &EvaluationState,86 error: &LocError,87 ) -> Result<(), std::io::Error> {88 writeln!(out, "{:?}", error.0)?;89 let file_names = (error.1)90 .091 .iter()92 .map(|el| {93 let resolved_path = self.resolver.resolve(&(el.0).0);94 95 let location =96 evaluation_state.map_source_locations(&(el.0).0, &[(el.0).1, (el.0).2]);97 (resolved_path, location)98 })99 .map(|(mut n, location)| {100 use std::fmt::Write;101 write!(n, ":").unwrap();102 print_code_location(&mut n, &location[0], &location[1]).unwrap();103 n104 })105 .collect::<Vec<_>>();106 let align = file_names.iter().map(|e| e.len()).max().unwrap_or(0);107 for (i, (el, file)) in (error.1).0.iter().zip(file_names).enumerate() {108 if i != 0 {109 writeln!(out)?;110 }111 write!(out, "{:<w$}: {}", file, el.1, w = align)?;112 }113 Ok(())114 }115}116117118pub struct ExplainingFormat {119 pub resolver: PathResolver,120}121impl TraceFormat for ExplainingFormat {122 fn write_trace(123 &self,124 out: &mut dyn std::io::Write,125 evaluation_state: &EvaluationState,126 error: &LocError,127 ) -> Result<(), std::io::Error> {128 use annotate_snippets::{129 display_list::{DisplayList, FormatOptions},130 snippet::{AnnotationType, Slice, Snippet, SourceAnnotation},131 };132 writeln!(out, "{:?}", error.0)?;133 let trace = &error.1;134 for item in trace.0.iter() {135 let desc = &item.1;136 let source = item.0.clone();137 let start_end = evaluation_state.map_source_locations(&source.0, &[source.1, source.2]);138139 let source_fragment: String = evaluation_state140 .get_source(&source.0)141 .unwrap()142 .chars()143 .skip(start_end[0].line_start_offset)144 .take(start_end[1].line_end_offset - start_end[0].line_start_offset)145 .collect();146147 let origin = self.resolver.resolve(&source.0);148 let snippet = Snippet {149 opt: FormatOptions {150 color: true,151 ..Default::default()152 },153 title: None,154 footer: vec![],155 slices: vec![Slice {156 source: &source_fragment,157 line_start: start_end[0].line,158 origin: Some(&origin),159 fold: false,160 annotations: vec![SourceAnnotation {161 label: desc,162 annotation_type: AnnotationType::Error,163 range: (164 source.1 - start_end[0].line_start_offset,165 source.2 - start_end[0].line_start_offset,166 ),167 }],168 }],169 };170171 let dl = DisplayList::from(snippet);172 writeln!(out, "{}", dl)?;173 }174 Ok(())175 }176}