git.delta.rocks / jrsonnet / refs/commits / 20cd69c85b05

difftreelog

source

crates/jrsonnet-evaluator/src/trace/mod.rs6.8 KiBsourcehistory
1mod location;23use crate::{error::Error, EvaluationState, LocError};4pub use location::*;5use std::path::{Path, PathBuf};67/// The way paths should be displayed8pub enum PathResolver {9	/// Only filename10	FileName,11	/// Absolute path12	Absolute,13	/// Path relative to base directory14	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}3435/// Implements pretty-printing of traces36pub 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	// fn print_trace(44	// 	&self,45	// 	evaluation_state: &EvaluationState,46	// 	error: &LocError,47	// ) -> Result<(), std::fmt::Error> {48	// 	self.write_trace(&mut std::fmt::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.saturating_sub(1),69			start.line,70			end.column71		)?;72	}73	Ok(())74}7576/// vanilla-like jsonnet formatting77pub 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					// TODO: Process all trace elements first129					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}197198/// rustc-like trace displaying199#[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}