difftreelog
refactor extrace trace format to separate crate
in: master
4 files changed
cmds/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"]
cmds/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
- );
}
}
crates/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"
crates/jrsonnet-trace/src/lib.rsdiffbeforeafterboth1use 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}