difftreelog
refactor remove trace format from state
in: master
5 files changed
cmds/jrsonnet/src/main.rsdiffbeforeafterboth--- a/cmds/jrsonnet/src/main.rs
+++ b/cmds/jrsonnet/src/main.rs
@@ -51,6 +51,9 @@
input: InputOpts,
#[clap(flatten)]
general: GeneralOpts,
+
+ #[clap(flatten)]
+ trace: TraceOpts,
#[clap(flatten)]
manifest: ManifestOpts,
#[clap(flatten)]
@@ -114,9 +117,15 @@
fn main_catch(opts: Opts) -> bool {
let s = State::default();
+ let trace = opts
+ .trace
+ .configure(&s)
+ .expect("this configurator doesn't fail");
if let Err(e) = main_real(&s, opts) {
if let Error::Evaluation(e) = e {
- eprintln!("{}", s.stringify_err(&e));
+ let mut out = String::new();
+ trace.write_trace(&mut out, &e).expect("format error");
+ eprintln!("{out}")
} else {
eprintln!("{}", e);
}
crates/jrsonnet-cli/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-cli/src/lib.rs
+++ b/crates/jrsonnet-cli/src/lib.rs
@@ -76,9 +76,6 @@
std: StdOpts,
#[clap(flatten)]
- trace: TraceOpts,
-
- #[clap(flatten)]
gc: GcOpts,
}
@@ -90,7 +87,6 @@
);
fn configure(&self, s: &State) -> Result<Self::Guards> {
// Configure trace first, because tla-code/ext-code can throw
- self.trace.configure(s)?;
let misc_guards = self.misc.configure(s)?;
let tla_guards = self.tla.configure(s)?;
self.std.configure(s)?;
crates/jrsonnet-cli/src/trace.rsdiffbeforeafterboth--- a/crates/jrsonnet-cli/src/trace.rs
+++ b/crates/jrsonnet-cli/src/trace.rs
@@ -1,7 +1,7 @@
use clap::{Parser, ValueEnum};
use jrsonnet_evaluator::{
error::Result,
- trace::{CompactFormat, ExplainingFormat, PathResolver},
+ trace::{CompactFormat, ExplainingFormat, PathResolver, TraceFormat},
State,
};
@@ -27,21 +27,25 @@
max_trace: usize,
}
impl ConfigureState for TraceOpts {
- type Guards = ();
- fn configure(&self, s: &State) -> Result<()> {
+ type Guards = Box<dyn TraceFormat>;
+ fn configure(&self, _s: &State) -> Result<Self::Guards> {
let resolver = PathResolver::new_cwd_fallback();
- match self
+ let max_trace = self.max_trace;
+ let format: Box<dyn TraceFormat> = match self
.trace_format
.as_ref()
.unwrap_or(&TraceFormatName::Compact)
{
- TraceFormatName::Compact => s.set_trace_format(CompactFormat {
+ TraceFormatName::Compact => Box::new(CompactFormat {
resolver,
padding: 4,
+ max_trace,
}),
- TraceFormatName::Explaining => s.set_trace_format(ExplainingFormat { resolver }),
- }
- s.set_max_trace(self.max_trace);
- Ok(())
+ TraceFormatName::Explaining => Box::new(ExplainingFormat {
+ resolver,
+ max_trace,
+ }),
+ };
+ Ok(format)
}
}
crates/jrsonnet-evaluator/src/error.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/error.rs
+++ b/crates/jrsonnet-evaluator/src/error.rs
@@ -262,7 +262,6 @@
write!(f, "\t{}", el.desc)?;
if let Some(loc) = &el.location {
write!(f, "at {}", loc.0 .0 .0)?;
- // loc.0
loc.0.map_source_locations(&[loc.1, loc.2]);
}
writeln!(f)?;
crates/jrsonnet-evaluator/src/trace/mod.rsdiffbeforeafterboth1use std::path::{Path, PathBuf};23use jrsonnet_gcmodule::Trace;4use jrsonnet_parser::{CodeLocation, Source};56use crate::{error::Error, LocError, State};78/// The way paths should be displayed9#[derive(Clone, Trace)]10pub enum PathResolver {11 /// Only filename12 FileName,13 /// Absolute path14 Absolute,15 /// Path relative to base directory16 Relative(PathBuf),17}1819impl PathResolver {20 /// Will return `Self::Relative(cwd)`, or `Self::Absolute` on cwd failure21 pub fn new_cwd_fallback() -> Self {22 std::env::current_dir().map_or(Self::Absolute, Self::Relative)23 }24 pub fn resolve(&self, from: &Path) -> String {25 match self {26 Self::FileName => from27 .file_name()28 .expect("file name exists")29 .to_string_lossy()30 .into_owned(),31 Self::Absolute => from.to_string_lossy().into_owned(),32 Self::Relative(base) => {33 if from.is_relative() {34 return from.to_string_lossy().into_owned();35 }36 pathdiff::diff_paths(from, base)37 .expect("base is absolute")38 .to_string_lossy()39 .into_owned()40 }41 }42 }43}4445/// Implements pretty-printing of traces46#[allow(clippy::module_name_repetitions)]47pub trait TraceFormat: Trace {48 fn write_trace(49 &self,50 out: &mut dyn std::fmt::Write,51 s: &State,52 error: &LocError,53 ) -> Result<(), std::fmt::Error>;54}5556fn print_code_location(57 out: &mut impl std::fmt::Write,58 start: &CodeLocation,59 end: &CodeLocation,60) -> Result<(), std::fmt::Error> {61 if start.line == end.line {62 if start.column == end.column {63 write!(out, "{}:{}", start.line, end.column.saturating_sub(1))?;64 } else {65 write!(out, "{}:{}-{}", start.line, start.column - 1, end.column)?;66 }67 } else {68 write!(69 out,70 "{}:{}-{}:{}",71 start.line,72 end.column.saturating_sub(1),73 start.line,74 end.column75 )?;76 }77 Ok(())78}7980/// vanilla-like jsonnet formatting81#[derive(Trace)]82pub struct CompactFormat {83 pub resolver: PathResolver,84 pub padding: usize,85}8687impl TraceFormat for CompactFormat {88 fn write_trace(89 &self,90 out: &mut dyn std::fmt::Write,91 _s: &State,92 error: &LocError,93 ) -> Result<(), std::fmt::Error> {94 write!(out, "{}", error.error())?;95 if let Error::ImportSyntaxError { path, error } = error.error() {96 use std::fmt::Write;9798 writeln!(out)?;99 let mut n = path.source_path().path().map_or_else(100 || path.source_path().to_string(),101 |r| self.resolver.resolve(r),102 );103 let mut offset = error.location.offset;104 let is_eof = if offset >= path.code().len() {105 offset = path.code().len().saturating_sub(1);106 true107 } else {108 false109 };110 let mut location = path111 .map_source_locations(&[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.source_path().path() {133 Some(r) => self.resolver.resolve(r),134 None => location.0.source_path().to_string(),135 };136 // TODO: Process all trace elements first137 let location = location.0.map_source_locations(&[location.1, location.2]);138 write!(resolved_path, ":").unwrap();139 print_code_location(&mut resolved_path, &location[0], &location[1]).unwrap();140 write!(resolved_path, ":").unwrap();141 Some(resolved_path)142 } else {143 None144 }145 })146 .collect::<Vec<_>>();147 let align = file_names148 .iter()149 .flatten()150 .map(String::len)151 .max()152 .unwrap_or(0);153 for (el, file) in error.trace().0.iter().zip(file_names) {154 writeln!(out)?;155 if let Some(file) = file {156 write!(157 out,158 "{:<p$}{:<w$} {}",159 "",160 file,161 el.desc,162 p = self.padding,163 w = align164 )?;165 } else {166 write!(out, "{:<p$}{}", "", el.desc, p = self.padding,)?;167 }168 }169 Ok(())170 }171}172173#[derive(Trace)]174pub 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 = source.0.map_source_locations(&[source.1, source.2]);188 let resolved_path = source.0.source_path().path().map_or_else(189 || source.0.source_path().to_string(),190 |r| r.display().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")]208#[derive(Trace)]209pub struct ExplainingFormat {210 pub resolver: PathResolver,211}212#[cfg(feature = "explaining-traces")]213impl TraceFormat for ExplainingFormat {214 fn write_trace(215 &self,216 out: &mut dyn std::fmt::Write,217 _s: &State,218 error: &LocError,219 ) -> Result<(), std::fmt::Error> {220 write!(out, "{}", error.error())?;221 if let Error::ImportSyntaxError { path, error } = error.error() {222 writeln!(out)?;223 let offset = error.location.offset;224 let location = path225 .map_source_locations(&[offset as u32])226 .into_iter()227 .next()228 .unwrap();229 let mut end_location = location;230 end_location.offset += 1;231232 self.print_snippet(233 out,234 path.code(),235 path,236 &location,237 &end_location,238 "syntax error",239 )?;240 }241 let trace = &error.trace();242 for item in &trace.0 {243 writeln!(out)?;244 let desc = &item.desc;245 if let Some(source) = &item.location {246 let start_end = source.0.map_source_locations(&[source.1, source.2]);247 self.print_snippet(248 out,249 source.0.code(),250 &source.0,251 &start_end[0],252 &start_end[1],253 desc,254 )?;255 } else {256 write!(out, "{desc}")?;257 }258 }259 Ok(())260 }261}262263impl ExplainingFormat {264 fn print_snippet(265 &self,266 out: &mut dyn std::fmt::Write,267 source: &str,268 origin: &Source,269 start: &CodeLocation,270 end: &CodeLocation,271 desc: &str,272 ) -> Result<(), std::fmt::Error> {273 use annotate_snippets::{274 display_list::{DisplayList, FormatOptions},275 snippet::{AnnotationType, Slice, Snippet, SourceAnnotation},276 };277278 let source_fragment: String = source279 .chars()280 .skip(start.line_start_offset)281 .take(end.line_end_offset - end.line_start_offset)282 .collect();283284 let origin = origin.source_path().path().map_or_else(285 || origin.source_path().to_string(),286 |r| self.resolver.resolve(r),287 );288 let snippet = Snippet {289 opt: FormatOptions {290 color: true,291 ..FormatOptions::default()292 },293 title: None,294 footer: vec![],295 slices: vec![Slice {296 source: &source_fragment,297 line_start: start.line,298 origin: Some(&origin),299 fold: false,300 annotations: vec![SourceAnnotation {301 label: desc,302 annotation_type: AnnotationType::Error,303 range: (304 start.offset - start.line_start_offset,305 (end.offset.saturating_sub(start.line_start_offset))306 .min(source_fragment.len()),307 ),308 }],309 }],310 };311312 let dl = DisplayList::from(snippet);313 write!(out, "{dl}")?;314315 Ok(())316 }317}1use std::{2 any::Any,3 path::{Path, PathBuf},4};56use jrsonnet_gcmodule::Trace;7use jrsonnet_parser::{CodeLocation, Source};89use crate::{error::Error, LocError};1011/// The way paths should be displayed12#[derive(Clone, Trace)]13pub enum PathResolver {14 /// Only filename15 FileName,16 /// Absolute path17 Absolute,18 /// Path relative to base directory19 Relative(PathBuf),20}2122impl PathResolver {23 /// Will return `Self::Relative(cwd)`, or `Self::Absolute` on cwd failure24 pub fn new_cwd_fallback() -> Self {25 std::env::current_dir().map_or(Self::Absolute, Self::Relative)26 }27 pub fn resolve(&self, from: &Path) -> String {28 match self {29 Self::FileName => from30 .file_name()31 .expect("file name exists")32 .to_string_lossy()33 .into_owned(),34 Self::Absolute => from.to_string_lossy().into_owned(),35 Self::Relative(base) => {36 if from.is_relative() {37 return from.to_string_lossy().into_owned();38 }39 pathdiff::diff_paths(from, base)40 .expect("base is absolute")41 .to_string_lossy()42 .into_owned()43 }44 }45 }46}4748/// Implements pretty-printing of traces49#[allow(clippy::module_name_repetitions)]50pub trait TraceFormat: Trace {51 fn write_trace(52 &self,53 out: &mut dyn std::fmt::Write,54 error: &LocError,55 ) -> Result<(), std::fmt::Error>;56 fn format(&self, error: &LocError) -> Result<String, std::fmt::Error> {57 let mut out = String::new();58 self.write_trace(&mut out, error)?;59 Ok(out)60 }61 fn as_any(&self) -> &dyn Any;62 fn as_any_mut(&mut self) -> &mut dyn Any;63}6465fn print_code_location(66 out: &mut impl std::fmt::Write,67 start: &CodeLocation,68 end: &CodeLocation,69) -> Result<(), std::fmt::Error> {70 if start.line == end.line {71 if start.column == end.column {72 write!(out, "{}:{}", start.line, end.column.saturating_sub(1))?;73 } else {74 write!(out, "{}:{}-{}", start.line, start.column - 1, end.column)?;75 }76 } else {77 write!(78 out,79 "{}:{}-{}:{}",80 start.line,81 end.column.saturating_sub(1),82 start.line,83 end.column84 )?;85 }86 Ok(())87}8889/// vanilla-like jsonnet formatting90#[derive(Trace)]91pub struct CompactFormat {92 pub resolver: PathResolver,93 pub max_trace: usize,94 pub padding: usize,95}96impl Default for CompactFormat {97 fn default() -> Self {98 Self {99 resolver: PathResolver::Absolute,100 max_trace: 20,101 padding: 4,102 }103 }104}105106impl TraceFormat for CompactFormat {107 fn write_trace(108 &self,109 out: &mut dyn std::fmt::Write,110 error: &LocError,111 ) -> Result<(), std::fmt::Error> {112 write!(out, "{}", error.error())?;113 if let Error::ImportSyntaxError { path, error } = error.error() {114 use std::fmt::Write;115116 writeln!(out)?;117 let mut n = path.source_path().path().map_or_else(118 || path.source_path().to_string(),119 |r| self.resolver.resolve(r),120 );121 let mut offset = error.location.offset;122 let is_eof = if offset >= path.code().len() {123 offset = path.code().len().saturating_sub(1);124 true125 } else {126 false127 };128 let mut location = path129 .map_source_locations(&[offset as u32])130 .into_iter()131 .next()132 .unwrap();133 if is_eof {134 location.column += 1;135 }136137 write!(n, ":").unwrap();138 print_code_location(&mut n, &location, &location).unwrap();139 write!(out, "{:<p$}{n}", "", p = self.padding)?;140 }141 let file_names = error142 .trace()143 .0144 .iter()145 .map(|el| &el.location)146 .map(|location| {147 use std::fmt::Write;148 #[allow(clippy::option_if_let_else)]149 if let Some(location) = location {150 let mut resolved_path = match location.0.source_path().path() {151 Some(r) => self.resolver.resolve(r),152 None => location.0.source_path().to_string(),153 };154 // TODO: Process all trace elements first155 let location = location.0.map_source_locations(&[location.1, location.2]);156 write!(resolved_path, ":").unwrap();157 print_code_location(&mut resolved_path, &location[0], &location[1]).unwrap();158 write!(resolved_path, ":").unwrap();159 Some(resolved_path)160 } else {161 None162 }163 })164 .collect::<Vec<_>>();165 let align = file_names166 .iter()167 .flatten()168 .map(String::len)169 .max()170 .unwrap_or(0);171 for (el, file) in error.trace().0.iter().zip(file_names) {172 writeln!(out)?;173 if let Some(file) = file {174 write!(175 out,176 "{:<p$}{:<w$} {}",177 "",178 file,179 el.desc,180 p = self.padding,181 w = align182 )?;183 } else {184 write!(out, "{:<p$}{}", "", el.desc, p = self.padding,)?;185 }186 }187 Ok(())188 }189190 fn as_any(&self) -> &dyn Any {191 self192 }193194 fn as_any_mut(&mut self) -> &mut dyn Any {195 self196 }197}198199#[derive(Trace)]200pub struct JsFormat {201 pub max_trace: usize,202}203impl TraceFormat for JsFormat {204 fn write_trace(205 &self,206 out: &mut dyn std::fmt::Write,207 error: &LocError,208 ) -> Result<(), std::fmt::Error> {209 write!(out, "{}", error.error())?;210 for item in &error.trace().0 {211 writeln!(out)?;212 let desc = &item.desc;213 if let Some(source) = &item.location {214 let start_end = source.0.map_source_locations(&[source.1, source.2]);215 let resolved_path = source.0.source_path().path().map_or_else(216 || source.0.source_path().to_string(),217 |r| r.display().to_string(),218 );219220 write!(221 out,222 " at {} ({}:{}:{})",223 desc, resolved_path, start_end[0].line, start_end[0].column,224 )?;225 } else {226 write!(out, " during {desc}")?;227 }228 }229 Ok(())230 }231232 fn as_any(&self) -> &dyn Any {233 self234 }235236 fn as_any_mut(&mut self) -> &mut dyn Any {237 self238 }239}240241/// rustc-like trace displaying242#[cfg(feature = "explaining-traces")]243#[derive(Trace)]244pub struct ExplainingFormat {245 pub resolver: PathResolver,246 pub max_trace: usize,247}248#[cfg(feature = "explaining-traces")]249impl TraceFormat for ExplainingFormat {250 fn write_trace(251 &self,252 out: &mut dyn std::fmt::Write,253 error: &LocError,254 ) -> Result<(), std::fmt::Error> {255 write!(out, "{}", error.error())?;256 if let Error::ImportSyntaxError { path, error } = error.error() {257 writeln!(out)?;258 let offset = error.location.offset;259 let location = path260 .map_source_locations(&[offset as u32])261 .into_iter()262 .next()263 .unwrap();264 let mut end_location = location;265 end_location.offset += 1;266267 self.print_snippet(268 out,269 path.code(),270 path,271 &location,272 &end_location,273 "syntax error",274 )?;275 }276 let trace = &error.trace();277 for item in &trace.0 {278 writeln!(out)?;279 let desc = &item.desc;280 if let Some(source) = &item.location {281 let start_end = source.0.map_source_locations(&[source.1, source.2]);282 self.print_snippet(283 out,284 source.0.code(),285 &source.0,286 &start_end[0],287 &start_end[1],288 desc,289 )?;290 } else {291 write!(out, "{desc}")?;292 }293 }294 Ok(())295 }296297 fn as_any(&self) -> &dyn Any {298 self299 }300301 fn as_any_mut(&mut self) -> &mut dyn Any {302 self303 }304}305306impl ExplainingFormat {307 fn print_snippet(308 &self,309 out: &mut dyn std::fmt::Write,310 source: &str,311 origin: &Source,312 start: &CodeLocation,313 end: &CodeLocation,314 desc: &str,315 ) -> Result<(), std::fmt::Error> {316 use annotate_snippets::{317 display_list::{DisplayList, FormatOptions},318 snippet::{AnnotationType, Slice, Snippet, SourceAnnotation},319 };320321 let source_fragment: String = source322 .chars()323 .skip(start.line_start_offset)324 .take(end.line_end_offset - end.line_start_offset)325 .collect();326327 let origin = origin.source_path().path().map_or_else(328 || origin.source_path().to_string(),329 |r| self.resolver.resolve(r),330 );331 let snippet = Snippet {332 opt: FormatOptions {333 color: true,334 ..FormatOptions::default()335 },336 title: None,337 footer: vec![],338 slices: vec![Slice {339 source: &source_fragment,340 line_start: start.line,341 origin: Some(&origin),342 fold: false,343 annotations: vec![SourceAnnotation {344 label: desc,345 annotation_type: AnnotationType::Error,346 range: (347 start.offset - start.line_start_offset,348 (end.offset.saturating_sub(start.line_start_offset))349 .min(source_fragment.len()),350 ),351 }],352 }],353 };354355 let dl = DisplayList::from(snippet);356 write!(out, "{dl}")?;357358 Ok(())359 }360}