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

difftreelog

source

crates/jrsonnet-evaluator/src/trace/mod.rs6.9 KiBsourcehistory
1mod location;23use std::path::{Path, PathBuf};45use jrsonnet_parser::Source;6pub use location::*;78use crate::{error::Error, LocError, State};910/// The way paths should be displayed11pub enum PathResolver {12	/// Only filename13	FileName,14	/// Absolute path15	Absolute,16	/// Path relative to base directory17	Relative(PathBuf),18}1920impl PathResolver {21	pub fn resolve(&self, from: &Path) -> String {22		match self {23			Self::FileName => from24				.file_name()25				.expect("file name exists")26				.to_string_lossy()27				.into_owned(),28			Self::Absolute => from.to_string_lossy().into_owned(),29			Self::Relative(base) => {30				if from.is_relative() {31					return from.to_string_lossy().into_owned();32				}33				pathdiff::diff_paths(from, base)34					.expect("base is absolute")35					.to_string_lossy()36					.into_owned()37			}38		}39	}40}4142/// Implements pretty-printing of traces43#[allow(clippy::module_name_repetitions)]44pub trait TraceFormat {45	fn write_trace(46		&self,47		out: &mut dyn std::fmt::Write,48		s: &State,49		error: &LocError,50	) -> Result<(), std::fmt::Error>;51}5253fn print_code_location(54	out: &mut impl std::fmt::Write,55	start: &CodeLocation,56	end: &CodeLocation,57) -> Result<(), std::fmt::Error> {58	if start.line == end.line {59		if start.column == end.column {60			write!(out, "{}:{}", start.line, end.column.saturating_sub(1))?;61		} else {62			write!(out, "{}:{}-{}", start.line, start.column - 1, end.column)?;63		}64	} else {65		write!(66			out,67			"{}:{}-{}:{}",68			start.line,69			end.column.saturating_sub(1),70			start.line,71			end.column72		)?;73	}74	Ok(())75}7677/// vanilla-like jsonnet formatting78pub struct CompactFormat {79	pub resolver: PathResolver,80	pub padding: usize,81}8283impl TraceFormat for CompactFormat {84	fn write_trace(85		&self,86		out: &mut dyn std::fmt::Write,87		s: &State,88		error: &LocError,89	) -> Result<(), std::fmt::Error> {90		write!(out, "{}", error.error())?;91		if let Error::ImportSyntaxError {92			path,93			source_code,94			error,95		} = error.error()96		{97			use std::fmt::Write;9899			writeln!(out)?;100			let mut n = match path.repr() {101				Ok(r) => self.resolver.resolve(r),102				Err(v) => v.to_string(),103			};104			let mut offset = error.location.offset;105			let is_eof = if offset >= source_code.len() {106				offset = source_code.len().saturating_sub(1);107				true108			} else {109				false110			};111			let mut location = offset_to_location(source_code, &[offset as u32])112				.into_iter()113				.next()114				.unwrap();115			if is_eof {116				location.column += 1;117			}118119			write!(n, ":").unwrap();120			print_code_location(&mut n, &location, &location).unwrap();121			write!(out, "{:<p$}{}", "", n, p = self.padding,)?;122		}123		let file_names = error124			.trace()125			.0126			.iter()127			.map(|el| &el.location)128			.map(|location| {129				use std::fmt::Write;130				#[allow(clippy::option_if_let_else)]131				if let Some(location) = location {132					let mut resolved_path = match location.0.repr() {133						Ok(r) => self.resolver.resolve(r),134						Err(v) => v.to_string(),135					};136					// TODO: Process all trace elements first137					let location =138						s.map_source_locations(location.0.clone(), &[location.1, location.2]);139					write!(resolved_path, ":").unwrap();140					print_code_location(&mut resolved_path, &location[0], &location[1]).unwrap();141					write!(resolved_path, ":").unwrap();142					Some(resolved_path)143				} else {144					None145				}146			})147			.collect::<Vec<_>>();148		let align = file_names149			.iter()150			.flatten()151			.map(String::len)152			.max()153			.unwrap_or(0);154		for (el, file) in error.trace().0.iter().zip(file_names) {155			writeln!(out)?;156			if let Some(file) = file {157				write!(158					out,159					"{:<p$}{:<w$} {}",160					"",161					file,162					el.desc,163					p = self.padding,164					w = align165				)?;166			} else {167				write!(out, "{:<p$}{}", "", el.desc, p = self.padding,)?;168			}169		}170		Ok(())171	}172}173174pub struct JsFormat;175impl TraceFormat for JsFormat {176	fn write_trace(177		&self,178		out: &mut dyn std::fmt::Write,179		s: &State,180		error: &LocError,181	) -> Result<(), std::fmt::Error> {182		write!(out, "{}", error.error())?;183		for item in &error.trace().0 {184			writeln!(out)?;185			let desc = &item.desc;186			if let Some(source) = &item.location {187				let start_end = s.map_source_locations(source.0.clone(), &[source.1, source.2]);188				let resolved_path = match source.0.repr() {189					Ok(r) => r.display().to_string(),190					Err(v) => v.to_string(),191				};192193				write!(194					out,195					"    at {} ({}:{}:{})",196					desc, resolved_path, start_end[0].line, start_end[0].column,197				)?;198			} else {199				write!(out, "    during {}", desc)?;200			}201		}202		Ok(())203	}204}205206/// rustc-like trace displaying207#[cfg(feature = "explaining-traces")]208pub struct ExplainingFormat {209	pub resolver: PathResolver,210}211#[cfg(feature = "explaining-traces")]212impl TraceFormat for ExplainingFormat {213	fn write_trace(214		&self,215		out: &mut dyn std::fmt::Write,216		s: &State,217		error: &LocError,218	) -> Result<(), std::fmt::Error> {219		write!(out, "{}", error.error())?;220		if let Error::ImportSyntaxError {221			path,222			source_code,223			error,224		} = error.error()225		{226			writeln!(out)?;227			let offset = error.location.offset;228			let location = offset_to_location(source_code, &[offset as u32])229				.into_iter()230				.next()231				.unwrap();232			let mut end_location = location.clone();233			end_location.offset += 1;234235			self.print_snippet(236				out,237				source_code,238				path,239				&location,240				&end_location,241				"syntax error",242			)?;243		}244		let trace = &error.trace();245		for item in &trace.0 {246			writeln!(out)?;247			let desc = &item.desc;248			if let Some(source) = &item.location {249				let start_end = s.map_source_locations(source.0.clone(), &[source.1, source.2]);250				self.print_snippet(251					out,252					&s.get_source(source.0.clone()).unwrap(),253					&source.0,254					&start_end[0],255					&start_end[1],256					desc,257				)?;258			} else {259				write!(out, "{}", desc)?;260			}261		}262		Ok(())263	}264}265266impl ExplainingFormat {267	fn print_snippet(268		&self,269		out: &mut dyn std::fmt::Write,270		source: &str,271		origin: &Source,272		start: &CodeLocation,273		end: &CodeLocation,274		desc: &str,275	) -> Result<(), std::fmt::Error> {276		use annotate_snippets::{277			display_list::{DisplayList, FormatOptions},278			snippet::{AnnotationType, Slice, Snippet, SourceAnnotation},279		};280281		let source_fragment: String = source282			.chars()283			.skip(start.line_start_offset)284			.take(end.line_end_offset - end.line_start_offset)285			.collect();286287		let origin = match origin.repr() {288			Ok(r) => self.resolver.resolve(r),289			Err(v) => v.to_string(),290		};291		let snippet = Snippet {292			opt: FormatOptions {293				color: true,294				..FormatOptions::default()295			},296			title: None,297			footer: vec![],298			slices: vec![Slice {299				source: &source_fragment,300				line_start: start.line,301				origin: Some(&origin),302				fold: false,303				annotations: vec![SourceAnnotation {304					label: desc,305					annotation_type: AnnotationType::Error,306					range: (307						start.offset - start.line_start_offset,308						(end.offset - start.line_start_offset).min(source_fragment.len()),309					),310				}],311			}],312		};313314		let dl = DisplayList::from(snippet);315		write!(out, "{}", dl)?;316317		Ok(())318	}319}