git.delta.rocks / jrsonnet / refs/commits / e53349155ac3

difftreelog

source

crates/jrsonnet-evaluator/src/trace/mod.rs6.8 KiBsourcehistory
1mod location;23use std::path::{Path, PathBuf};45pub use location::*;67use crate::{error::Error, EvaluationState, LocError};89/// The way paths should be displayed10pub enum PathResolver {11	/// Only filename12	FileName,13	/// Absolute path14	Absolute,15	/// Path relative to base directory16	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}3637/// Implements pretty-printing of traces38pub 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	// fn print_trace(46	// 	&self,47	// 	evaluation_state: &EvaluationState,48	// 	error: &LocError,49	// ) -> Result<(), std::fmt::Error> {50	// 	self.write_trace(&mut std::fmt::stdout(), evaluation_state, error)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}7778/// vanilla-like jsonnet formatting79pub 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					// TODO: Process all trace elements first131					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}199200/// rustc-like trace displaying201#[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}