git.delta.rocks / jrsonnet / refs/commits / 94fa86f59bc6

difftreelog

source

crates/jrsonnet-evaluator/src/trace/mod.rs6.5 KiBsourcehistory
1mod location;23use std::path::{Path, PathBuf};45pub use location::*;67use crate::{error::Error, LocError, State};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 => from23				.file_name()24				.expect("file name exists")25				.to_string_lossy()26				.into_owned(),27			Self::Absolute => from.to_string_lossy().into_owned(),28			Self::Relative(base) => {29				if from.is_relative() {30					return from.to_string_lossy().into_owned();31				}32				pathdiff::diff_paths(from, base)33					.expect("base is absolute")34					.to_string_lossy()35					.into_owned()36			}37		}38	}39}4041/// Implements pretty-printing of traces42#[allow(clippy::module_name_repetitions)]43pub trait TraceFormat {44	fn write_trace(45		&self,46		out: &mut dyn std::fmt::Write,47		s: &State,48		error: &LocError,49	) -> Result<(), std::fmt::Error>;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		s: &State,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			use std::fmt::Write;9798			writeln!(out)?;99			let mut n = self.resolver.resolve(path);100			let mut offset = error.location.offset;101			let is_eof = if offset >= source_code.len() {102				offset = source_code.len().saturating_sub(1);103				true104			} else {105				false106			};107			let mut location = offset_to_location(source_code, &[offset])108				.into_iter()109				.next()110				.unwrap();111			if is_eof {112				location.column += 1;113			}114115			write!(n, ":").unwrap();116			print_code_location(&mut n, &location, &location).unwrap();117			write!(out, "{:<p$}{}", "", n, p = self.padding,)?;118		}119		let file_names = error120			.trace()121			.0122			.iter()123			.map(|el| &el.location)124			.map(|location| {125				use std::fmt::Write;126				#[allow(clippy::option_if_let_else)]127				if let Some(location) = location {128					let mut resolved_path = self.resolver.resolve(&location.0);129					// TODO: Process all trace elements first130					let location = s.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(String::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		s: &State,172		error: &LocError,173	) -> Result<(), std::fmt::Error> {174		write!(out, "{}", error.error())?;175		for item in &error.trace().0 {176			writeln!(out)?;177			let desc = &item.desc;178			if let Some(source) = &item.location {179				let start_end = s.map_source_locations(&source.0, &[source.1, source.2]);180181				write!(182					out,183					"    at {} ({}:{}:{})",184					desc,185					source.0.to_str().unwrap(),186					start_end[0].line,187					start_end[0].column,188				)?;189			} else {190				write!(out, "    during {}", desc)?;191			}192		}193		Ok(())194	}195}196197/// rustc-like trace displaying198#[cfg(feature = "explaining-traces")]199pub struct ExplainingFormat {200	pub resolver: PathResolver,201}202#[cfg(feature = "explaining-traces")]203impl TraceFormat for ExplainingFormat {204	fn write_trace(205		&self,206		out: &mut dyn std::fmt::Write,207		s: &State,208		error: &LocError,209	) -> Result<(), std::fmt::Error> {210		write!(out, "{}", error.error())?;211		if let Error::ImportSyntaxError {212			path,213			source_code,214			error,215		} = error.error()216		{217			writeln!(out)?;218			let offset = error.location.offset;219			let location = offset_to_location(source_code, &[offset])220				.into_iter()221				.next()222				.unwrap();223			let mut end_location = location.clone();224			end_location.offset += 1;225226			self.print_snippet(227				out,228				source_code,229				path,230				&location,231				&end_location,232				"syntax error",233			)?;234		}235		let trace = &error.trace();236		for item in &trace.0 {237			writeln!(out)?;238			let desc = &item.desc;239			if let Some(source) = &item.location {240				let start_end = s.map_source_locations(&source.0, &[source.1, source.2]);241				self.print_snippet(242					out,243					&s.get_source(&source.0).unwrap(),244					&source.0,245					&start_end[0],246					&start_end[1],247					desc,248				)?;249			} else {250				write!(out, "{}", desc)?;251			}252		}253		Ok(())254	}255}256257impl ExplainingFormat {258	fn print_snippet(259		&self,260		out: &mut dyn std::fmt::Write,261		source: &str,262		origin: &Path,263		start: &CodeLocation,264		end: &CodeLocation,265		desc: &str,266	) -> Result<(), std::fmt::Error> {267		use annotate_snippets::{268			display_list::{DisplayList, FormatOptions},269			snippet::{AnnotationType, Slice, Snippet, SourceAnnotation},270		};271272		let source_fragment: String = source273			.chars()274			.skip(start.line_start_offset)275			.take(end.line_end_offset - end.line_start_offset)276			.collect();277278		let origin = self.resolver.resolve(origin);279		let snippet = Snippet {280			opt: FormatOptions {281				color: true,282				..FormatOptions::default()283			},284			title: None,285			footer: vec![],286			slices: vec![Slice {287				source: &source_fragment,288				line_start: start.line,289				origin: Some(&origin),290				fold: false,291				annotations: vec![SourceAnnotation {292					label: desc,293					annotation_type: AnnotationType::Error,294					range: (295						start.offset - start.line_start_offset,296						(end.offset - start.line_start_offset).min(source_fragment.len()),297					),298				}],299			}],300		};301302		let dl = DisplayList::from(snippet);303		write!(out, "{}", dl)?;304305		Ok(())306	}307}