git.delta.rocks / jrsonnet / refs/commits / 8e1facac94b0

difftreelog

source

crates/jrsonnet-evaluator/src/trace/mod.rs6.6 KiBsourcehistory
1mod location;23use crate::{error::Error, EvaluationState, LocError};4pub use location::*;5use std::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: &PathBuf) -> 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() - 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| {123				el.location.as_ref().map(|l| {124					use std::fmt::Write;125					let mut resolved_path = self.resolver.resolve(&l.0);126					// TODO: Process all trace elements first127					let location = evaluation_state.map_source_locations(&l.0, &[l.1, l.2]);128					write!(resolved_path, ":").unwrap();129					print_code_location(&mut resolved_path, &location[0], &location[1]).unwrap();130					resolved_path131				})132			})133			.collect::<Vec<_>>();134		let align = file_names135			.iter()136			.flatten()137			.map(|e| e.len())138			.max()139			.unwrap_or(0);140		for (el, file) in error.trace().0.iter().zip(file_names) {141			writeln!(out)?;142			write!(143				out,144				"{:<p$}{:<w$}: {}",145				"",146				file.unwrap_or_else(|| "".to_owned()),147				el.desc,148				p = self.padding,149				w = align150			)?;151		}152		Ok(())153	}154}155156pub struct JsFormat;157impl TraceFormat for JsFormat {158	fn write_trace(159		&self,160		out: &mut dyn std::fmt::Write,161		evaluation_state: &EvaluationState,162		error: &LocError,163	) -> Result<(), std::fmt::Error> {164		write!(out, "{}", error.error())?;165		for item in error.trace().0.iter() {166			writeln!(out)?;167			let desc = &item.desc;168			if let Some(source) = &item.location {169				let start_end =170					evaluation_state.map_source_locations(&source.0, &[source.1, source.2]);171172				write!(173					out,174					"    at {} ({}:{}:{})",175					desc,176					source.0.to_str().unwrap(),177					start_end[0].line,178					start_end[0].column,179				)?;180			} else {181				write!(out, "    at {}", desc,)?;182			}183		}184		Ok(())185	}186}187188/// rustc-like trace displaying189#[cfg(feature = "explaining-traces")]190pub struct ExplainingFormat {191	pub resolver: PathResolver,192}193#[cfg(feature = "explaining-traces")]194impl TraceFormat for ExplainingFormat {195	fn write_trace(196		&self,197		out: &mut dyn std::fmt::Write,198		evaluation_state: &EvaluationState,199		error: &LocError,200	) -> Result<(), std::fmt::Error> {201		write!(out, "{}", error.error())?;202		if let Error::ImportSyntaxError {203			path,204			source_code,205			error,206		} = error.error()207		{208			writeln!(out)?;209			let mut offset = error.location.offset;210			if offset >= source_code.len() {211				offset = source_code.len() - 1;212			}213			let mut location = offset_to_location(source_code, &[offset])214				.into_iter()215				.next()216				.unwrap();217			if location.column >= 1 {218				location.column -= 1;219			}220221			self.print_snippet(222				out,223				source_code,224				path,225				&location,226				&location,227				"^ syntax error",228			)?;229		}230		let trace = &error.trace();231		for item in trace.0.iter() {232			writeln!(out)?;233			let desc = &item.desc;234			if let Some(source) = &item.location {235				let start_end =236					evaluation_state.map_source_locations(&source.0, &[source.1, source.2]);237				self.print_snippet(238					out,239					&evaluation_state.get_source(&source.0).unwrap(),240					&source.0,241					&start_end[0],242					&start_end[1],243					desc,244				)?;245			} else {246				write!(out, "{}", desc)?;247			}248		}249		Ok(())250	}251}252253impl ExplainingFormat {254	fn print_snippet(255		&self,256		out: &mut dyn std::fmt::Write,257		source: &str,258		origin: &PathBuf,259		start: &CodeLocation,260		end: &CodeLocation,261		desc: &str,262	) -> Result<(), std::fmt::Error> {263		use annotate_snippets::{264			display_list::{DisplayList, FormatOptions},265			snippet::{AnnotationType, Slice, Snippet, SourceAnnotation},266		};267268		let source_fragment: String = source269			.chars()270			.skip(start.line_start_offset)271			.take(end.line_end_offset - end.line_start_offset)272			.collect();273274		let origin = self.resolver.resolve(origin);275		let snippet = Snippet {276			opt: FormatOptions {277				color: true,278				..Default::default()279			},280			title: None,281			footer: vec![],282			slices: vec![Slice {283				source: &source_fragment,284				line_start: start.line,285				origin: Some(&origin),286				fold: false,287				annotations: vec![SourceAnnotation {288					label: desc,289					annotation_type: AnnotationType::Error,290					range: (291						start.offset - start.line_start_offset,292						end.offset - start.line_start_offset,293					),294				}],295			}],296		};297298		let dl = DisplayList::from(snippet);299		write!(out, "{}", dl)?;300301		Ok(())302	}303}