difftreelog
perf rc imports
in: master
3 files changed
cmds/jrsonnet/src/main.rsdiffbeforeafterboth1pub mod location;23use clap::Clap;4use jsonnet_evaluator::{EvaluationSettings, EvaluationState, LocError, StackTrace, Val};5use jsonnet_parser::{el, Arg, ArgsDesc, Expr, LocExpr, ParserSettings};6use location::{offset_to_location, CodeLocation};7use std::env::current_dir;8use std::{collections::HashMap, path::PathBuf, rc::Rc, str::FromStr};910enum Format {11 None,12 Json,13 Yaml,14}1516impl FromStr for Format {17 type Err = &'static str;18 fn from_str(s: &str) -> Result<Self, Self::Err> {19 Ok(match s {20 "none" => Format::None,21 "json" => Format::Json,22 "yaml" => Format::Yaml,23 _ => return Err("no such format"),24 })25 }26}2728#[derive(PartialEq)]29enum TraceFormat {30 CppJsonnet,31 GoJsonnet,32 Custom,33}34impl FromStr for TraceFormat {35 type Err = &'static str;36 fn from_str(s: &str) -> Result<Self, Self::Err> {37 Ok(match s {38 "cpp" => TraceFormat::CppJsonnet,39 "go" => TraceFormat::GoJsonnet,40 "default" => TraceFormat::Custom,41 _ => return Err("no such format"),42 })43 }44}4546#[derive(Clone)]47struct ExtStr {48 name: String,49 value: String,50}51impl FromStr for ExtStr {52 type Err = &'static str;53 fn from_str(s: &str) -> Result<Self, Self::Err> {54 let out: Vec<_> = s.split('=').collect();55 match out.len() {56 1 => Ok(ExtStr {57 name: out[0].to_owned(),58 value: std::env::var(out[0]).or(Err("missing env var"))?,59 }),60 2 => Ok(ExtStr {61 name: out[0].to_owned(),62 value: out[1].to_owned(),63 }),64 _ => Err("bad ext-str syntax"),65 }66 }67}6869#[derive(Clap)]70#[clap(version = "0.1.0", author = "Lach <iam@lach.pw>")]71struct Opts {72 #[clap(long, about = "Disable global std variable")]73 no_stdlib: bool,74 #[clap(long, about = "Add external string")]75 ext_str: Vec<ExtStr>,76 #[clap(long, about = "Add external string from code")]77 ext_code: Vec<ExtStr>,78 #[clap(long, about = "Add TLA")]79 tla_str: Vec<ExtStr>,80 #[clap(long, about = "Add TLA from code")]81 tla_code: Vec<ExtStr>,82 #[clap(long, short = "f", default_value = "json", possible_values = &["none", "json", "yaml"], about = "Output format, wraps resulting value to corresponding std.manifest call")]83 format: Format,84 #[clap(long, default_value = "default", possible_values = &["cpp", "go", "default"], about = "Emulated needed stacktrace display")]85 trace_format: TraceFormat,8687 #[clap(88 long,89 short = "s",90 default_value = "200",91 about = "Number of allowed stack frames"92 )]93 max_stack: usize,94 #[clap(95 long,96 short = "t",97 default_value = "20",98 about = "Max length of stack trace before cropping"99 )]100 max_trace: usize,101102 #[clap(long, short = "J", about = "Library search dir")]103 jpath: Vec<PathBuf>,104105 #[clap(106 long,107 default_value = "3",108 about = "When using --format, this option specifies string to pad output with"109 )]110 line_padding: usize,111112 #[clap(about = "File to compile", index = 1)]113 input: String,114}115116fn main() {117 let opts: Opts = Opts::parse();118 let evaluator = jsonnet_evaluator::EvaluationState::new(119 EvaluationSettings {120 max_stack_trace_size: opts.max_trace,121 max_stack_frames: opts.max_stack,122 },123 Box::new(jsonnet_evaluator::FileImportResolver {124 library_paths: opts.jpath.clone(),125 }),126 );127 if !opts.no_stdlib {128 evaluator.with_stdlib();129 }130 for ExtStr { name, value } in opts.ext_str.iter().cloned() {131 evaluator.add_ext_var(name.into(), Val::Str(value.into()));132 }133 for ExtStr { name, value } in opts.ext_code.iter().cloned() {134 evaluator.add_ext_var(name.into(), evaluator.parse_evaluate_raw(&value).unwrap());135 }136 let mut input = current_dir().unwrap();137 input.push(opts.input.clone());138 let code_string = String::from_utf8(std::fs::read(opts.input.clone()).unwrap()).unwrap();139 if let Err(e) = evaluator.add_file(input.clone(), code_string.clone()) {140 print_syntax_error(e, &input, &code_string);141 std::process::exit(1);142 }143 let result = evaluator.evaluate_file(&input);144 match result {145 Ok(v) => {146 let v = match v {147 Val::Func(f) => {148 let mut desc_map = HashMap::new();149 for ExtStr { name, value } in opts.tla_str.iter().cloned() {150 desc_map.insert(name, el!(Expr::Str(value.into())));151 }152 for ExtStr { name, value } in opts.tla_code.iter().cloned() {153 desc_map.insert(154 name,155 jsonnet_parser::parse(156 &value,157 &ParserSettings {158 file_name: Rc::new(PathBuf::new()),159 loc_data: false,160 },161 )162 .unwrap(),163 );164 }165 evaluator.add_global("__tmp__tlf__".into(), Val::Func(f));166 evaluator167 .evaluate_raw(el!(Expr::Apply(168 el!(Expr::Var("__tmp__tlf__".into())),169 ArgsDesc(desc_map.into_iter().map(|(k, v)| Arg(Some(k), v)).collect()),170 false,171 )))172 .unwrap()173 }174 v => v,175 };176 let v = evaluator.run_in_state(|| match opts.format {177 Format::Json => Ok(Val::Str(v.into_json(opts.line_padding)?)),178 Format::Yaml => {179 evaluator.add_global("__tmp__to_yaml__".into(), v);180 evaluator.parse_evaluate_raw("std.manifestYamlDoc(__tmp__to_yaml__, \" \")")181 }182 _ => Ok(v),183 });184 let v = match v {185 Ok(v) => v,186 Err(err) => {187 print_error(&err, evaluator, &opts);188 std::process::exit(1);189 }190 };191 match v {192 Val::Str(s) => println!("{}", s),193 Val::Num(n) => println!("{}", n),194 _v => eprintln!(195 "jsonnet output is not a string.\nDid you forgot to set --format, or wrap your data with std.manifestJson?"196 ),197 }198 }199 Err(err) => {200 print_error(&err, evaluator, &opts);201 std::process::exit(1);202 }203 }204}205206fn print_error(err: &LocError, evaluator: EvaluationState, opts: &Opts) {207 println!("Error: {:?}", err.0);208 print_trace(&(err.1), evaluator, &opts);209}210211fn print_syntax_error(error: jsonnet_parser::ParseError, file: &PathBuf, code: &str) {212 use annotate_snippets::{213 display_list::{DisplayList, FormatOptions},214 snippet::{Annotation, AnnotationType, Slice, Snippet, SourceAnnotation},215 };216 //&("Expected: ".to_owned() + error.expected)217 let origin = file.to_str().unwrap();218 let error_message = format!("Expected: {}", error.expected);219 let snippet = Snippet {220 opt: FormatOptions {221 color: true,222 ..Default::default()223 },224 title: Some(Annotation {225 label: Some(&error_message),226 id: None,227 annotation_type: AnnotationType::Error,228 }),229 footer: vec![],230 slices: vec![Slice {231 source: &code,232 line_start: 1,233 origin: Some(origin),234 fold: false,235 annotations: vec![SourceAnnotation {236 label: "At this position",237 annotation_type: AnnotationType::Error,238 range: (error.location.offset, error.location.offset + 1),239 }],240 }],241 };242243 let dl = DisplayList::from(snippet);244 println!("{}", dl);245}246247fn print_trace(trace: &StackTrace, evaluator: EvaluationState, opts: &Opts) {248 use annotate_snippets::{249 display_list::{DisplayList, FormatOptions},250 snippet::{Annotation, AnnotationType, Slice, Snippet, SourceAnnotation},251 };252 for item in trace.0.iter() {253 let desc = &item.1;254 let source = item.0.clone();255 let code = evaluator.get_source(&source.0);256 if code.is_none() {257 continue;258 }259 let code = code.unwrap();260 let start_end = offset_to_location(&code, &[source.1, source.2]);261 if opts.trace_format == TraceFormat::Custom {262 let source_fragment: String = code263 .chars()264 .skip(start_end[0].line_start_offset)265 .take(start_end[1].line_end_offset - start_end[0].line_start_offset)266 .collect();267 let snippet = Snippet {268 opt: FormatOptions {269 color: true,270 ..Default::default()271 },272 title: Some(Annotation {273 label: Some(&item.1),274 id: None,275 annotation_type: AnnotationType::Error,276 }),277 footer: vec![],278 slices: vec![Slice {279 source: &source_fragment,280 line_start: start_end[0].line,281 origin: Some(&source.0.to_str().unwrap()),282 fold: false,283 annotations: vec![SourceAnnotation {284 label: desc,285 annotation_type: AnnotationType::Error,286 range: (287 source.1 - start_end[0].line_start_offset,288 source.2 - start_end[0].line_start_offset,289 ),290 }],291 }],292 };293294 let dl = DisplayList::from(snippet);295 println!("{}", dl);296 } else {297 print_jsonnet_pair(298 source.0.to_str().unwrap(),299 &start_end[0],300 &start_end[1],301 opts.trace_format == TraceFormat::GoJsonnet,302 );303 }304 }305}306307fn print_jsonnet_pair(file: &str, start: &CodeLocation, end: &CodeLocation, is_go: bool) {308 if is_go {309 print!(" ");310 } else {311 print!(" ");312 }313 print!("{}:", file);314 if start.line == end.line {315 // IDK why, but this is the behavior original jsonnet cpp impl shows316 if start.column == end.column || !is_go && start.column + 1 == end.column {317 println!("{}:{}", start.line, end.column)318 } else {319 println!("{}:{}-{}", start.line, start.column, end.column);320 }321 } else {322 println!(323 "({}:{})-({}:{})",324 start.line, end.column, start.line, end.column325 );326 }327}crates/jsonnet-evaluator/src/import.rsdiffbeforeafterboth--- a/crates/jsonnet-evaluator/src/import.rs
+++ b/crates/jsonnet-evaluator/src/import.rs
@@ -3,19 +3,19 @@
use fs::File;
use std::fs;
use std::io::Read;
-use std::{cell::RefCell, collections::HashMap, path::PathBuf};
+use std::{cell::RefCell, collections::HashMap, path::PathBuf, rc::Rc};
pub trait ImportResolver {
- fn resolve_file(&self, from: &PathBuf, path: &PathBuf) -> Result<PathBuf>;
- fn load_file_contents(&self, resolved: &PathBuf) -> Result<String>;
+ fn resolve_file(&self, from: &PathBuf, path: &PathBuf) -> Result<Rc<PathBuf>>;
+ fn load_file_contents(&self, resolved: &PathBuf) -> Result<Rc<str>>;
}
pub struct DummyImportResolver;
impl ImportResolver for DummyImportResolver {
- fn resolve_file(&self, from: &PathBuf, path: &PathBuf) -> Result<PathBuf> {
+ fn resolve_file(&self, from: &PathBuf, path: &PathBuf) -> Result<Rc<PathBuf>> {
create_error(Error::ImportNotSupported(from.clone(), path.clone()))
}
- fn load_file_contents(&self, _resolved: &PathBuf) -> Result<String> {
+ fn load_file_contents(&self, _resolved: &PathBuf) -> Result<Rc<str>> {
// Can be only caused by library direct consumer, not by supplied jsonnet
panic!("dummy resolver can't load any file")
}
@@ -30,23 +30,23 @@
pub library_paths: Vec<PathBuf>,
}
impl ImportResolver for FileImportResolver {
- fn resolve_file(&self, from: &PathBuf, path: &PathBuf) -> Result<PathBuf> {
+ fn resolve_file(&self, from: &PathBuf, path: &PathBuf) -> Result<Rc<PathBuf>> {
let mut new_path = from.clone();
new_path.push(path);
if new_path.exists() {
- Ok(new_path)
+ Ok(Rc::new(new_path))
} else {
for library_path in self.library_paths.iter() {
let mut cloned = library_path.clone();
cloned.push(path);
if cloned.exists() {
- return Ok(cloned);
+ return Ok(Rc::new(cloned));
}
}
create_error(Error::ImportFileNotFound(from.clone(), path.clone()))
}
}
- fn load_file_contents(&self, id: &PathBuf) -> Result<String> {
+ fn load_file_contents(&self, id: &PathBuf) -> Result<Rc<str>> {
let mut file = File::open(id).map_err(|_e| {
create_error::<()>(Error::ResolvedFileNotFound(id.clone()))
.err()
@@ -58,24 +58,24 @@
.err()
.unwrap()
})?;
- Ok(out)
+ Ok(out.into())
}
}
pub struct CachingImportResolver {
- resolution_cache: RefCell<HashMap<(PathBuf, PathBuf), Result<PathBuf>>>,
- loading_cache: RefCell<HashMap<PathBuf, Result<String>>>,
+ resolution_cache: RefCell<HashMap<(PathBuf, PathBuf), Result<Rc<PathBuf>>>>,
+ loading_cache: RefCell<HashMap<PathBuf, Result<Rc<str>>>>,
inner: Box<dyn ImportResolver>,
}
impl ImportResolver for CachingImportResolver {
- fn resolve_file(&self, from: &PathBuf, path: &PathBuf) -> Result<PathBuf> {
+ fn resolve_file(&self, from: &PathBuf, path: &PathBuf) -> Result<Rc<PathBuf>> {
self.resolution_cache
.borrow_mut()
.entry((from.clone(), path.clone()))
.or_insert_with(|| self.inner.resolve_file(from, path))
.clone()
}
- fn load_file_contents(&self, resolved: &PathBuf) -> Result<String> {
+ fn load_file_contents(&self, resolved: &PathBuf) -> Result<Rc<str>> {
self.loading_cache
.borrow_mut()
.entry(resolved.clone())
crates/jsonnet-evaluator/src/lib.rsdiffbeforeafterboth--- a/crates/jsonnet-evaluator/src/lib.rs
+++ b/crates/jsonnet-evaluator/src/lib.rs
@@ -57,15 +57,15 @@
}
}
-pub struct FileData(String, LocExpr, Option<Val>);
+pub struct FileData(Rc<str>, LocExpr, Option<Val>);
#[derive(Default)]
pub struct EvaluationStateInternals {
/// Used for stack-overflows and stacktraces
stack: RefCell<Vec<StackTraceElement>>,
/// Contains file source codes and evaluated results for imports and pretty
/// printing stacktraces
- files: RefCell<HashMap<PathBuf, FileData>>,
- str_files: RefCell<HashMap<PathBuf, Rc<str>>>,
+ files: RefCell<HashMap<Rc<PathBuf>, FileData>>,
+ str_files: RefCell<HashMap<Rc<PathBuf>, Rc<str>>>,
globals: RefCell<HashMap<Rc<str>, Val>>,
/// Values to use with std.extVar
@@ -109,7 +109,11 @@
..Default::default()
}))
}
- pub fn add_file(&self, name: PathBuf, code: String) -> std::result::Result<(), ParseError> {
+ pub fn add_file(
+ &self,
+ name: Rc<PathBuf>,
+ code: Rc<str>,
+ ) -> std::result::Result<(), ParseError> {
self.0.files.borrow_mut().insert(
name.clone(),
FileData(
@@ -117,7 +121,7 @@
parse(
&code,
&ParserSettings {
- file_name: Rc::new(name),
+ file_name: name,
loc_data: true,
},
)?,
@@ -129,8 +133,8 @@
}
pub fn add_parsed_file(
&self,
- name: PathBuf,
- code: String,
+ name: Rc<PathBuf>,
+ code: Rc<str>,
parsed: LocExpr,
) -> std::result::Result<(), ()> {
self.0
@@ -140,7 +144,7 @@
Ok(())
}
- pub fn get_source(&self, name: &PathBuf) -> Option<String> {
+ pub fn get_source(&self, name: &PathBuf) -> Option<Rc<str>> {
let ro_map = self.0.files.borrow();
ro_map.get(name).map(|value| value.0.clone())
}
@@ -221,12 +225,13 @@
}
pub fn with_stdlib(&self) -> &Self {
+ let std_path = Rc::new(PathBuf::from("std.jsonnet"));
self.run_in_state(|| {
use jsonnet_stdlib::STDLIB_STR;
if cfg!(feature = "serialized-stdlib") {
self.add_parsed_file(
- PathBuf::from("std.jsonnet"),
- STDLIB_STR.to_owned(),
+ std_path,
+ STDLIB_STR.to_owned().into(),
bincode::deserialize(include_bytes!(concat!(
env!("OUT_DIR"),
"/stdlib.bincode"
@@ -235,7 +240,7 @@
)
.unwrap();
} else {
- self.add_file(PathBuf::from("std.jsonnet"), STDLIB_STR.to_owned())
+ self.add_file(std_path, STDLIB_STR.to_owned().into())
.unwrap();
}
let val = self.evaluate_file(&PathBuf::from("std.jsonnet")).unwrap();