git.delta.rocks / jrsonnet / refs/commits / 590966465ed7

difftreelog

source

crates/jrsonnet-evaluator/src/trace/mod.rs6.4 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 => 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		s: &State,43		error: &LocError,44	) -> Result<(), std::fmt::Error>;45}4647fn print_code_location(48	out: &mut impl std::fmt::Write,49	start: &CodeLocation,50	end: &CodeLocation,51) -> Result<(), std::fmt::Error> {52	if start.line == end.line {53		if start.column == end.column {54			write!(out, "{}:{}", start.line, end.column - 1)?;55		} else {56			write!(out, "{}:{}-{}", start.line, start.column - 1, end.column)?;57		}58	} else {59		write!(60			out,61			"{}:{}-{}:{}",62			start.line,63			end.column.saturating_sub(1),64			start.line,65			end.column66		)?;67	}68	Ok(())69}7071/// vanilla-like jsonnet formatting72pub struct CompactFormat {73	pub resolver: PathResolver,74	pub padding: usize,75}7677impl TraceFormat for CompactFormat {78	fn write_trace(79		&self,80		out: &mut dyn std::fmt::Write,81		s: &State,82		error: &LocError,83	) -> Result<(), std::fmt::Error> {84		write!(out, "{}", error.error())?;85		if let Error::ImportSyntaxError {86			path,87			source_code,88			error,89		} = error.error()90		{91			writeln!(out)?;92			use std::fmt::Write;93			let mut n = self.resolver.resolve(path);94			let mut offset = error.location.offset;95			let is_eof = if offset >= source_code.len() {96				offset = source_code.len().saturating_sub(1);97				true98			} else {99				false100			};101			let mut location = offset_to_location(source_code, &[offset])102				.into_iter()103				.next()104				.unwrap();105			if is_eof {106				location.column += 1;107			}108109			write!(n, ":").unwrap();110			print_code_location(&mut n, &location, &location).unwrap();111			write!(out, "{:<p$}{}", "", n, p = self.padding,)?;112		}113		let file_names = error114			.trace()115			.0116			.iter()117			.map(|el| &el.location)118			.map(|location| {119				use std::fmt::Write;120				#[allow(clippy::option_if_let_else)]121				if let Some(location) = location {122					let mut resolved_path = self.resolver.resolve(&location.0);123					// TODO: Process all trace elements first124					let location = s.map_source_locations(&location.0, &[location.1, location.2]);125					write!(resolved_path, ":").unwrap();126					print_code_location(&mut resolved_path, &location[0], &location[1]).unwrap();127					write!(resolved_path, ":").unwrap();128					Some(resolved_path)129				} else {130					None131				}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			if let Some(file) = file {143				write!(144					out,145					"{:<p$}{:<w$} {}",146					"",147					file,148					el.desc,149					p = self.padding,150					w = align151				)?;152			} else {153				write!(out, "{:<p$}{}", "", el.desc, p = self.padding,)?;154			}155		}156		Ok(())157	}158}159160pub struct JsFormat;161impl TraceFormat for JsFormat {162	fn write_trace(163		&self,164		out: &mut dyn std::fmt::Write,165		s: &State,166		error: &LocError,167	) -> Result<(), std::fmt::Error> {168		write!(out, "{}", error.error())?;169		for item in error.trace().0.iter() {170			writeln!(out)?;171			let desc = &item.desc;172			if let Some(source) = &item.location {173				let start_end = s.map_source_locations(&source.0, &[source.1, source.2]);174175				write!(176					out,177					"    at {} ({}:{}:{})",178					desc,179					source.0.to_str().unwrap(),180					start_end[0].line,181					start_end[0].column,182				)?;183			} else {184				write!(out, "    during {}", desc)?;185			}186		}187		Ok(())188	}189}190191/// rustc-like trace displaying192#[cfg(feature = "explaining-traces")]193pub struct ExplainingFormat {194	pub resolver: PathResolver,195}196#[cfg(feature = "explaining-traces")]197impl TraceFormat for ExplainingFormat {198	fn write_trace(199		&self,200		out: &mut dyn std::fmt::Write,201		s: &State,202		error: &LocError,203	) -> Result<(), std::fmt::Error> {204		write!(out, "{}", error.error())?;205		if let Error::ImportSyntaxError {206			path,207			source_code,208			error,209		} = error.error()210		{211			writeln!(out)?;212			let offset = error.location.offset;213			let location = offset_to_location(source_code, &[offset])214				.into_iter()215				.next()216				.unwrap();217			let mut end_location = location.clone();218			end_location.offset += 1;219220			self.print_snippet(221				out,222				source_code,223				path,224				&location,225				&end_location,226				"syntax error",227			)?;228		}229		let trace = &error.trace();230		for item in trace.0.iter() {231			writeln!(out)?;232			let desc = &item.desc;233			if let Some(source) = &item.location {234				let start_end = s.map_source_locations(&source.0, &[source.1, source.2]);235				self.print_snippet(236					out,237					&s.get_source(&source.0).unwrap(),238					&source.0,239					&start_end[0],240					&start_end[1],241					desc,242				)?;243			} else {244				write!(out, "{}", desc)?;245			}246		}247		Ok(())248	}249}250251impl ExplainingFormat {252	fn print_snippet(253		&self,254		out: &mut dyn std::fmt::Write,255		source: &str,256		origin: &Path,257		start: &CodeLocation,258		end: &CodeLocation,259		desc: &str,260	) -> Result<(), std::fmt::Error> {261		use annotate_snippets::{262			display_list::{DisplayList, FormatOptions},263			snippet::{AnnotationType, Slice, Snippet, SourceAnnotation},264		};265266		let source_fragment: String = source267			.chars()268			.skip(start.line_start_offset)269			.take(end.line_end_offset - end.line_start_offset)270			.collect();271272		let origin = self.resolver.resolve(origin);273		let snippet = Snippet {274			opt: FormatOptions {275				color: true,276				..Default::default()277			},278			title: None,279			footer: vec![],280			slices: vec![Slice {281				source: &source_fragment,282				line_start: start.line,283				origin: Some(&origin),284				fold: false,285				annotations: vec![SourceAnnotation {286					label: desc,287					annotation_type: AnnotationType::Error,288					range: (289						start.offset - start.line_start_offset,290						(end.offset - start.line_start_offset).min(source_fragment.len()),291					),292				}],293			}],294		};295296		let dl = DisplayList::from(snippet);297		write!(out, "{}", dl)?;298299		Ok(())300	}301}