difftreelog
feat --exp-apply argument
in: master
3 files changed
cmds/jrsonnet/Cargo.tomldiffbeforeafterboth--- a/cmds/jrsonnet/Cargo.toml
+++ b/cmds/jrsonnet/Cargo.toml
@@ -8,7 +8,7 @@
edition = "2021"
[features]
-experimental = ["exp-preserve-order", "exp-destruct"]
+experimental = ["exp-preserve-order", "exp-destruct", "exp-null-coaelse", "exp-object-iteration", "exp-bigint", "exp-apply"]
# Use mimalloc as allocator
mimalloc = ["mimallocator"]
# Experimental feature, which allows to preserve order of object fields
@@ -22,6 +22,10 @@
exp-object-iteration = ["jrsonnet-evaluator/exp-object-iteration"]
# Bigint type
exp-bigint = ["jrsonnet-evaluator/exp-bigint", "jrsonnet-cli/exp-bigint"]
+# obj?.field, obj?.['field']
+exp-null-coaelse = ["jrsonnet-evaluator/exp-null-coaelse", "jrsonnet-parser/exp-null-coaelse"]
+# --exp-apply
+exp-apply = []
# std.thisFile support
legacy-this-file = ["jrsonnet-cli/legacy-this-file"]
cmds/jrsonnet/src/main.rsdiffbeforeafterboth44 /// Path to the file to be compiled if `--evaluate` is unset, otherwise code itself44 /// Path to the file to be compiled if `--evaluate` is unset, otherwise code itself45 pub input: Option<String>,45 pub input: Option<String>,4647 /// After executing input, apply specified code.48 /// Output of the initial input will be accessible using `$`49 #[cfg(feature = "exp-apply")]50 #[clap(long)]51 pub exp_apply: Vec<String>,46}52}475348/// Jsonnet commandline interpreter (Rust implementation)54/// Jsonnet commandline interpreter (Rust implementation)181 };187 };182188183 let tla = opts.tla.tla_opts()?;189 let tla = opts.tla.tla_opts()?;190 #[allow(unused_mut)]184 let val = apply_tla(s.clone(), &tla, val)?;191 let mut val = apply_tla(s.clone(), &tla, val)?;192193 #[cfg(feature = "exp-apply")]194 for apply in opts.input.exp_apply {195 use jrsonnet_evaluator::{InitialUnderscore, Thunk};196 val = s.evaluate_snippet_with(197 "<exp_apply>".to_owned(),198 &apply,199 InitialUnderscore(Thunk::evaluated(val)),200 )?;201 }185202186 let manifest_format = opts.manifest.manifest_format();203 let manifest_format = opts.manifest.manifest_format();187 if let Some(multi) = opts.output.multi {204 if let Some(multi) = opts.output.multi {crates/jrsonnet-evaluator/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/lib.rs
+++ b/crates/jrsonnet-evaluator/src/lib.rs
@@ -154,6 +154,37 @@
}
}
+macro_rules! impl_context_initializer {
+ ($($gen:ident)*) => {
+ #[allow(non_snake_case)]
+ impl<$($gen: ContextInitializer + Trace,)*> ContextInitializer for ($($gen,)*) {
+ fn reserve_vars(&self) -> usize {
+ let mut out = 0;
+ let ($($gen,)*) = self;
+ $(out += $gen.reserve_vars();)*
+ out
+ }
+ fn populate(&self, for_file: Source, builder: &mut ContextBuilder) {
+ let ($($gen,)*) = self;
+ $($gen.populate(for_file.clone(), builder);)*
+ }
+ fn as_any(&self) -> &dyn Any {
+ self
+ }
+ }
+ };
+ ($($cur:ident)* @ $c:ident $($rest:ident)*) => {
+ impl_context_initializer!($($cur)*);
+ impl_context_initializer!($($cur)* $c @ $($rest)*);
+ };
+ ($($cur:ident)* @) => {
+ impl_context_initializer!($($cur)*);
+ }
+}
+impl_context_initializer! {
+ A B @ C D E
+}
+
/// Dynamically reconfigurable evaluation settings
#[derive(Trace)]
pub struct EvaluationSettings {
@@ -361,6 +392,23 @@
context_initializer.initialize(self.clone(), source)
}
+ /// Creates context with all passed global variables, calling custom modifier
+ pub fn create_default_context_with(
+ &self,
+ source: Source,
+ context_initializer: impl ContextInitializer,
+ ) -> Context {
+ let default_initializer = &self.settings().context_initializer;
+ let mut builder = ContextBuilder::with_capacity(
+ self.clone(),
+ default_initializer.reserve_vars() + context_initializer.reserve_vars(),
+ );
+ default_initializer.populate(source.clone(), &mut builder);
+ context_initializer.populate(source, &mut builder);
+
+ builder.build()
+ }
+
/// Executes code creating a new stack frame
pub fn push<T>(
e: CallLocation<'_>,
@@ -428,25 +476,34 @@
}
let mut settings = self.settings_mut();
let initializer = &mut settings.context_initializer;
- match initializer.as_any().downcast_ref::<GlobalsCtx>() {
- Some(glob) => {
- glob.globals.borrow_mut().insert(name, value);
- }
- None => {
- let inner = std::mem::replace(&mut settings.context_initializer, tb!(()));
- settings.context_initializer = tb!(GlobalsCtx {
- globals: {
- let mut out = GcHashMap::with_capacity(1);
- out.insert(name, value);
- RefCell::new(out)
- },
- inner
- })
- }
+ if let Some(global) = initializer.as_any().downcast_ref::<GlobalsCtx>() {
+ global.globals.borrow_mut().insert(name, value);
+ } else {
+ let inner = std::mem::replace(&mut settings.context_initializer, tb!(()));
+ settings.context_initializer = tb!(GlobalsCtx {
+ globals: {
+ let mut out = GcHashMap::with_capacity(1);
+ out.insert(name, value);
+ RefCell::new(out)
+ },
+ inner
+ });
}
}
}
+#[derive(Trace)]
+pub struct InitialUnderscore(pub Thunk<Val>);
+impl ContextInitializer for InitialUnderscore {
+ fn populate(&self, _for_file: Source, builder: &mut ContextBuilder) {
+ builder.bind("_".into(), self.0.clone());
+ }
+
+ fn as_any(&self) -> &dyn Any {
+ self
+ }
+}
+
/// Raw methods evaluate passed values but don't perform TLA execution
impl State {
/// Parses and evaluates the given snippet
@@ -465,6 +522,30 @@
})?;
evaluate(self.create_default_context(source), &parsed)
}
+ /// Parses and evaluates the given snippet with custom context modifier
+ pub fn evaluate_snippet_with(
+ &self,
+ name: impl Into<IStr>,
+ code: impl Into<IStr>,
+ context_initializer: impl ContextInitializer,
+ ) -> Result<Val> {
+ let code = code.into();
+ let source = Source::new_virtual(name.into(), code.clone());
+ let parsed = jrsonnet_parser::parse(
+ &code,
+ &ParserSettings {
+ source: source.clone(),
+ },
+ )
+ .map_err(|e| ImportSyntaxError {
+ path: source.clone(),
+ error: Box::new(e),
+ })?;
+ evaluate(
+ self.create_default_context_with(source, context_initializer),
+ &parsed,
+ )
+ }
}
/// Settings utilities