git.delta.rocks / jrsonnet / refs/commits / 458a1bc63c56

difftreelog

refactor extrace trace format to separate crate

Лач2020-07-16parent: #3b3945f.patch.diff
in: master

4 files changed

modifiedcmds/jrsonnet/Cargo.tomldiffbeforeafterboth
--- a/cmds/jrsonnet/Cargo.toml
+++ b/cmds/jrsonnet/Cargo.toml
@@ -11,11 +11,9 @@
 [dependencies]
 jrsonnet-evaluator = { path = "../../crates/jrsonnet-evaluator", version = "1.0.0" }
 jrsonnet-parser = { path = "../../crates/jrsonnet-parser", version = "1.0.0" }
-annotate-snippets = "0.8.0"
+jrsonnet-trace = { path = "../../crates/jrsonnet-trace", version = "1.0.0" }
 # TODO: Fix mimalloc compile errors, and use them
 mimallocator = "0.1.3"
 
 [dependencies.clap]
 version = "3.0.0-beta.1"
-default-features = false
-features = ["std", "derive"]
modifiedcmds/jrsonnet/src/main.rsdiffbeforeafterboth
--- a/cmds/jrsonnet/src/main.rs
+++ b/cmds/jrsonnet/src/main.rs
@@ -1,6 +1,7 @@
 use clap::Clap;
-use jrsonnet_evaluator::{trace::CodeLocation, EvaluationState, LocError, StackTrace, Val};
+use jrsonnet_evaluator::Val;
 use jrsonnet_parser::{el, Arg, ArgsDesc, Expr, LocExpr, ParserSettings};
+use jrsonnet_trace::{CompactFormat, ExplainingFormat, PathResolver, TraceFormat};
 use std::env::current_dir;
 use std::{collections::HashMap, path::PathBuf, rc::Rc, str::FromStr};
 
@@ -26,18 +27,16 @@
 }
 
 #[derive(PartialEq)]
-enum TraceFormat {
-	CppJsonnet,
-	GoJsonnet,
-	Custom,
+enum TraceFormatName {
+	Compact,
+	Explaining,
 }
-impl FromStr for TraceFormat {
+impl FromStr for TraceFormatName {
 	type Err = &'static str;
 	fn from_str(s: &str) -> Result<Self, Self::Err> {
 		Ok(match s {
-			"cpp" => TraceFormat::CppJsonnet,
-			"go" => TraceFormat::GoJsonnet,
-			"default" => TraceFormat::Custom,
+			"compact" => TraceFormatName::Compact,
+			"explaining" => TraceFormatName::Explaining,
 			_ => return Err("no such format"),
 		})
 	}
@@ -67,8 +66,8 @@
 }
 
 #[derive(Clap)]
-#[clap(version = "0.1.0", author = "Lach <iam@lach.pw>")]
-struct Opts {
+#[clap(name = "jrsonnet", version, author)]
+pub struct Opts {
 	#[clap(long, about = "Disable global std variable")]
 	no_stdlib: bool,
 	#[clap(long, about = "Add external string", number_of_values = 1)]
@@ -81,8 +80,8 @@
 	tla_code: Vec<ExtStr>,
 	#[clap(long, short = "f", default_value = "json", possible_values = &["none", "json", "yaml"], about = "Output format, wraps resulting value to corresponding std.manifest call")]
 	format: Format,
-	#[clap(long, default_value = "default", possible_values = &["cpp", "go", "default"], about = "Emulated needed stacktrace display")]
-	trace_format: TraceFormat,
+	#[clap(long, default_value = "compact", possible_values = &["compact", "explaining"], about = "Choose format of displayed stacktraces")]
+	trace_format: TraceFormatName,
 
 	#[clap(
 		long,
@@ -149,11 +148,18 @@
 	for ExtStr { name, value } in opts.ext_code.iter().cloned() {
 		evaluator.add_ext_var(name.into(), evaluator.parse_evaluate_raw(&value).unwrap());
 	}
+
+	let resolver = PathResolver::Relative(std::env::current_dir().unwrap());
+	let trace_format: Box<dyn TraceFormat> = match opts.trace_format {
+		TraceFormatName::Compact => Box::new(CompactFormat { resolver }),
+		TraceFormatName::Explaining => Box::new(ExplainingFormat { resolver }),
+	};
+
 	let mut input = current_dir().unwrap();
 	input.push(opts.input.clone());
 	let code_string = String::from_utf8(std::fs::read(opts.input.clone()).unwrap()).unwrap();
-	if let Err(e) = evaluator.add_file(Rc::new(input.clone()), code_string.clone().into()) {
-		print_syntax_error(e, &input, &code_string);
+	if let Err(e) = evaluator.add_file(Rc::new(input.clone()), code_string.into()) {
+		trace_format.print_trace(&evaluator, &e).unwrap();
 		std::process::exit(1);
 	}
 	let result = evaluator.evaluate_file(&input);
@@ -191,16 +197,13 @@
 			};
 			let v = evaluator.run_in_state(|| match opts.format {
 				Format::Json => Ok(Val::Str(v.into_json(opts.line_padding)?)),
-				Format::Yaml => {
-					evaluator.add_global("__tmp__to_yaml__".into(), v);
-					evaluator.parse_evaluate_raw("std.manifestYamlDoc(__tmp__to_yaml__, \"  \")")
-				}
+				Format::Yaml => Ok(Val::Str(v.into_yaml(opts.line_padding)?)),
 				_ => Ok(v),
 			});
 			let v = match v {
 				Ok(v) => v,
 				Err(err) => {
-					print_error(&err, evaluator, &opts);
+					trace_format.print_trace(&evaluator, &err).unwrap();
 					std::process::exit(1);
 				}
 			};
@@ -213,128 +216,8 @@
 			}
 		}
 		Err(err) => {
-			print_error(&err, evaluator, &opts);
+			trace_format.print_trace(&evaluator, &err).unwrap();
 			std::process::exit(1);
 		}
-	}
-}
-
-fn print_error(err: &LocError, evaluator: EvaluationState, opts: &Opts) {
-	println!("Error: {:?}", err.0);
-	print_trace(&(err.1), evaluator, &opts);
-}
-
-fn print_syntax_error(error: jrsonnet_parser::ParseError, file: &PathBuf, code: &str) {
-	use annotate_snippets::{
-		display_list::{DisplayList, FormatOptions},
-		snippet::{Annotation, AnnotationType, Slice, Snippet, SourceAnnotation},
-	};
-	//&("Expected: ".to_owned() + error.expected)
-	let origin = file.to_str().unwrap();
-	let error_message = format!("Expected: {}", error.expected);
-	let snippet = Snippet {
-		opt: FormatOptions {
-			color: true,
-			..Default::default()
-		},
-		title: Some(Annotation {
-			label: Some(&error_message),
-			id: None,
-			annotation_type: AnnotationType::Error,
-		}),
-		footer: vec![],
-		slices: vec![Slice {
-			source: &code,
-			line_start: 1,
-			origin: Some(origin),
-			fold: false,
-			annotations: vec![SourceAnnotation {
-				label: "At this position",
-				annotation_type: AnnotationType::Error,
-				range: (error.location.offset, error.location.offset + 1),
-			}],
-		}],
-	};
-
-	let dl = DisplayList::from(snippet);
-	println!("{}", dl);
-}
-
-fn print_trace(trace: &StackTrace, evaluator: EvaluationState, opts: &Opts) {
-	use annotate_snippets::{
-		display_list::{DisplayList, FormatOptions},
-		snippet::{Annotation, AnnotationType, Slice, Snippet, SourceAnnotation},
-	};
-	for item in trace.0.iter() {
-		let desc = &item.1;
-		let source = item.0.clone();
-		let start_end = evaluator.map_source_locations(&source.0, &[source.1, source.2]);
-		if opts.trace_format == TraceFormat::Custom {
-			let source_fragment: String = evaluator
-				.get_source(&source.0)
-				.unwrap()
-				.chars()
-				.skip(start_end[0].line_start_offset)
-				.take(start_end[1].line_end_offset - start_end[0].line_start_offset)
-				.collect();
-			let snippet = Snippet {
-				opt: FormatOptions {
-					color: true,
-					..Default::default()
-				},
-				title: Some(Annotation {
-					label: Some(&item.1),
-					id: None,
-					annotation_type: AnnotationType::Error,
-				}),
-				footer: vec![],
-				slices: vec![Slice {
-					source: &source_fragment,
-					line_start: start_end[0].line,
-					origin: Some(&source.0.to_str().unwrap()),
-					fold: false,
-					annotations: vec![SourceAnnotation {
-						label: desc,
-						annotation_type: AnnotationType::Error,
-						range: (
-							source.1 - start_end[0].line_start_offset,
-							source.2 - start_end[0].line_start_offset,
-						),
-					}],
-				}],
-			};
-
-			let dl = DisplayList::from(snippet);
-			println!("{}", dl);
-		} else {
-			print_jsonnet_pair(
-				source.0.to_str().unwrap(),
-				&start_end[0],
-				&start_end[1],
-				opts.trace_format == TraceFormat::GoJsonnet,
-			);
-		}
-	}
-}
-
-fn print_jsonnet_pair(file: &str, start: &CodeLocation, end: &CodeLocation, is_go: bool) {
-	if is_go {
-		print!("        ");
-	} else {
-		print!("  ");
-	}
-	print!("{}:", file);
-	if start.line == end.line {
-		// IDK why, but this is the behavior original jsonnet cpp impl shows
-		if start.column == end.column || !is_go && start.column + 1 == end.column {
-			println!("{}:{}", start.line, end.column)
-		} else {
-			println!("{}:{}-{}", start.line, start.column, end.column);
-		}
-	} else {
-		println!(
-			"({}:{})-({}:{})",
-			start.line, end.column, start.line, end.column
-		);
 	}
 }
addedcrates/jrsonnet-trace/Cargo.tomldiffbeforeafterboth
--- /dev/null
+++ b/crates/jrsonnet-trace/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+name = "jrsonnet-trace"
+version = "1.0.0"
+authors = ["Лач <iam@lach.pw>"]
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+jrsonnet-evaluator = { path = "../jrsonnet-evaluator", version = "1.0.0" }
+jrsonnet-parser = { path = "../jrsonnet-parser", version = "1.0.0" }
+pathdiff = "0.2.0"
+annotate-snippets = "0.9.0"
addedcrates/jrsonnet-trace/src/lib.rsdiffbeforeafterboth
after · crates/jrsonnet-trace/src/lib.rs
1use jrsonnet_evaluator::{2	trace::{offset_to_location, CodeLocation},3	EvaluationState, LocError,4};5use std::path::PathBuf;67/// How paths should be displayed8pub enum PathResolver {9	/// Only filename will be shown10	FileName,11	/// Absolute path of file12	Absolute,13	/// Relative path from base directory14	Relative(PathBuf),15}1617impl PathResolver {18	pub fn resolve(&self, from: &PathBuf) -> String {19		match self {20			PathResolver::FileName => from.file_name().unwrap().to_string_lossy().into_owned(),21			PathResolver::Absolute => from.to_string_lossy().into_owned(),22			PathResolver::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 trace to string pretty-printing36pub trait TraceFormat {37	fn write_trace(38		&self,39		out: &mut dyn std::io::Write,40		evaluation_state: &EvaluationState,41		error: &LocError,42	) -> Result<(), std::io::Error>;43	fn print_trace(44		&self,45		evaluation_state: &EvaluationState,46		error: &LocError,47	) -> Result<(), std::io::Error> {48		self.write_trace(&mut std::io::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 - 1,69			start.line,70			end.column71		)?;72	}73	Ok(())74}7576/// vanilla jsonnet like formatting77pub struct CompactFormat {78	pub resolver: PathResolver,79}8081impl TraceFormat for CompactFormat {82	fn write_trace(83		&self,84		out: &mut dyn std::io::Write,85		evaluation_state: &EvaluationState,86		error: &LocError,87	) -> Result<(), std::io::Error> {88		writeln!(out, "{:?}", error.0)?;89		let file_names = (error.1)90			.091			.iter()92			.map(|el| {93				let resolved_path = self.resolver.resolve(&(el.0).0);94				// TODO: Process all trace elements first95				let location =96					evaluation_state.map_source_locations(&(el.0).0, &[(el.0).1, (el.0).2]);97				(resolved_path, location)98			})99			.map(|(mut n, location)| {100				use std::fmt::Write;101				write!(n, ":").unwrap();102				print_code_location(&mut n, &location[0], &location[1]).unwrap();103				n104			})105			.collect::<Vec<_>>();106		let align = file_names.iter().map(|e| e.len()).max().unwrap_or(0);107		for (i, (el, file)) in (error.1).0.iter().zip(file_names).enumerate() {108			if i != 0 {109				writeln!(out)?;110			}111			write!(out, "{:<w$}: {}", file, el.1, w = align)?;112		}113		Ok(())114	}115}116117/// rustc-like trace displaying118pub struct ExplainingFormat {119	pub resolver: PathResolver,120}121impl TraceFormat for ExplainingFormat {122	fn write_trace(123		&self,124		out: &mut dyn std::io::Write,125		evaluation_state: &EvaluationState,126		error: &LocError,127	) -> Result<(), std::io::Error> {128		use annotate_snippets::{129			display_list::{DisplayList, FormatOptions},130			snippet::{AnnotationType, Slice, Snippet, SourceAnnotation},131		};132		writeln!(out, "{:?}", error.0)?;133		let trace = &error.1;134		for item in trace.0.iter() {135			let desc = &item.1;136			let source = item.0.clone();137			let start_end = evaluation_state.map_source_locations(&source.0, &[source.1, source.2]);138139			let source_fragment: String = evaluation_state140				.get_source(&source.0)141				.unwrap()142				.chars()143				.skip(start_end[0].line_start_offset)144				.take(start_end[1].line_end_offset - start_end[0].line_start_offset)145				.collect();146147			let origin = self.resolver.resolve(&source.0);148			let snippet = Snippet {149				opt: FormatOptions {150					color: true,151					..Default::default()152				},153				title: None,154				footer: vec![],155				slices: vec![Slice {156					source: &source_fragment,157					line_start: start_end[0].line,158					origin: Some(&origin),159					fold: false,160					annotations: vec![SourceAnnotation {161						label: desc,162						annotation_type: AnnotationType::Error,163						range: (164							source.1 - start_end[0].line_start_offset,165							source.2 - start_end[0].line_start_offset,166						),167					}],168				}],169			};170171			let dl = DisplayList::from(snippet);172			writeln!(out, "{}", dl)?;173		}174		Ok(())175	}176}