difftreelog
feat(evaluator) custom source paths
in: master
5 files changed
crates/jrsonnet-evaluator/src/error.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/error.rs
+++ b/crates/jrsonnet-evaluator/src/error.rs
@@ -137,15 +137,21 @@
StandaloneSuper,
#[error("can't resolve {1} from {0}")]
- ImportFileNotFound(PathBuf, String),
+ ImportFileNotFound(SourcePath, String),
+ #[error("can't resolve absolute {0}")]
+ AbsoluteImportFileNotFound(PathBuf),
#[error("resolved file not found: {:?}", .0)]
ResolvedFileNotFound(SourcePath),
+ #[error("can't import {0}: is a directory")]
+ ImportIsADirectory(SourcePath),
#[error("imported file is not valid utf-8: {0:?}")]
ImportBadFileUtf8(SourcePath),
#[error("import io error: {0}")]
ImportIo(String),
- #[error("tried to import {1} from {0}, but imports is not supported")]
- ImportNotSupported(PathBuf, PathBuf),
+ #[error("tried to import {1} from {0}, but imports are not supported")]
+ ImportNotSupported(SourcePath, String),
+ #[error("tried to import {0}, but absolute imports are not supported")]
+ AbsoluteImportNotSupported(PathBuf),
#[error("can't import from virtual file")]
CantImportFromVirtualFile,
#[error(
crates/jrsonnet-evaluator/src/evaluate/mod.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/evaluate/mod.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate/mod.rs
@@ -630,15 +630,7 @@
}
i @ (Import(path) | ImportStr(path) | ImportBin(path)) => {
let tmp = loc.clone().0;
- let import_location = tmp
- .path()
- .map(|p| {
- let mut p = p.to_owned();
- p.pop();
- p
- })
- .unwrap_or_default();
- let resolved_path = s.resolve_file(&import_location, path as &str)?;
+ let resolved_path = s.resolve_from(tmp.source_path(), path as &str)?;
match i {
Import(_) => s.push(
CallLocation::new(loc),
crates/jrsonnet-evaluator/src/import.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/import.rs
+++ b/crates/jrsonnet-evaluator/src/import.rs
@@ -1,51 +1,60 @@
use std::{
any::Any,
+ cell::RefCell,
+ env::current_dir,
fs,
- io::Read,
+ io::{ErrorKind, Read},
path::{Path, PathBuf},
};
use fs::File;
-use jrsonnet_parser::SourcePath;
+use jrsonnet_parser::{SourceDirectory, SourceFile, SourcePath};
use crate::{
- error::{Error::*, Result},
+ error::{
+ Error::{self, *},
+ Result,
+ },
throw,
};
/// Implements file resolution logic for `import` and `importStr`
pub trait ImportResolver {
- /// Resolves real file path, e.g. `(/home/user/manifests, b.libjsonnet)` can correspond
+ /// Resolves file path, e.g. `(/home/user/manifests, b.libjsonnet)` can correspond
/// both to `/home/user/manifests/b.libjsonnet` and to `/home/user/${vendor}/b.libjsonnet`
/// where `${vendor}` is a library path.
- fn resolve_file_relative(&self, from: &Path, path: &str) -> Result<SourcePath>;
+ ///
+ /// `from` should only be returned from [`ImportResolver::resolve`], or from other defined file, any other value
+ /// may result in panic
+ fn resolve_from(&self, from: &SourcePath, path: &str) -> Result<SourcePath> {
+ throw!(ImportNotSupported(from.clone(), path.into()))
+ }
+ fn resolve_from_default(&self, path: &str) -> Result<SourcePath> {
+ self.resolve_from(&SourcePath::default(), path)
+ }
+ /// Resolves absolute path, doesn't supports jpath and other fancy things
+ fn resolve(&self, path: &Path) -> Result<SourcePath> {
+ throw!(AbsoluteImportNotSupported(path.to_owned()))
+ }
/// Load resolved file
- /// This should only be called with value returned from `resolve_file`, this cannot be resolved using associated type,
- /// as evaluator uses object instead of generic for [`ImportResolver`]
+ /// This should only be called with value returned from [`ImportResolver::resolve_file`]/[`ImportResolver::resolve`],
+ /// this cannot be resolved using associated type, as evaluator uses object instead of generic for [`ImportResolver`]
fn load_file_contents(&self, resolved: &SourcePath) -> Result<Vec<u8>>;
- /// # Safety
- ///
- /// For use only in bindings, should not be used elsewhere.
- /// Implementations which are not intended to be used in bindings
- /// should panic on call to this method.
- unsafe fn as_any(&self) -> &dyn Any;
+ /// For downcasts
+ fn as_any(&self) -> &dyn Any;
}
/// Dummy resolver, can't resolve/load any file
pub struct DummyImportResolver;
impl ImportResolver for DummyImportResolver {
- fn resolve_file_relative(&self, from: &Path, path: &str) -> Result<SourcePath> {
- throw!(ImportNotSupported(from.into(), path.into()))
- }
-
fn load_file_contents(&self, _resolved: &SourcePath) -> Result<Vec<u8>> {
panic!("dummy resolver can't load any file")
}
- unsafe fn as_any(&self) -> &dyn Any {
- panic!("`as_any(&self)` is not supported by dummy resolver")
+ fn as_any(&self) -> &dyn Any {
+ self
}
}
#[allow(clippy::use_self)]
@@ -60,36 +69,82 @@
pub struct FileImportResolver {
/// Library directories to search for file.
/// Referred to as `jpath` in original jsonnet implementation.
- pub library_paths: Vec<PathBuf>,
+ library_paths: RefCell<Vec<PathBuf>>,
}
+impl FileImportResolver {
+ pub fn new(jpath: Vec<PathBuf>) -> Self {
+ Self {
+ library_paths: RefCell::new(jpath),
+ }
+ }
+ /// Dynamically add new jpath, used by bindings
+ pub fn add_jpath(&self, path: PathBuf) {
+ self.library_paths.borrow_mut().push(path);
+ }
+}
impl ImportResolver for FileImportResolver {
- fn resolve_file_relative(&self, from: &Path, path: &str) -> Result<SourcePath> {
- let mut direct = from.to_path_buf();
+ fn resolve_from(&self, from: &SourcePath, path: &str) -> Result<SourcePath> {
+ let mut direct = if let Some(f) = from.downcast_ref::<SourceFile>() {
+ let mut o = f.path().to_owned();
+ o.pop();
+ o
+ } else if let Some(d) = from.downcast_ref::<SourceDirectory>() {
+ d.path().to_owned()
+ } else if from.is_default() {
+ current_dir().map_err(|e| Error::ImportIo(e.to_string()))?
+ } else {
+ unreachable!("resolver can't return this path")
+ };
direct.push(path);
- if direct.exists() {
- Ok(SourcePath::Path(
+ if direct.is_file() {
+ Ok(SourcePath::new(SourceFile::new(
direct.canonicalize().map_err(|e| ImportIo(e.to_string()))?,
- ))
+ )))
} else {
- for library_path in &self.library_paths {
+ for library_path in self.library_paths.borrow().iter() {
let mut cloned = library_path.clone();
cloned.push(path);
if cloned.exists() {
- return Ok(SourcePath::Path(
+ return Ok(SourcePath::new(SourceFile::new(
cloned.canonicalize().map_err(|e| ImportIo(e.to_string()))?,
- ));
+ )));
}
}
- throw!(ImportFileNotFound(from.to_owned(), path.to_owned()))
+ throw!(ImportFileNotFound(from.clone(), path.to_owned()))
+ }
+ }
+ fn resolve(&self, path: &Path) -> Result<SourcePath> {
+ let meta = match fs::metadata(path) {
+ Ok(v) => v,
+ Err(e) if e.kind() == ErrorKind::NotFound => {
+ throw!(AbsoluteImportFileNotFound(path.to_owned()))
+ }
+ Err(e) => throw!(Error::ImportIo(e.to_string())),
+ };
+ if meta.is_file() {
+ Ok(SourcePath::new(SourceFile::new(
+ path.canonicalize()
+ .map_err(|e| ImportIo(e.to_string()))?
+ .to_owned(),
+ )))
+ } else if meta.is_dir() {
+ Ok(SourcePath::new(SourceDirectory::new(
+ path.canonicalize()
+ .map_err(|e| ImportIo(e.to_string()))?
+ .to_owned(),
+ )))
+ } else {
+ unreachable!("this can't be a symlink")
}
}
fn load_file_contents(&self, id: &SourcePath) -> Result<Vec<u8>> {
- let path = match id {
- SourcePath::Path(path) => path,
- _ => {
- panic!("this resolver can only resolve to path")
- }
+ let path = if let Some(f) = id.downcast_ref::<SourceFile>() {
+ f.path()
+ } else if id.downcast_ref::<SourceDirectory>().is_some() || id.is_default() {
+ throw!(Error::ImportIsADirectory(id.clone()))
+ } else {
+ unreachable!("other types are not supported in resolve");
};
let mut file = File::open(path).map_err(|_e| ResolvedFileNotFound(id.clone()))?;
let mut out = Vec::new();
@@ -97,7 +152,12 @@
.map_err(|e| ImportIo(e.to_string()))?;
Ok(out)
}
- unsafe fn as_any(&self) -> &dyn Any {
- panic!("this resolver can't be used as any")
+
+ fn as_any(&self) -> &dyn Any {
+ self
+ }
+
+ fn resolve_from_default(&self, path: &str) -> Result<SourcePath> {
+ self.resolve_from(&SourcePath::default(), path)
}
}
crates/jrsonnet-evaluator/src/lib.rsdiffbeforeafterboth1#![warn(clippy::all, clippy::nursery, clippy::pedantic)]2#![allow(3 macro_expanded_macro_exports_accessed_by_absolute_paths,4 clippy::ptr_arg,5 // Too verbose6 clippy::must_use_candidate,7 // A lot of functions pass around errors thrown by code8 clippy::missing_errors_doc,9 // A lot of pointers have interior Rc10 clippy::needless_pass_by_value,11 // Its fine12 clippy::wildcard_imports,13 clippy::enum_glob_use,14 clippy::module_name_repetitions,15 // TODO: fix individual issues, however this works as intended almost everywhere16 clippy::cast_precision_loss,17 clippy::cast_possible_wrap,18 clippy::cast_possible_truncation,19 clippy::cast_sign_loss,20 // False positives21 // https://github.com/rust-lang/rust-clippy/issues/690222 clippy::use_self,23 // https://github.com/rust-lang/rust-clippy/issues/853924 clippy::iter_with_drain,25)]2627// For jrsonnet-macros28extern crate self as jrsonnet_evaluator;2930mod ctx;31mod dynamic;32pub mod error;33mod evaluate;34pub mod function;35pub mod gc;36mod import;37mod integrations;38mod map;39mod obj;40pub mod stdlib;41pub mod trace;42pub mod typed;43pub mod val;4445use std::{46 any::Any,47 borrow::Cow,48 cell::{Ref, RefCell, RefMut},49 collections::HashMap,50 fmt::{self, Debug},51 path::Path,52 rc::Rc,53};5455pub use ctx::*;56pub use dynamic::*;57use error::{Error::*, LocError, Result, StackTraceElement};58pub use evaluate::*;59use function::{CallLocation, TlaArg};60use gc::{GcHashMap, TraceBox};61use hashbrown::hash_map::RawEntryMut;62pub use import::*;63use jrsonnet_gcmodule::{Cc, Trace};64pub use jrsonnet_interner::{IBytes, IStr};65pub use jrsonnet_parser as parser;66use jrsonnet_parser::*;67pub use obj::*;68use trace::{CompactFormat, TraceFormat};69pub use val::{ManifestFormat, Thunk, Val};7071pub trait Unbound: Trace {72 type Bound;73 fn bind(&self, s: State, sup: Option<ObjValue>, this: Option<ObjValue>) -> Result<Self::Bound>;74}7576#[derive(Clone, Trace)]77pub enum LazyBinding {78 Bindable(Cc<TraceBox<dyn Unbound<Bound = Thunk<Val>>>>),79 Bound(Thunk<Val>),80}8182impl Debug for LazyBinding {83 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {84 write!(f, "LazyBinding")85 }86}87impl LazyBinding {88 pub fn evaluate(89 &self,90 s: State,91 sup: Option<ObjValue>,92 this: Option<ObjValue>,93 ) -> Result<Thunk<Val>> {94 match self {95 Self::Bindable(v) => v.bind(s, sup, this),96 Self::Bound(v) => Ok(v.clone()),97 }98 }99}100101/// During import, this trait will be called to create initial context for file102/// It may initialize global variables, stdlib for example103pub trait ContextInitializer {104 fn initialize(&self, state: State, for_file: Source) -> Context;105106 /// # Safety107 ///108 /// For use only in bindings, should not be used elsewhere.109 /// Implementations which are not intended to be used in bindings110 /// should panic on call to this method.111 unsafe fn as_any(&self) -> &dyn Any;112}113114/// Context initializer, which adds noth115pub struct DummyContextInitializer;116impl ContextInitializer for DummyContextInitializer {117 fn initialize(&self, _state: State, _for_file: Source) -> Context {118 Context::default()119 }120 unsafe fn as_any(&self) -> &dyn Any {121 panic!("`as_any(&self)` is not supported by dummy initializer")122 }123}124125pub struct EvaluationSettings {126 /// Limits recursion by limiting the number of stack frames127 pub max_stack: usize,128 /// Limits amount of stack trace items preserved129 pub max_trace: usize,130 /// TLA vars131 pub tla_vars: HashMap<IStr, TlaArg>,132 /// Context initializer, which will be used for imports and everything133 /// [`NoopContextInitializer`] is used by default, most likely you want to have `jrsonnet-stdlib`134 pub context_initializer: Box<dyn ContextInitializer>,135 /// Used to resolve file locations/contents136 pub import_resolver: Box<dyn ImportResolver>,137 /// Used in manifestification functions138 pub manifest_format: ManifestFormat,139 /// Used for bindings140 pub trace_format: Box<dyn TraceFormat>,141}142impl Default for EvaluationSettings {143 fn default() -> Self {144 Self {145 max_stack: 200,146 max_trace: 20,147 context_initializer: Box::new(DummyContextInitializer),148 tla_vars: HashMap::default(),149 import_resolver: Box::new(DummyImportResolver),150 manifest_format: ManifestFormat::Json {151 padding: 4,152 #[cfg(feature = "exp-preserve-order")]153 preserve_order: false,154 },155 trace_format: Box::new(CompactFormat {156 padding: 4,157 resolver: trace::PathResolver::Absolute,158 }),159 }160 }161}162163#[derive(Default)]164struct EvaluationData {165 /// Used for stack overflow detection, stacktrace is populated on unwind166 stack_depth: usize,167 /// Updated every time stack entry is popt168 stack_generation: usize,169170 breakpoints: Breakpoints,171172 /// Contains file source codes and evaluation results for imports and pretty-printed stacktraces173 files: GcHashMap<SourcePath, FileData>,174}175struct FileData {176 string: Option<IStr>,177 bytes: Option<IBytes>,178 parsed: Option<LocExpr>,179 evaluated: Option<Val>,180181 evaluating: bool,182}183impl FileData {184 fn new_string(data: IStr) -> Self {185 Self {186 string: Some(data),187 bytes: None,188 parsed: None,189 evaluated: None,190 evaluating: false,191 }192 }193 fn new_bytes(data: IBytes) -> Self {194 Self {195 string: None,196 bytes: Some(data),197 parsed: None,198 evaluated: None,199 evaluating: false,200 }201 }202}203204#[allow(clippy::type_complexity)]205pub struct Breakpoint {206 loc: ExprLocation,207 collected: RefCell<HashMap<usize, (usize, Vec<Result<Val>>)>>,208}209#[derive(Default)]210struct Breakpoints(Vec<Rc<Breakpoint>>);211impl Breakpoints {212 fn insert(213 &self,214 stack_depth: usize,215 stack_generation: usize,216 loc: &ExprLocation,217 result: Result<Val>,218 ) -> Result<Val> {219 if self.0.is_empty() {220 return result;221 }222 for item in &self.0 {223 if item.loc.belongs_to(loc) {224 let mut collected = item.collected.borrow_mut();225 let (depth, vals) = collected.entry(stack_generation).or_default();226 if stack_depth > *depth {227 vals.clear();228 }229 vals.push(result.clone());230 }231 }232 result233 }234}235236#[derive(Default)]237pub struct EvaluationStateInternals {238 /// Internal state239 data: RefCell<EvaluationData>,240 /// Settings, safe to change at runtime241 settings: RefCell<EvaluationSettings>,242}243244/// Maintains stack trace and import resolution245#[derive(Default, Clone)]246pub struct State(Rc<EvaluationStateInternals>);247248impl State {249 /// Should only be called with path retrieved from [`resolve_path`], may panic otherwise250 pub fn import_resolved_str(&self, path: SourcePath) -> Result<IStr> {251 let mut data = self.data_mut();252 let mut file = data.files.raw_entry_mut().from_key(&path);253254 let file = match file {255 RawEntryMut::Occupied(ref mut d) => d.get_mut(),256 RawEntryMut::Vacant(v) => {257 let data = self.settings().import_resolver.load_file_contents(&path)?;258 v.insert(259 path.clone(),260 FileData::new_string(261 std::str::from_utf8(&data)262 .map_err(|_| ImportBadFileUtf8(path.clone()))?263 .into(),264 ),265 )266 .1267 }268 };269 if let Some(str) = &file.string {270 return Ok(str.clone());271 }272 if file.string.is_none() {273 file.string = Some(274 file.bytes275 .as_ref()276 .expect("either string or bytes should be set")277 .clone()278 .cast_str()279 .ok_or_else(|| ImportBadFileUtf8(path.clone()))?,280 );281 }282 Ok(file.string.as_ref().expect("just set").clone())283 }284 /// Should only be called with path retrieved from [`resolve_path`], may panic otherwise285 pub fn import_resolved_bin(&self, path: SourcePath) -> Result<IBytes> {286 let mut data = self.data_mut();287 let mut file = data.files.raw_entry_mut().from_key(&path);288289 let file = match file {290 RawEntryMut::Occupied(ref mut d) => d.get_mut(),291 RawEntryMut::Vacant(v) => {292 let data = self.settings().import_resolver.load_file_contents(&path)?;293 v.insert(path.clone(), FileData::new_bytes(data.as_slice().into()))294 .1295 }296 };297 if let Some(str) = &file.bytes {298 return Ok(str.clone());299 }300 if file.bytes.is_none() {301 file.bytes = Some(302 file.string303 .as_ref()304 .expect("either string or bytes should be set")305 .clone()306 .cast_bytes(),307 );308 }309 Ok(file.bytes.as_ref().expect("just set").clone())310 }311 /// Should only be called with path retrieved from [`resolve_path`], may panic otherwise312 pub fn import_resolved(&self, path: SourcePath) -> Result<Val> {313 let mut data = self.data_mut();314 let mut file = data.files.raw_entry_mut().from_key(&path);315316 let file = match file {317 RawEntryMut::Occupied(ref mut d) => d.get_mut(),318 RawEntryMut::Vacant(v) => {319 let data = self.settings().import_resolver.load_file_contents(&path)?;320 v.insert(321 path.clone(),322 FileData::new_string(323 std::str::from_utf8(&data)324 .map_err(|_| ImportBadFileUtf8(path.clone()))?325 .into(),326 ),327 )328 .1329 }330 };331 if let Some(val) = &file.evaluated {332 return Ok(val.clone());333 }334 if file.string.is_none() {335 file.string = Some(336 std::str::from_utf8(337 file.bytes338 .as_ref()339 .expect("either string or bytes should be set"),340 )341 .map_err(|_| ImportBadFileUtf8(path.clone()))?342 .into(),343 );344 }345 let code = file.string.as_ref().expect("just set");346 let file_name =347 Source::new(path.clone(), code.clone()).expect("resolver should return correct name");348 if file.parsed.is_none() {349 file.parsed = Some(350 jrsonnet_parser::parse(351 code,352 &ParserSettings {353 file_name: file_name.clone(),354 },355 )356 .map_err(|e| ImportSyntaxError {357 path: file_name.clone(),358 error: Box::new(e),359 })?,360 );361 }362 let parsed = file.parsed.as_ref().expect("just set").clone();363 if file.evaluating {364 throw!(InfiniteRecursionDetected)365 }366 file.evaluating = true;367 // Dropping file here, as it borrows data, which may be used in evaluation368 drop(data);369 let res = evaluate(370 self.clone(),371 self.create_default_context(file_name),372 &parsed,373 );374375 let mut data = self.data_mut();376 let mut file = data.files.raw_entry_mut().from_key(&path);377378 let file = match file {379 RawEntryMut::Occupied(ref mut d) => d.get_mut(),380 RawEntryMut::Vacant(_) => unreachable!("this file was just here!"),381 };382 file.evaluating = false;383 match res {384 Ok(v) => {385 file.evaluated = Some(v.clone());386 Ok(v)387 }388 Err(e) => Err(e),389 }390 }391 pub fn import(&self, from: &Path, path: &str) -> Result<Val> {392 let resolved = self.resolve_file(from, path)?;393 self.import_resolved(resolved)394 }395396 /// Creates context with all passed global variables397 pub fn create_default_context(&self, source: Source) -> Context {398 let context_initializer = &self.settings().context_initializer;399 context_initializer.initialize(self.clone(), source)400 }401402 /// Executes code creating a new stack frame403 pub fn push<T>(404 &self,405 e: CallLocation,406 frame_desc: impl FnOnce() -> String,407 f: impl FnOnce() -> Result<T>,408 ) -> Result<T> {409 {410 let mut data = self.data_mut();411 let stack_depth = &mut data.stack_depth;412 if *stack_depth > self.max_stack() {413 // Error creation uses data, so i drop guard here414 drop(data);415 throw!(StackOverflow);416 }417 *stack_depth += 1;418 }419 let result = f();420 {421 let mut data = self.data_mut();422 data.stack_depth -= 1;423 data.stack_generation += 1;424 }425 if let Err(mut err) = result {426 err.trace_mut().0.push(StackTraceElement {427 location: e.0.cloned(),428 desc: frame_desc(),429 });430 return Err(err);431 }432 result433 }434435 /// Executes code creating a new stack frame436 pub fn push_val(437 &self,438 e: &ExprLocation,439 frame_desc: impl FnOnce() -> String,440 f: impl FnOnce() -> Result<Val>,441 ) -> Result<Val> {442 {443 let mut data = self.data_mut();444 let stack_depth = &mut data.stack_depth;445 if *stack_depth > self.max_stack() {446 // Error creation uses data, so i drop guard here447 drop(data);448 throw!(StackOverflow);449 }450 *stack_depth += 1;451 }452 let mut result = f();453 {454 let mut data = self.data_mut();455 data.stack_depth -= 1;456 data.stack_generation += 1;457 result = data458 .breakpoints459 .insert(data.stack_depth, data.stack_generation, e, result);460 }461 if let Err(mut err) = result {462 err.trace_mut().0.push(StackTraceElement {463 location: Some(e.clone()),464 desc: frame_desc(),465 });466 return Err(err);467 }468 result469 }470 /// Executes code creating a new stack frame471 pub fn push_description<T>(472 &self,473 frame_desc: impl FnOnce() -> String,474 f: impl FnOnce() -> Result<T>,475 ) -> Result<T> {476 {477 let mut data = self.data_mut();478 let stack_depth = &mut data.stack_depth;479 if *stack_depth > self.max_stack() {480 // Error creation uses data, so i drop guard here481 drop(data);482 throw!(StackOverflow);483 }484 *stack_depth += 1;485 }486 let result = f();487 {488 let mut data = self.data_mut();489 data.stack_depth -= 1;490 data.stack_generation += 1;491 }492 if let Err(mut err) = result {493 err.trace_mut().0.push(StackTraceElement {494 location: None,495 desc: frame_desc(),496 });497 return Err(err);498 }499 result500 }501502 /// # Panics503 /// In case of formatting failure504 pub fn stringify_err(&self, e: &LocError) -> String {505 let mut out = String::new();506 self.settings()507 .trace_format508 .write_trace(&mut out, self, e)509 .unwrap();510 out511 }512513 pub fn manifest(&self, val: Val) -> Result<IStr> {514 self.push_description(515 || "manifestification".to_string(),516 || val.manifest(self.clone(), &self.manifest_format()),517 )518 }519 pub fn manifest_multi(&self, val: Val) -> Result<Vec<(IStr, IStr)>> {520 val.manifest_multi(self.clone(), &self.manifest_format())521 }522 pub fn manifest_stream(&self, val: Val) -> Result<Vec<IStr>> {523 val.manifest_stream(self.clone(), &self.manifest_format())524 }525526 /// If passed value is function then call with set TLA527 pub fn with_tla(&self, val: Val) -> Result<Val> {528 Ok(match val {529 Val::Func(func) => self.push_description(530 || "during TLA call".to_owned(),531 || {532 func.evaluate(533 self.clone(),534 self.create_default_context(Source::new_virtual(535 Cow::Borrowed("<tla>"),536 IStr::empty(),537 )),538 CallLocation::native(),539 &self.settings().tla_vars,540 true,541 )542 },543 )?,544 v => v,545 })546 }547}548549/// Internals550impl State {551 // fn data(&self) -> Ref<EvaluationData> {552 // self.0.data.borrow()553 // }554 fn data_mut(&self) -> RefMut<EvaluationData> {555 self.0.data.borrow_mut()556 }557 pub fn settings(&self) -> Ref<EvaluationSettings> {558 self.0.settings.borrow()559 }560 pub fn settings_mut(&self) -> RefMut<EvaluationSettings> {561 self.0.settings.borrow_mut()562 }563}564565/// Raw methods evaluate passed values but don't perform TLA execution566impl State {567 /// Parses and evaluates the given snippet568 pub fn evaluate_snippet(&self, name: String, code: impl Into<IStr>) -> Result<Val> {569 let code = code.into();570 let source = Source::new_virtual(Cow::Owned(name), code.clone());571 let parsed = jrsonnet_parser::parse(572 &code,573 &ParserSettings {574 file_name: source.clone(),575 },576 )577 .map_err(|e| ImportSyntaxError {578 path: source.clone(),579 error: Box::new(e),580 })?;581 evaluate(self.clone(), self.create_default_context(source), &parsed)582 }583}584585/// Settings utilities586impl State {587 pub fn add_tla(&self, name: IStr, value: Val) {588 self.settings_mut()589 .tla_vars590 .insert(name, TlaArg::Val(value));591 }592 pub fn add_tla_str(&self, name: IStr, value: IStr) {593 self.settings_mut()594 .tla_vars595 .insert(name, TlaArg::String(value));596 }597 pub fn add_tla_code(&self, name: IStr, code: &str) -> Result<()> {598 let source_name = format!("<top-level-arg:{}>", name);599 let source = Source::new_virtual(Cow::Owned(source_name), code.into());600 let parsed = jrsonnet_parser::parse(601 code,602 &ParserSettings {603 file_name: source.clone(),604 },605 )606 .map_err(|e| ImportSyntaxError {607 path: source,608 error: Box::new(e),609 })?;610 self.settings_mut()611 .tla_vars612 .insert(name, TlaArg::Code(parsed));613 Ok(())614 }615616 pub fn resolve_file(&self, from: &Path, path: &str) -> Result<SourcePath> {617 self.settings()618 .import_resolver619 .resolve_file_relative(from, path.as_ref())620 }621622 pub fn import_resolver(&self) -> Ref<dyn ImportResolver> {623 Ref::map(self.settings(), |s| &*s.import_resolver)624 }625 pub fn set_import_resolver(&self, resolver: Box<dyn ImportResolver>) {626 self.settings_mut().import_resolver = resolver;627 }628 pub fn context_initializer(&self) -> Ref<dyn ContextInitializer> {629 Ref::map(self.settings(), |s| &*s.context_initializer)630 }631632 pub fn manifest_format(&self) -> ManifestFormat {633 self.settings().manifest_format.clone()634 }635 pub fn set_manifest_format(&self, format: ManifestFormat) {636 self.settings_mut().manifest_format = format;637 }638639 pub fn trace_format(&self) -> Ref<dyn TraceFormat> {640 Ref::map(self.settings(), |s| &*s.trace_format)641 }642 pub fn set_trace_format(&self, format: Box<dyn TraceFormat>) {643 self.settings_mut().trace_format = format;644 }645646 pub fn max_trace(&self) -> usize {647 self.settings().max_trace648 }649 pub fn set_max_trace(&self, trace: usize) {650 self.settings_mut().max_trace = trace;651 }652653 pub fn max_stack(&self) -> usize {654 self.settings().max_stack655 }656 pub fn set_max_stack(&self, trace: usize) {657 self.settings_mut().max_stack = trace;658 }659}1#![warn(clippy::all, clippy::nursery, clippy::pedantic)]2#![allow(3 macro_expanded_macro_exports_accessed_by_absolute_paths,4 clippy::ptr_arg,5 // Too verbose6 clippy::must_use_candidate,7 // A lot of functions pass around errors thrown by code8 clippy::missing_errors_doc,9 // A lot of pointers have interior Rc10 clippy::needless_pass_by_value,11 // Its fine12 clippy::wildcard_imports,13 clippy::enum_glob_use,14 clippy::module_name_repetitions,15 // TODO: fix individual issues, however this works as intended almost everywhere16 clippy::cast_precision_loss,17 clippy::cast_possible_wrap,18 clippy::cast_possible_truncation,19 clippy::cast_sign_loss,20 // False positives21 // https://github.com/rust-lang/rust-clippy/issues/690222 clippy::use_self,23 // https://github.com/rust-lang/rust-clippy/issues/853924 clippy::iter_with_drain,25)]2627// For jrsonnet-macros28extern crate self as jrsonnet_evaluator;2930mod ctx;31mod dynamic;32pub mod error;33mod evaluate;34pub mod function;35pub mod gc;36mod import;37mod integrations;38mod map;39mod obj;40pub mod stdlib;41pub mod trace;42pub mod typed;43pub mod val;4445use std::{46 any::Any,47 cell::{Ref, RefCell, RefMut},48 collections::HashMap,49 fmt::{self, Debug},50 path::Path,51 rc::Rc,52};5354pub use ctx::*;55pub use dynamic::*;56use error::{Error::*, LocError, Result, StackTraceElement};57pub use evaluate::*;58use function::{CallLocation, TlaArg};59use gc::{GcHashMap, TraceBox};60use hashbrown::hash_map::RawEntryMut;61pub use import::*;62use jrsonnet_gcmodule::{Cc, Trace};63pub use jrsonnet_interner::{IBytes, IStr};64pub use jrsonnet_parser as parser;65use jrsonnet_parser::*;66pub use obj::*;67use trace::{CompactFormat, TraceFormat};68pub use val::{ManifestFormat, Thunk, Val};6970pub trait Unbound: Trace {71 type Bound;72 fn bind(&self, s: State, sup: Option<ObjValue>, this: Option<ObjValue>) -> Result<Self::Bound>;73}7475#[derive(Clone, Trace)]76pub enum LazyBinding {77 Bindable(Cc<TraceBox<dyn Unbound<Bound = Thunk<Val>>>>),78 Bound(Thunk<Val>),79}8081impl Debug for LazyBinding {82 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {83 write!(f, "LazyBinding")84 }85}86impl LazyBinding {87 pub fn evaluate(88 &self,89 s: State,90 sup: Option<ObjValue>,91 this: Option<ObjValue>,92 ) -> Result<Thunk<Val>> {93 match self {94 Self::Bindable(v) => v.bind(s, sup, this),95 Self::Bound(v) => Ok(v.clone()),96 }97 }98}99100/// During import, this trait will be called to create initial context for file101/// It may initialize global variables, stdlib for example102pub trait ContextInitializer {103 fn initialize(&self, state: State, for_file: Source) -> Context;104105 fn as_any(&self) -> &dyn Any;106}107108/// Context initializer, which adds noth109pub struct DummyContextInitializer;110impl ContextInitializer for DummyContextInitializer {111 fn initialize(&self, _state: State, _for_file: Source) -> Context {112 Context::default()113 }114 fn as_any(&self) -> &dyn Any {115 self116 }117}118119pub struct EvaluationSettings {120 /// Limits recursion by limiting the number of stack frames121 pub max_stack: usize,122 /// Limits amount of stack trace items preserved123 pub max_trace: usize,124 /// TLA vars125 pub tla_vars: HashMap<IStr, TlaArg>,126 /// Context initializer, which will be used for imports and everything127 /// [`NoopContextInitializer`] is used by default, most likely you want to have `jrsonnet-stdlib`128 pub context_initializer: Box<dyn ContextInitializer>,129 /// Used to resolve file locations/contents130 pub import_resolver: Box<dyn ImportResolver>,131 /// Used in manifestification functions132 pub manifest_format: ManifestFormat,133 /// Used for bindings134 pub trace_format: Box<dyn TraceFormat>,135}136impl Default for EvaluationSettings {137 fn default() -> Self {138 Self {139 max_stack: 200,140 max_trace: 20,141 context_initializer: Box::new(DummyContextInitializer),142 tla_vars: HashMap::default(),143 import_resolver: Box::new(DummyImportResolver),144 manifest_format: ManifestFormat::Json {145 padding: 4,146 #[cfg(feature = "exp-preserve-order")]147 preserve_order: false,148 },149 trace_format: Box::new(CompactFormat {150 padding: 4,151 resolver: trace::PathResolver::Absolute,152 }),153 }154 }155}156157#[derive(Default)]158struct EvaluationData {159 /// Used for stack overflow detection, stacktrace is populated on unwind160 stack_depth: usize,161 /// Updated every time stack entry is popt162 stack_generation: usize,163164 breakpoints: Breakpoints,165166 /// Contains file source codes and evaluation results for imports and pretty-printed stacktraces167 files: GcHashMap<SourcePath, FileData>,168}169struct FileData {170 string: Option<IStr>,171 bytes: Option<IBytes>,172 parsed: Option<LocExpr>,173 evaluated: Option<Val>,174175 evaluating: bool,176}177impl FileData {178 fn new_string(data: IStr) -> Self {179 Self {180 string: Some(data),181 bytes: None,182 parsed: None,183 evaluated: None,184 evaluating: false,185 }186 }187 fn new_bytes(data: IBytes) -> Self {188 Self {189 string: None,190 bytes: Some(data),191 parsed: None,192 evaluated: None,193 evaluating: false,194 }195 }196}197198#[allow(clippy::type_complexity)]199pub struct Breakpoint {200 loc: ExprLocation,201 collected: RefCell<HashMap<usize, (usize, Vec<Result<Val>>)>>,202}203#[derive(Default)]204struct Breakpoints(Vec<Rc<Breakpoint>>);205impl Breakpoints {206 fn insert(207 &self,208 stack_depth: usize,209 stack_generation: usize,210 loc: &ExprLocation,211 result: Result<Val>,212 ) -> Result<Val> {213 if self.0.is_empty() {214 return result;215 }216 for item in &self.0 {217 if item.loc.belongs_to(loc) {218 let mut collected = item.collected.borrow_mut();219 let (depth, vals) = collected.entry(stack_generation).or_default();220 if stack_depth > *depth {221 vals.clear();222 }223 vals.push(result.clone());224 }225 }226 result227 }228}229230#[derive(Default)]231pub struct EvaluationStateInternals {232 /// Internal state233 data: RefCell<EvaluationData>,234 /// Settings, safe to change at runtime235 settings: RefCell<EvaluationSettings>,236}237238/// Maintains stack trace and import resolution239#[derive(Default, Clone)]240pub struct State(Rc<EvaluationStateInternals>);241242impl State {243 /// Should only be called with path retrieved from [`resolve_path`], may panic otherwise244 pub fn import_resolved_str(&self, path: SourcePath) -> Result<IStr> {245 let mut data = self.data_mut();246 let mut file = data.files.raw_entry_mut().from_key(&path);247248 let file = match file {249 RawEntryMut::Occupied(ref mut d) => d.get_mut(),250 RawEntryMut::Vacant(v) => {251 let data = self.settings().import_resolver.load_file_contents(&path)?;252 v.insert(253 path.clone(),254 FileData::new_string(255 std::str::from_utf8(&data)256 .map_err(|_| ImportBadFileUtf8(path.clone()))?257 .into(),258 ),259 )260 .1261 }262 };263 if let Some(str) = &file.string {264 return Ok(str.clone());265 }266 if file.string.is_none() {267 file.string = Some(268 file.bytes269 .as_ref()270 .expect("either string or bytes should be set")271 .clone()272 .cast_str()273 .ok_or_else(|| ImportBadFileUtf8(path.clone()))?,274 );275 }276 Ok(file.string.as_ref().expect("just set").clone())277 }278 /// Should only be called with path retrieved from [`resolve_path`], may panic otherwise279 pub fn import_resolved_bin(&self, path: SourcePath) -> Result<IBytes> {280 let mut data = self.data_mut();281 let mut file = data.files.raw_entry_mut().from_key(&path);282283 let file = match file {284 RawEntryMut::Occupied(ref mut d) => d.get_mut(),285 RawEntryMut::Vacant(v) => {286 let data = self.settings().import_resolver.load_file_contents(&path)?;287 v.insert(path.clone(), FileData::new_bytes(data.as_slice().into()))288 .1289 }290 };291 if let Some(str) = &file.bytes {292 return Ok(str.clone());293 }294 if file.bytes.is_none() {295 file.bytes = Some(296 file.string297 .as_ref()298 .expect("either string or bytes should be set")299 .clone()300 .cast_bytes(),301 );302 }303 Ok(file.bytes.as_ref().expect("just set").clone())304 }305 /// Should only be called with path retrieved from [`resolve_path`], may panic otherwise306 pub fn import_resolved(&self, path: SourcePath) -> Result<Val> {307 let mut data = self.data_mut();308 let mut file = data.files.raw_entry_mut().from_key(&path);309310 let file = match file {311 RawEntryMut::Occupied(ref mut d) => d.get_mut(),312 RawEntryMut::Vacant(v) => {313 let data = self.settings().import_resolver.load_file_contents(&path)?;314 v.insert(315 path.clone(),316 FileData::new_string(317 std::str::from_utf8(&data)318 .map_err(|_| ImportBadFileUtf8(path.clone()))?319 .into(),320 ),321 )322 .1323 }324 };325 if let Some(val) = &file.evaluated {326 return Ok(val.clone());327 }328 if file.string.is_none() {329 file.string = Some(330 std::str::from_utf8(331 file.bytes332 .as_ref()333 .expect("either string or bytes should be set"),334 )335 .map_err(|_| ImportBadFileUtf8(path.clone()))?336 .into(),337 );338 }339 let code = file.string.as_ref().expect("just set");340 let file_name = Source::new(path.clone(), code.clone());341 if file.parsed.is_none() {342 file.parsed = Some(343 jrsonnet_parser::parse(344 code,345 &ParserSettings {346 file_name: file_name.clone(),347 },348 )349 .map_err(|e| ImportSyntaxError {350 path: file_name.clone(),351 error: Box::new(e),352 })?,353 );354 }355 let parsed = file.parsed.as_ref().expect("just set").clone();356 if file.evaluating {357 throw!(InfiniteRecursionDetected)358 }359 file.evaluating = true;360 // Dropping file here, as it borrows data, which may be used in evaluation361 drop(data);362 let res = evaluate(363 self.clone(),364 self.create_default_context(file_name),365 &parsed,366 );367368 let mut data = self.data_mut();369 let mut file = data.files.raw_entry_mut().from_key(&path);370371 let file = match file {372 RawEntryMut::Occupied(ref mut d) => d.get_mut(),373 RawEntryMut::Vacant(_) => unreachable!("this file was just here!"),374 };375 file.evaluating = false;376 match res {377 Ok(v) => {378 file.evaluated = Some(v.clone());379 Ok(v)380 }381 Err(e) => Err(e),382 }383 }384385 /// Has same semantics as `import 'path'` called from `from` file386 pub fn import_from(&self, from: &SourcePath, path: &str) -> Result<Val> {387 let resolved = self.resolve_from(from, path)?;388 self.import_resolved(resolved)389 }390 pub fn import(&self, path: &impl AsRef<Path>) -> Result<Val> {391 let resolved = self.resolve(path)?;392 self.import_resolved(resolved)393 }394395 /// Creates context with all passed global variables396 pub fn create_default_context(&self, source: Source) -> Context {397 let context_initializer = &self.settings().context_initializer;398 context_initializer.initialize(self.clone(), source)399 }400401 /// Executes code creating a new stack frame402 pub fn push<T>(403 &self,404 e: CallLocation,405 frame_desc: impl FnOnce() -> String,406 f: impl FnOnce() -> Result<T>,407 ) -> Result<T> {408 {409 let mut data = self.data_mut();410 let stack_depth = &mut data.stack_depth;411 if *stack_depth > self.max_stack() {412 // Error creation uses data, so i drop guard here413 drop(data);414 throw!(StackOverflow);415 }416 *stack_depth += 1;417 }418 let result = f();419 {420 let mut data = self.data_mut();421 data.stack_depth -= 1;422 data.stack_generation += 1;423 }424 if let Err(mut err) = result {425 err.trace_mut().0.push(StackTraceElement {426 location: e.0.cloned(),427 desc: frame_desc(),428 });429 return Err(err);430 }431 result432 }433434 /// Executes code creating a new stack frame435 pub fn push_val(436 &self,437 e: &ExprLocation,438 frame_desc: impl FnOnce() -> String,439 f: impl FnOnce() -> Result<Val>,440 ) -> Result<Val> {441 {442 let mut data = self.data_mut();443 let stack_depth = &mut data.stack_depth;444 if *stack_depth > self.max_stack() {445 // Error creation uses data, so i drop guard here446 drop(data);447 throw!(StackOverflow);448 }449 *stack_depth += 1;450 }451 let mut result = f();452 {453 let mut data = self.data_mut();454 data.stack_depth -= 1;455 data.stack_generation += 1;456 result = data457 .breakpoints458 .insert(data.stack_depth, data.stack_generation, e, result);459 }460 if let Err(mut err) = result {461 err.trace_mut().0.push(StackTraceElement {462 location: Some(e.clone()),463 desc: frame_desc(),464 });465 return Err(err);466 }467 result468 }469 /// Executes code creating a new stack frame470 pub fn push_description<T>(471 &self,472 frame_desc: impl FnOnce() -> String,473 f: impl FnOnce() -> Result<T>,474 ) -> Result<T> {475 {476 let mut data = self.data_mut();477 let stack_depth = &mut data.stack_depth;478 if *stack_depth > self.max_stack() {479 // Error creation uses data, so i drop guard here480 drop(data);481 throw!(StackOverflow);482 }483 *stack_depth += 1;484 }485 let result = f();486 {487 let mut data = self.data_mut();488 data.stack_depth -= 1;489 data.stack_generation += 1;490 }491 if let Err(mut err) = result {492 err.trace_mut().0.push(StackTraceElement {493 location: None,494 desc: frame_desc(),495 });496 return Err(err);497 }498 result499 }500501 /// # Panics502 /// In case of formatting failure503 pub fn stringify_err(&self, e: &LocError) -> String {504 let mut out = String::new();505 self.settings()506 .trace_format507 .write_trace(&mut out, self, e)508 .unwrap();509 out510 }511512 pub fn manifest(&self, val: Val) -> Result<IStr> {513 self.push_description(514 || "manifestification".to_string(),515 || val.manifest(self.clone(), &self.manifest_format()),516 )517 }518 pub fn manifest_multi(&self, val: Val) -> Result<Vec<(IStr, IStr)>> {519 val.manifest_multi(self.clone(), &self.manifest_format())520 }521 pub fn manifest_stream(&self, val: Val) -> Result<Vec<IStr>> {522 val.manifest_stream(self.clone(), &self.manifest_format())523 }524525 /// If passed value is function then call with set TLA526 pub fn with_tla(&self, val: Val) -> Result<Val> {527 Ok(match val {528 Val::Func(func) => self.push_description(529 || "during TLA call".to_owned(),530 || {531 func.evaluate(532 self.clone(),533 self.create_default_context(Source::new_virtual(534 "<tla>".into(),535 IStr::empty(),536 )),537 CallLocation::native(),538 &self.settings().tla_vars,539 true,540 )541 },542 )?,543 v => v,544 })545 }546}547548/// Internals549impl State {550 // fn data(&self) -> Ref<EvaluationData> {551 // self.0.data.borrow()552 // }553 fn data_mut(&self) -> RefMut<EvaluationData> {554 self.0.data.borrow_mut()555 }556 pub fn settings(&self) -> Ref<EvaluationSettings> {557 self.0.settings.borrow()558 }559 pub fn settings_mut(&self) -> RefMut<EvaluationSettings> {560 self.0.settings.borrow_mut()561 }562}563564/// Raw methods evaluate passed values but don't perform TLA execution565impl State {566 /// Parses and evaluates the given snippet567 pub fn evaluate_snippet(&self, name: impl Into<IStr>, code: impl Into<IStr>) -> Result<Val> {568 let code = code.into();569 let source = Source::new_virtual(name.into(), code.clone());570 let parsed = jrsonnet_parser::parse(571 &code,572 &ParserSettings {573 file_name: source.clone(),574 },575 )576 .map_err(|e| ImportSyntaxError {577 path: source.clone(),578 error: Box::new(e),579 })?;580 evaluate(self.clone(), self.create_default_context(source), &parsed)581 }582}583584/// Settings utilities585impl State {586 pub fn add_tla(&self, name: IStr, value: Val) {587 self.settings_mut()588 .tla_vars589 .insert(name, TlaArg::Val(value));590 }591 pub fn add_tla_str(&self, name: IStr, value: IStr) {592 self.settings_mut()593 .tla_vars594 .insert(name, TlaArg::String(value));595 }596 pub fn add_tla_code(&self, name: IStr, code: &str) -> Result<()> {597 let source_name = format!("<top-level-arg:{}>", name);598 let source = Source::new_virtual(source_name.into(), code.into());599 let parsed = jrsonnet_parser::parse(600 code,601 &ParserSettings {602 file_name: source.clone(),603 },604 )605 .map_err(|e| ImportSyntaxError {606 path: source,607 error: Box::new(e),608 })?;609 self.settings_mut()610 .tla_vars611 .insert(name, TlaArg::Code(parsed));612 Ok(())613 }614615 // Only panics in case of [`ImportResolver`] contract violation616 #[allow(clippy::missing_panics_doc)]617 pub fn resolve_from(&self, from: &SourcePath, path: &str) -> Result<SourcePath> {618 self.import_resolver().resolve_from(from, path.as_ref())619 }620621 // Only panics in case of [`ImportResolver`] contract violation622 #[allow(clippy::missing_panics_doc)]623 pub fn resolve(&self, path: &impl AsRef<Path>) -> Result<SourcePath> {624 self.import_resolver().resolve(path.as_ref())625 }626 pub fn import_resolver(&self) -> Ref<dyn ImportResolver> {627 Ref::map(self.settings(), |s| &*s.import_resolver)628 }629 pub fn set_import_resolver(&self, resolver: Box<dyn ImportResolver>) {630 self.settings_mut().import_resolver = resolver;631 }632 pub fn context_initializer(&self) -> Ref<dyn ContextInitializer> {633 Ref::map(self.settings(), |s| &*s.context_initializer)634 }635636 pub fn manifest_format(&self) -> ManifestFormat {637 self.settings().manifest_format.clone()638 }639 pub fn set_manifest_format(&self, format: ManifestFormat) {640 self.settings_mut().manifest_format = format;641 }642643 pub fn trace_format(&self) -> Ref<dyn TraceFormat> {644 Ref::map(self.settings(), |s| &*s.trace_format)645 }646 pub fn set_trace_format(&self, format: Box<dyn TraceFormat>) {647 self.settings_mut().trace_format = format;648 }649650 pub fn max_trace(&self) -> usize {651 self.settings().max_trace652 }653 pub fn set_max_trace(&self, trace: usize) {654 self.settings_mut().max_trace = trace;655 }656657 pub fn max_stack(&self) -> usize {658 self.settings().max_stack659 }660 pub fn set_max_stack(&self, trace: usize) {661 self.settings_mut().max_stack = trace;662 }663}crates/jrsonnet-evaluator/src/trace/mod.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/trace/mod.rs
+++ b/crates/jrsonnet-evaluator/src/trace/mod.rs
@@ -5,6 +5,7 @@
use crate::{error::Error, LocError, State};
/// The way paths should be displayed
+#[derive(Clone)]
pub enum PathResolver {
/// Only filename
FileName,
@@ -15,6 +16,13 @@
}
impl PathResolver {
+ /// Will return Self::Relative(cwd), or Self::Absolute on cwd failure
+ pub fn new_cwd_fallback() -> Self {
+ match std::env::current_dir() {
+ Ok(v) => Self::Relative(v),
+ Err(_) => Self::Absolute,
+ }
+ }
pub fn resolve(&self, from: &Path) -> String {
match self {
Self::FileName => from
@@ -89,9 +97,9 @@
use std::fmt::Write;
writeln!(out)?;
- let mut n = match path.path() {
+ let mut n = match path.source_path().path() {
Some(r) => self.resolver.resolve(r),
- None => path.short_display().to_string(),
+ None => path.source_path().to_string(),
};
let mut offset = error.location.offset;
let is_eof = if offset >= path.code().len() {
@@ -122,9 +130,9 @@
use std::fmt::Write;
#[allow(clippy::option_if_let_else)]
if let Some(location) = location {
- let mut resolved_path = match location.0.path() {
+ let mut resolved_path = match location.0.source_path().path() {
Some(r) => self.resolver.resolve(r),
- None => location.0.short_display().to_string(),
+ None => location.0.source_path().to_string(),
};
// TODO: Process all trace elements first
let location = location.0.map_source_locations(&[location.1, location.2]);
@@ -177,9 +185,9 @@
let desc = &item.desc;
if let Some(source) = &item.location {
let start_end = source.0.map_source_locations(&[source.1, source.2]);
- let resolved_path = match source.0.path() {
+ let resolved_path = match source.0.source_path().path() {
Some(r) => r.display().to_string(),
- None => source.0.short_display().to_string(),
+ None => source.0.source_path().to_string(),
};
write!(
@@ -272,9 +280,9 @@
.take(end.line_end_offset - end.line_start_offset)
.collect();
- let origin = match origin.path() {
+ let origin = match origin.source_path().path() {
Some(r) => self.resolver.resolve(r),
- None => origin.short_display().to_string(),
+ None => origin.source_path().to_string(),
};
let snippet = Snippet {
opt: FormatOptions {