difftreelog
refactor move state to context
in: master
46 files changed
Cargo.lockdiffbeforeafterboth240 "pathdiff",240 "pathdiff",241 "rustc-hash",241 "rustc-hash",242 "serde",242 "serde",243 "serde_json",244 "static_assertions",243 "static_assertions",245 "strsim",244 "strsim",246 "thiserror",245 "thiserror",541source = "registry+https://github.com/rust-lang/crates.io-index"540source = "registry+https://github.com/rust-lang/crates.io-index"542checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7"541checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7"543dependencies = [542dependencies = [544 "indexmap",545 "itoa",543 "itoa",546 "ryu",544 "ryu",547 "serde",545 "serde",bindings/jsonnet/src/import.rsdiffbeforeafterboth15 error::{Error::*, Result},15 error::{Error::*, Result},16 throw, FileImportResolver, ImportResolver, State,16 throw, FileImportResolver, ImportResolver, State,17};17};18use jrsonnet_gcmodule::Trace;18use jrsonnet_parser::{SourceDirectory, SourceFile, SourcePath};19use jrsonnet_parser::{SourceDirectory, SourceFile, SourcePath};192020pub type JsonnetImportCallback = unsafe extern "C" fn(21pub type JsonnetImportCallback = unsafe extern "C" fn(26) -> *mut c_char;27) -> *mut c_char;272828/// Resolves imports using callback29/// Resolves imports using callback30#[derive(Trace)]29pub struct CallbackImportResolver {31pub struct CallbackImportResolver {32 #[trace(skip)]30 cb: JsonnetImportCallback,33 cb: JsonnetImportCallback,34 #[trace(skip)]31 ctx: *mut c_void,35 ctx: *mut c_void,32 out: RefCell<HashMap<SourcePath, Vec<u8>>>,36 out: RefCell<HashMap<SourcePath, Vec<u8>>>,33}37}bindings/jsonnet/src/lib.rsdiffbeforeafterboth17};17};181819use jrsonnet_evaluator::{19use jrsonnet_evaluator::{20 trace::PathResolver, FileImportResolver, IStr, ManifestFormat, State, Val,20 tb, trace::PathResolver, FileImportResolver, IStr, ManifestFormat, State, Val,21};21};222223/// WASM stub23/// WASM stub68#[allow(clippy::box_default)]68#[allow(clippy::box_default)]69pub extern "C" fn jsonnet_make() -> *mut State {69pub extern "C" fn jsonnet_make() -> *mut State {70 let state = State::default();70 let state = State::default();71 state.settings_mut().import_resolver = Box::new(FileImportResolver::default());71 state.settings_mut().import_resolver = tb!(FileImportResolver::default());72 state.settings_mut().context_initializer = Box::new(jrsonnet_stdlib::ContextInitializer::new(72 state.settings_mut().context_initializer = tb!(jrsonnet_stdlib::ContextInitializer::new(73 state.clone(),73 state.clone(),74 PathResolver::new_cwd_fallback(),74 PathResolver::new_cwd_fallback(),75 ));75 ));bindings/jsonnet/src/native.rsdiffbeforeafterboth36 cb: JsonnetNativeCallback,36 cb: JsonnetNativeCallback,37}37}38impl NativeCallbackHandler for JsonnetNativeCallbackHandler {38impl NativeCallbackHandler for JsonnetNativeCallbackHandler {39 fn call(&self, s: State, args: &[Val]) -> Result<Val, LocError> {39 fn call(&self, args: &[Val]) -> Result<Val, LocError> {40 let mut n_args = Vec::new();40 let mut n_args = Vec::new();41 for a in args {41 for a in args {42 n_args.push(Some(Box::new(a.clone())));42 n_args.push(Some(Box::new(a.clone())));54 if success == 1 {54 if success == 1 {55 Ok(v)55 Ok(v)56 } else {56 } else {57 let e = IStr::from_untyped(v, s).expect("error msg should be a string");57 let e = IStr::from_untyped(v).expect("error msg should be a string");58 Err(Error::RuntimeError(e).into())58 Err(Error::RuntimeError(e).into())59 }59 }60 }60 }crates/jrsonnet-cli/src/stdlib.rsdiffbeforeafterboth1use std::{fs::read_to_string, str::FromStr};1use std::{fs::read_to_string, str::FromStr};223use clap::Parser;3use clap::Parser;4use jrsonnet_evaluator::{error::Result, trace::PathResolver, State};4use jrsonnet_evaluator::{error::Result, tb, trace::PathResolver, State};556use crate::ConfigureState;6use crate::ConfigureState;77125 for ext in self.ext_code_file.iter() {125 for ext in self.ext_code_file.iter() {126 ctx.add_ext_code(&ext.name as &str, &ext.value as &str)?;126 ctx.add_ext_code(&ext.name as &str, &ext.value as &str)?;127 }127 }128 s.settings_mut().context_initializer = Box::new(ctx);128 s.settings_mut().context_initializer = tb!(ctx);129 Ok(())129 Ok(())130 }130 }131}131}crates/jrsonnet-cli/src/trace.rsdiffbeforeafterboth49 .as_ref()49 .as_ref()50 .unwrap_or(&TraceFormatName::Compact)50 .unwrap_or(&TraceFormatName::Compact)51 {51 {52 TraceFormatName::Compact => s.set_trace_format(Box::new(CompactFormat {52 TraceFormatName::Compact => s.set_trace_format(CompactFormat {53 resolver,53 resolver,54 padding: 4,54 padding: 4,55 })),55 }),56 TraceFormatName::Explaining => {56 TraceFormatName::Explaining => s.set_trace_format(ExplainingFormat { resolver }),57 s.set_trace_format(Box::new(ExplainingFormat { resolver }))58 }59 }57 }60 s.set_max_trace(self.max_trace);58 s.set_max_trace(self.max_trace);61 Ok(())59 Ok(())crates/jrsonnet-evaluator/src/ctx.rsdiffbeforeafterboth4use jrsonnet_interner::IStr;4use jrsonnet_interner::IStr;556use crate::{6use crate::{7 error::Error::*, gc::GcHashMap, map::LayeredHashMap, ObjValue, Pending, Result, Thunk, Val,7 error::Error::*, gc::GcHashMap, map::LayeredHashMap, ObjValue, Pending, Result, State, Thunk,8 Val,8};9};91010#[derive(Trace)]11#[derive(Trace)]11struct ContextInternals {12struct ContextInternals {13 state: Option<State>,12 dollar: Option<ObjValue>,14 dollar: Option<ObjValue>,13 sup: Option<ObjValue>,15 sup: Option<ObjValue>,14 this: Option<ObjValue>,16 this: Option<ObjValue>,30 Pending::new()32 Pending::new()31 }33 }3435 pub fn state(&self) -> &State {36 self.037 .state38 .as_ref()39 .expect("used state from dummy context")40 }324133 pub fn dollar(&self) -> &Option<ObjValue> {42 pub fn dollar(&self) -> &Option<ObjValue> {34 &self.0.dollar43 &self.0.dollar42 &self.0.sup51 &self.0.sup43 }52 }4445 pub fn new() -> Self {46 Self(Cc::new(ContextInternals {47 dollar: None,48 this: None,49 sup: None,50 bindings: LayeredHashMap::default(),51 }))52 }535354 #[cfg(not(feature = "friendly-errors"))]54 #[cfg(not(feature = "friendly-errors"))]55 pub fn binding(&self, name: IStr) -> Result<Thunk<Val>> {55 pub fn binding(&self, name: IStr) -> Result<Thunk<Val>> {122 ctx.bindings.clone().extend(new_bindings)122 ctx.bindings.clone().extend(new_bindings)123 };123 };124 Self(Cc::new(ContextInternals {124 Self(Cc::new(ContextInternals {125 state: ctx.state.clone(),125 dollar,126 dollar,126 sup,127 sup,127 this,128 this,130 }131 }131}132}132133133impl Default for Context {134// impl Default for Context {134 fn default() -> Self {135// fn default() -> Self {135 Self::new()136// Self::new()136 }137// }137}138// }138139139impl PartialEq for Context {140impl PartialEq for Context {140 fn eq(&self, other: &Self) -> bool {141 fn eq(&self, other: &Self) -> bool {143}144}144145145pub struct ContextBuilder {146pub struct ContextBuilder {147 state: Option<State>,146 bindings: GcHashMap<IStr, Thunk<Val>>,148 bindings: GcHashMap<IStr, Thunk<Val>>,147 extend: Option<Context>,149 extend: Option<Context>,148}150}149151150impl ContextBuilder {152impl ContextBuilder {153 /// # Panics154 /// Panics aren't directly caused by this function, but if state from resulting context is used155 pub fn dangerous_empty_state() -> Self {156 Self {157 state: None,158 bindings: GcHashMap::new(),159 extend: None,160 }161 }151 pub fn new() -> Self {162 pub fn new(state: State) -> Self {152 Self::with_capacity(0)163 Self::with_capacity(state, 0)153 }164 }154 pub fn with_capacity(capacity: usize) -> Self {165 pub fn with_capacity(state: State, capacity: usize) -> Self {155 Self {166 Self {167 state: Some(state),156 bindings: GcHashMap::with_capacity(capacity),168 bindings: GcHashMap::with_capacity(capacity),157 extend: None,169 extend: None,158 }170 }159 }171 }160 pub fn extend(parent: Context) -> Self {172 pub fn extend(parent: Context) -> Self {161 Self {173 Self {174 state: parent.0.state.clone(),162 bindings: GcHashMap::new(),175 bindings: GcHashMap::new(),163 extend: Some(parent),176 extend: Some(parent),164 }177 }172 }185 }173 pub fn build(self) -> Context {186 pub fn build(self) -> Context {174 if let Some(parent) = self.extend {187 if let Some(parent) = self.extend {188 // TODO: replace self.extend with Result<Context, State>, and remove `state` field175 parent.extend(self.bindings, None, None, None)189 parent.extend(self.bindings, None, None, None)176 } else {190 } else {177 Context(Cc::new(ContextInternals {191 Context(Cc::new(ContextInternals {192 state: self.state,178 bindings: LayeredHashMap::new(self.bindings),193 bindings: LayeredHashMap::new(self.bindings),179 dollar: None,194 dollar: None,180 sup: None,195 sup: None,184 }199 }185}200}186187impl Default for ContextBuilder {188 fn default() -> Self {189 Self::new()190 }191}192201crates/jrsonnet-evaluator/src/error.rsdiffbeforeafterboth1use std::{fmt::Debug, path::PathBuf};1use std::{2 fmt::{Debug, Display},3 path::PathBuf,4};253use jrsonnet_gcmodule::Trace;6use jrsonnet_gcmodule::Trace;252 &mut (self.0).1255 &mut (self.0).1253 }256 }254}257}255impl Debug for LocError {258impl Display for LocError {256 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {259 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {257 writeln!(f, "{}", self.0 .0)?;260 writeln!(f, "{}", self.0 .0)?;258 for el in &self.0 .1 .0 {261 for el in &self.0 .1 .0 {262 write!(f, "\t{}", el.desc)?;263 if let Some(loc) = &el.location {264 write!(f, "at {}", loc.0 .0 .0)?;265 // loc.0266 loc.0.map_source_locations(&[loc.1, loc.2]);267 }259 writeln!(f, "\t{el:?}")?;268 writeln!(f)?;260 }269 }261 Ok(())270 Ok(())262 }271 }263}272}273impl Debug for LocError {274 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {275 f.debug_tuple("LocError").field(&self.0).finish()276 }277}264278265pub trait ErrorSource {279pub trait ErrorSource {266 fn to_location(self) -> Option<ExprLocation>;280 fn to_location(self) -> Option<ExprLocation>;crates/jrsonnet-evaluator/src/evaluate/destructure.rsdiffbeforeafterboth8 gc::GcHashMap,8 gc::GcHashMap,9 tb, throw,9 tb, throw,10 val::ThunkValue,10 val::ThunkValue,11 Context, Pending, State, Thunk, Val,11 Context, Pending, Thunk, Val,12};12};131314#[allow(clippy::too_many_lines)]14#[allow(clippy::too_many_lines)]261 }261 }262 impl ThunkValue for EvaluateThunkValue {262 impl ThunkValue for EvaluateThunkValue {263 type Output = Val;263 type Output = Val;264 fn get(self: Box<Self>, s: State) -> Result<Self::Output> {264 fn get(self: Box<Self>) -> Result<Self::Output> {265 if let Some(name) = self.name {265 self.name.map_or_else(266 evaluate_named(s, self.fctx.unwrap(), &self.expr, name)266 || evaluate(self.fctx.unwrap(), &self.expr),267 } else {268 evaluate(s, self.fctx.unwrap(), &self.expr)267 |name| evaluate_named(self.fctx.unwrap(), &self.expr, name),269 }268 )270 }269 }271 }270 }272 let data = Thunk::new(tb!(EvaluateThunkValue {271 let data = Thunk::new(tb!(EvaluateThunkValue {291 impl ThunkValue for MethodThunk {290 impl ThunkValue for MethodThunk {292 type Output = Val;291 type Output = Val;293292294 fn get(self: Box<Self>, _s: State) -> Result<Self::Output> {293 fn get(self: Box<Self>) -> Result<Self::Output> {295 Ok(evaluate_method(294 Ok(evaluate_method(296 self.fctx.unwrap(),295 self.fctx.unwrap(),297 self.name,296 self.name,crates/jrsonnet-evaluator/src/evaluate/mod.rsdiffbeforeafterboth31 })))31 })))32}32}333334pub fn evaluate_field_name(s: State, ctx: Context, field_name: &FieldName) -> Result<Option<IStr>> {34pub fn evaluate_field_name(ctx: Context, field_name: &FieldName) -> Result<Option<IStr>> {35 Ok(match field_name {35 Ok(match field_name {36 FieldName::Fixed(n) => Some(n.clone()),36 FieldName::Fixed(n) => Some(n.clone()),37 FieldName::Dyn(expr) => State::push(37 FieldName::Dyn(expr) => State::push(38 CallLocation::new(&expr.1),38 CallLocation::new(&expr.1),39 || "evaluating field name".to_string(),39 || "evaluating field name".to_string(),40 || {40 || {41 let value = evaluate(s.clone(), ctx, expr)?;41 let value = evaluate(ctx, expr)?;42 if matches!(value, Val::Null) {42 if matches!(value, Val::Null) {43 Ok(None)43 Ok(None)44 } else {44 } else {45 Ok(Some(IStr::from_untyped(value, s.clone())?))45 Ok(Some(IStr::from_untyped(value)?))46 }46 }47 },47 },48 )?,48 )?,49 })49 })50}50}515152pub fn evaluate_comp(52pub fn evaluate_comp(53 s: State,54 ctx: Context,53 ctx: Context,55 specs: &[CompSpec],54 specs: &[CompSpec],56 callback: &mut impl FnMut(Context) -> Result<()>,55 callback: &mut impl FnMut(Context) -> Result<()>,57) -> Result<()> {56) -> Result<()> {58 match specs.get(0) {57 match specs.get(0) {59 None => callback(ctx)?,58 None => callback(ctx)?,60 Some(CompSpec::IfSpec(IfSpecData(cond))) => {59 Some(CompSpec::IfSpec(IfSpecData(cond))) => {61 if bool::from_untyped(evaluate(s.clone(), ctx.clone(), cond)?, s.clone())? {60 if bool::from_untyped(evaluate(ctx.clone(), cond)?)? {62 evaluate_comp(s, ctx, &specs[1..], callback)?;61 evaluate_comp(ctx, &specs[1..], callback)?;63 }62 }64 }63 }65 Some(CompSpec::ForSpec(ForSpecData(var, expr))) => {64 Some(CompSpec::ForSpec(ForSpecData(var, expr))) => match evaluate(ctx.clone(), expr)? {66 match evaluate(s.clone(), ctx.clone(), expr)? {67 Val::Arr(list) => {65 Val::Arr(list) => {68 for item in list.iter(s.clone()) {66 for item in list.iter() {69 evaluate_comp(67 evaluate_comp(70 s.clone(),71 ctx.clone().with_var(var.clone(), item?.clone()),68 ctx.clone().with_var(var.clone(), item?.clone()),72 &specs[1..],69 &specs[1..],73 callback,70 callback,74 )?;71 )?;75 }72 }76 }73 }77 _ => throw!(InComprehensionCanOnlyIterateOverArray),74 _ => throw!(InComprehensionCanOnlyIterateOverArray),78 }75 },79 }80 }76 }81 Ok(())77 Ok(())82}78}989499 fn bind(95 fn bind(&self, sup: Option<ObjValue>, this: Option<ObjValue>) -> Result<Context> {100 &self,101 _s: State,102 sup: Option<ObjValue>,103 this: Option<ObjValue>,104 ) -> Result<Context> {105 let fctx = Context::new_future();96 let fctx = Context::new_future();106 let mut new_bindings = GcHashMap::new();97 let mut new_bindings = GcHashMap::new();123}114}124115125#[allow(clippy::too_many_lines)]116#[allow(clippy::too_many_lines)]126pub fn evaluate_member_list_object(s: State, ctx: Context, members: &[Member]) -> Result<ObjValue> {117pub fn evaluate_member_list_object(ctx: Context, members: &[Member]) -> Result<ObjValue> {127 let mut builder = ObjValueBuilder::new();118 let mut builder = ObjValueBuilder::new();128 let locals = Rc::new(119 let locals = Rc::new(129 members120 members159 type Bound = Thunk<Val>;150 type Bound = Thunk<Val>;160 fn bind(151 fn bind(161 &self,152 &self,162 s: State,163 sup: Option<ObjValue>,153 sup: Option<ObjValue>,164 this: Option<ObjValue>,154 this: Option<ObjValue>,165 ) -> Result<Thunk<Val>> {155 ) -> Result<Thunk<Val>> {166 Ok(Thunk::evaluated(evaluate_named(156 Ok(Thunk::evaluated(evaluate_named(167 s.clone(),168 self.uctx.bind(s, sup, this)?,157 self.uctx.bind(sup, this)?,169 &self.value,158 &self.value,170 self.name.clone(),159 self.name.clone(),171 )?))160 )?))172 }161 }173 }162 }174163175 let name = evaluate_field_name(s.clone(), ctx.clone(), name)?;164 let name = evaluate_field_name(ctx.clone(), name)?;176 let name = if let Some(name) = name {165 let name = if let Some(name) = name {177 name166 name178 } else {167 } else {207 type Bound = Thunk<Val>;196 type Bound = Thunk<Val>;208 fn bind(197 fn bind(209 &self,198 &self,210 s: State,211 sup: Option<ObjValue>,199 sup: Option<ObjValue>,212 this: Option<ObjValue>,200 this: Option<ObjValue>,213 ) -> Result<Thunk<Val>> {201 ) -> Result<Thunk<Val>> {214 Ok(Thunk::evaluated(evaluate_method(202 Ok(Thunk::evaluated(evaluate_method(215 self.uctx.bind(s, sup, this)?,203 self.uctx.bind(sup, this)?,216 self.name.clone(),204 self.name.clone(),217 self.params.clone(),205 self.params.clone(),218 self.value.clone(),206 self.value.clone(),219 )))207 )))220 }208 }221 }209 }222210223 let name = if let Some(name) = evaluate_field_name(s.clone(), ctx.clone(), name)? {211 let name = if let Some(name) = evaluate_field_name(ctx.clone(), name)? {224 name212 name225 } else {213 } else {226 continue;214 continue;247 impl<B: Unbound<Bound = Context>> ObjectAssertion for ObjectAssert<B> {235 impl<B: Unbound<Bound = Context>> ObjectAssertion for ObjectAssert<B> {248 fn run(236 fn run(&self, sup: Option<ObjValue>, this: Option<ObjValue>) -> Result<()> {249 &self,250 s: State,251 sup: Option<ObjValue>,252 this: Option<ObjValue>,253 ) -> Result<()> {254 let ctx = self.uctx.bind(s.clone(), sup, this)?;237 let ctx = self.uctx.bind(sup, this)?;255 evaluate_assert(s, ctx, &self.assert)238 evaluate_assert(ctx, &self.assert)256 }239 }257 }240 }258 builder.assert(tb!(ObjectAssert {241 builder.assert(tb!(ObjectAssert {267 Ok(this)250 Ok(this)268}251}269252270pub fn evaluate_object(s: State, ctx: Context, object: &ObjBody) -> Result<ObjValue> {253pub fn evaluate_object(ctx: Context, object: &ObjBody) -> Result<ObjValue> {271 Ok(match object {254 Ok(match object {272 ObjBody::MemberList(members) => evaluate_member_list_object(s, ctx, members)?,255 ObjBody::MemberList(members) => evaluate_member_list_object(ctx, members)?,273 ObjBody::ObjComp(obj) => {256 ObjBody::ObjComp(obj) => {274 let mut builder = ObjValueBuilder::new();257 let mut builder = ObjValueBuilder::new();275 let locals = Rc::new(258 let locals = Rc::new(280 .collect::<Vec<_>>(),263 .collect::<Vec<_>>(),281 );264 );282 let mut ctxs = vec![];265 let mut ctxs = vec![];283 evaluate_comp(s.clone(), ctx, &obj.compspecs, &mut |ctx| {266 evaluate_comp(ctx, &obj.compspecs, &mut |ctx| {284 let key = evaluate(s.clone(), ctx.clone(), &obj.key)?;267 let key = evaluate(ctx.clone(), &obj.key)?;285 let fctx = Context::new_future();268 let fctx = Context::new_future();286 ctxs.push((ctx, fctx.clone()));269 ctxs.push((ctx, fctx.clone()));287 let uctx = evaluate_object_locals(fctx, locals.clone());270 let uctx = evaluate_object_locals(fctx, locals.clone());298 type Bound = Thunk<Val>;281 type Bound = Thunk<Val>;299 fn bind(282 fn bind(300 &self,283 &self,301 s: State,302 sup: Option<ObjValue>,284 sup: Option<ObjValue>,303 this: Option<ObjValue>,285 this: Option<ObjValue>,304 ) -> Result<Thunk<Val>> {286 ) -> Result<Thunk<Val>> {305 Ok(Thunk::evaluated(evaluate(287 Ok(Thunk::evaluated(evaluate(306 s.clone(),307 self.uctx.bind(s, sup, this.clone())?.extend(288 self.uctx.bind(sup, this.clone())?.extend(308 GcHashMap::new(),289 GcHashMap::new(),309 None,290 None,310 None,291 None,341}322}342323343pub fn evaluate_apply(324pub fn evaluate_apply(344 s: State,345 ctx: Context,325 ctx: Context,346 value: &LocExpr,326 value: &LocExpr,347 args: &ArgsDesc,327 args: &ArgsDesc,348 loc: CallLocation<'_>,328 loc: CallLocation<'_>,349 tailstrict: bool,329 tailstrict: bool,350) -> Result<Val> {330) -> Result<Val> {351 let value = evaluate(s.clone(), ctx.clone(), value)?;331 let value = evaluate(ctx.clone(), value)?;352 Ok(match value {332 Ok(match value {353 Val::Func(f) => {333 Val::Func(f) => {354 let body = || f.evaluate(s.clone(), ctx, loc, args, tailstrict);334 let body = || f.evaluate(ctx, loc, args, tailstrict);355 if tailstrict {335 if tailstrict {356 body()?336 body()?357 } else {337 } else {362 })342 })363}343}364344365pub fn evaluate_assert(s: State, ctx: Context, assertion: &AssertStmt) -> Result<()> {345pub fn evaluate_assert(ctx: Context, assertion: &AssertStmt) -> Result<()> {366 let value = &assertion.0;346 let value = &assertion.0;367 let msg = &assertion.1;347 let msg = &assertion.1;368 let assertion_result = State::push(348 let assertion_result = State::push(369 CallLocation::new(&value.1),349 CallLocation::new(&value.1),370 || "assertion condition".to_owned(),350 || "assertion condition".to_owned(),371 || bool::from_untyped(evaluate(s.clone(), ctx.clone(), value)?, s.clone()),351 || bool::from_untyped(evaluate(ctx.clone(), value)?),372 )?;352 )?;373 if !assertion_result {353 if !assertion_result {374 State::push(354 State::push(377 || {357 || {378 if let Some(msg) = msg {358 if let Some(msg) = msg {379 throw!(AssertionFailed(359 throw!(AssertionFailed(evaluate(ctx, msg)?.to_string()?));380 evaluate(s.clone(), ctx, msg)?.to_string(s.clone())?381 ));382 }360 }383 throw!(AssertionFailed(Val::Null.to_string(s.clone())?));361 throw!(AssertionFailed(Val::Null.to_string()?));384 },362 },385 )?;363 )?;386 }364 }387 Ok(())365 Ok(())388}366}389367390pub fn evaluate_named(s: State, ctx: Context, expr: &LocExpr, name: IStr) -> Result<Val> {368pub fn evaluate_named(ctx: Context, expr: &LocExpr, name: IStr) -> Result<Val> {391 use Expr::*;369 use Expr::*;392 let LocExpr(raw_expr, _loc) = expr;370 let LocExpr(raw_expr, _loc) = expr;393 Ok(match &**raw_expr {371 Ok(match &**raw_expr {394 Function(params, body) => evaluate_method(ctx, name, params.clone(), body.clone()),372 Function(params, body) => evaluate_method(ctx, name, params.clone(), body.clone()),395 _ => evaluate(s, ctx, expr)?,373 _ => evaluate(ctx, expr)?,396 })374 })397}375}398376399#[allow(clippy::too_many_lines)]377#[allow(clippy::too_many_lines)]400pub fn evaluate(s: State, ctx: Context, expr: &LocExpr) -> Result<Val> {378pub fn evaluate(ctx: Context, expr: &LocExpr) -> Result<Val> {401 use Expr::*;379 use Expr::*;402 let LocExpr(expr, loc) = expr;380 let LocExpr(expr, loc) = expr;403 // let bp = with_state(|s| s.0.stop_at.borrow().clone());381 // let bp = with_state(|s| s.0.stop_at.borrow().clone());418 Literal(LiteralType::True) => Val::Bool(true),396 Literal(LiteralType::True) => Val::Bool(true),419 Literal(LiteralType::False) => Val::Bool(false),397 Literal(LiteralType::False) => Val::Bool(false),420 Literal(LiteralType::Null) => Val::Null,398 Literal(LiteralType::Null) => Val::Null,421 Parened(e) => evaluate(s, ctx, e)?,399 Parened(e) => evaluate(ctx, e)?,422 Str(v) => Val::Str(v.clone()),400 Str(v) => Val::Str(v.clone()),423 Num(v) => Val::new_checked_num(*v)?,401 Num(v) => Val::new_checked_num(*v)?,424 BinaryOp(v1, o, v2) => evaluate_binary_op_special(s, ctx, v1, *o, v2)?,402 BinaryOp(v1, o, v2) => evaluate_binary_op_special(ctx, v1, *o, v2)?,425 UnaryOp(o, v) => evaluate_unary_op(*o, &evaluate(s, ctx, v)?)?,403 UnaryOp(o, v) => evaluate_unary_op(*o, &evaluate(ctx, v)?)?,426 Var(name) => State::push(404 Var(name) => State::push(427 CallLocation::new(loc),405 CallLocation::new(loc),428 || format!("variable <{name}> access"),406 || format!("variable <{name}> access"),429 || ctx.binding(name.clone())?.evaluate(s.clone()),407 || ctx.binding(name.clone())?.evaluate(),430 )?,408 )?,431 Index(value, index) => {409 Index(value, index) => match (evaluate(ctx.clone(), value)?, evaluate(ctx, index)?) {432 match (433 evaluate(s.clone(), ctx.clone(), value)?,434 evaluate(s.clone(), ctx, index)?,435 ) {436 (Val::Obj(v), Val::Str(key)) => State::push(410 (Val::Obj(v), Val::Str(key)) => State::push(437 CallLocation::new(loc),411 CallLocation::new(loc),438 || format!("field <{key}> access"),412 || format!("field <{key}> access"),439 || match v.get(s.clone(), key.clone()) {413 || match v.get(key.clone()) {440 Ok(Some(v)) => Ok(v),414 Ok(Some(v)) => Ok(v),441 #[cfg(not(feature = "friendly-errors"))]415 #[cfg(not(feature = "friendly-errors"))]442 Ok(None) => throw!(NoSuchField(key.clone(), vec![])),416 Ok(None) => throw!(NoSuchField(key.clone(), vec![])),474 if n.fract() > f64::EPSILON {448 if n.fract() > f64::EPSILON {475 throw!(FractionalIndex)449 throw!(FractionalIndex)476 }450 }477 v.get(s, n as usize)?451 v.get(n as usize)?478 .ok_or_else(|| ArrayBoundsError(n as usize, v.len()))?452 .ok_or_else(|| ArrayBoundsError(n as usize, v.len()))?479 }453 }480 (Val::Arr(_), Val::Str(n)) => throw!(AttemptedIndexAnArrayWithString(n)),454 (Val::Arr(_), Val::Str(n)) => throw!(AttemptedIndexAnArrayWithString(n)),504 )),478 )),505479506 (v, _) => throw!(CantIndexInto(v.value_type())),480 (v, _) => throw!(CantIndexInto(v.value_type())),507 }481 },508 }509 LocalExpr(bindings, returned) => {482 LocalExpr(bindings, returned) => {510 let mut new_bindings: GcHashMap<IStr, Thunk<Val>> =483 let mut new_bindings: GcHashMap<IStr, Thunk<Val>> =511 GcHashMap::with_capacity(bindings.len());484 GcHashMap::with_capacity(bindings.len());514 evaluate_dest(b, fctx.clone(), &mut new_bindings)?;487 evaluate_dest(b, fctx.clone(), &mut new_bindings)?;515 }488 }516 let ctx = ctx.extend(new_bindings, None, None, None).into_future(fctx);489 let ctx = ctx.extend(new_bindings, None, None, None).into_future(fctx);517 evaluate(s, ctx, &returned.clone())?490 evaluate(ctx, &returned.clone())?518 }491 }519 Arr(items) => {492 Arr(items) => {520 let mut out = Vec::with_capacity(items.len());493 let mut out = Vec::with_capacity(items.len());527 }500 }528 impl ThunkValue for ArrayElement {501 impl ThunkValue for ArrayElement {529 type Output = Val;502 type Output = Val;530 fn get(self: Box<Self>, s: State) -> Result<Val> {503 fn get(self: Box<Self>) -> Result<Val> {531 evaluate(s, self.ctx, &self.item)504 evaluate(self.ctx, &self.item)532 }505 }533 }506 }534 out.push(Thunk::new(tb!(ArrayElement {507 out.push(Thunk::new(tb!(ArrayElement {540 }513 }541 ArrComp(expr, comp_specs) => {514 ArrComp(expr, comp_specs) => {542 let mut out = Vec::new();515 let mut out = Vec::new();543 evaluate_comp(s.clone(), ctx, comp_specs, &mut |ctx| {516 evaluate_comp(ctx, comp_specs, &mut |ctx| {544 out.push(evaluate(s.clone(), ctx, expr)?);517 out.push(evaluate(ctx, expr)?);545 Ok(())518 Ok(())546 })?;519 })?;547 Val::Arr(ArrValue::Eager(Cc::new(out)))520 Val::Arr(ArrValue::Eager(Cc::new(out)))548 }521 }549 Obj(body) => Val::Obj(evaluate_object(s, ctx, body)?),522 Obj(body) => Val::Obj(evaluate_object(ctx, body)?),550 ObjExtend(a, b) => evaluate_add_op(523 ObjExtend(a, b) => evaluate_add_op(551 s.clone(),552 &evaluate(s.clone(), ctx.clone(), a)?,524 &evaluate(ctx.clone(), a)?,553 &Val::Obj(evaluate_object(s, ctx, b)?),525 &Val::Obj(evaluate_object(ctx, b)?),554 )?,526 )?,555 Apply(value, args, tailstrict) => {527 Apply(value, args, tailstrict) => {556 evaluate_apply(s, ctx, value, args, CallLocation::new(loc), *tailstrict)?528 evaluate_apply(ctx, value, args, CallLocation::new(loc), *tailstrict)?557 }529 }558 Function(params, body) => {530 Function(params, body) => {559 evaluate_method(ctx, "anonymous".into(), params.clone(), body.clone())531 evaluate_method(ctx, "anonymous".into(), params.clone(), body.clone())560 }532 }561 AssertExpr(assert, returned) => {533 AssertExpr(assert, returned) => {562 evaluate_assert(s.clone(), ctx.clone(), assert)?;534 evaluate_assert(ctx.clone(), assert)?;563 evaluate(s, ctx, returned)?535 evaluate(ctx, returned)?564 }536 }565 ErrorStmt(e) => State::push(537 ErrorStmt(e) => State::push(566 CallLocation::new(loc),538 CallLocation::new(loc),567 || "error statement".to_owned(),539 || "error statement".to_owned(),568 || {540 || throw!(RuntimeError(evaluate(ctx, e)?.to_string()?,)),569 throw!(RuntimeError(570 evaluate(s.clone(), ctx, e)?.to_string(s.clone())?,571 ))572 },573 )?,541 )?,574 IfElse {542 IfElse {575 cond,543 cond,579 if State::push(547 if State::push(580 CallLocation::new(loc),548 CallLocation::new(loc),581 || "if condition".to_owned(),549 || "if condition".to_owned(),582 || bool::from_untyped(evaluate(s.clone(), ctx.clone(), &cond.0)?, s.clone()),550 || bool::from_untyped(evaluate(ctx.clone(), &cond.0)?),583 )? {551 )? {584 evaluate(s, ctx, cond_then)?552 evaluate(ctx, cond_then)?585 } else {553 } else {586 match cond_else {554 match cond_else {587 Some(v) => evaluate(s, ctx, v)?,555 Some(v) => evaluate(ctx, v)?,588 None => Val::Null,556 None => Val::Null,589 }557 }590 }558 }591 }559 }592 Slice(value, desc) => {560 Slice(value, desc) => {593 fn parse_idx<T: Typed>(561 fn parse_idx<T: Typed>(594 loc: CallLocation<'_>,562 loc: CallLocation<'_>,595 s: State,596 ctx: &Context,563 ctx: &Context,597 expr: &Option<LocExpr>,564 expr: &Option<LocExpr>,598 desc: &'static str,565 desc: &'static str,601 Ok(Some(State::push(568 Ok(Some(State::push(602 loc,569 loc,603 || format!("slice {desc}"),570 || format!("slice {desc}"),604 || T::from_untyped(evaluate(s.clone(), ctx.clone(), value)?, s.clone()),571 || T::from_untyped(evaluate(ctx.clone(), value)?),605 )?))572 )?))606 } else {573 } else {607 Ok(None)574 Ok(None)608 }575 }609 }576 }610577611 let indexable = evaluate(s.clone(), ctx.clone(), value)?;578 let indexable = evaluate(ctx.clone(), value)?;612 let loc = CallLocation::new(loc);579 let loc = CallLocation::new(loc);613580614 let start = parse_idx(loc, s.clone(), &ctx, &desc.start, "start")?;581 let start = parse_idx(loc, &ctx, &desc.start, "start")?;615 let end = parse_idx(loc, s.clone(), &ctx, &desc.end, "end")?;582 let end = parse_idx(loc, &ctx, &desc.end, "end")?;616 let step = parse_idx(loc, s.clone(), &ctx, &desc.step, "step")?;583 let step = parse_idx(loc, &ctx, &desc.step, "step")?;617584618 IndexableVal::into_untyped(indexable.into_indexable()?.slice(start, end, step)?, s)?585 IndexableVal::into_untyped(indexable.into_indexable()?.slice(start, end, step)?)?619 }586 }620 i @ (Import(path) | ImportStr(path) | ImportBin(path)) => {587 i @ (Import(path) | ImportStr(path) | ImportBin(path)) => {621 let tmp = loc.clone().0;588 let tmp = loc.clone().0;589 let s = ctx.state();622 let resolved_path = s.resolve_from(tmp.source_path(), path as &str)?;590 let resolved_path = s.resolve_from(tmp.source_path(), path as &str)?;623 match i {591 match i {624 Import(_) => State::push(592 Import(_) => State::push(crates/jrsonnet-evaluator/src/evaluate/operator.rsdiffbeforeafterboth445use crate::{5use crate::{6 error::Error::*, evaluate, stdlib::std_format, throw, typed::Typed, val::equals, Context,6 error::Error::*, evaluate, stdlib::std_format, throw, typed::Typed, val::equals, Context,7 Result, State, Val,7 Result, Val,8};8};9910pub fn evaluate_unary_op(op: UnaryOpType, b: &Val) -> Result<Val> {10pub fn evaluate_unary_op(op: UnaryOpType, b: &Val) -> Result<Val> {18 })18 })19}19}202021pub fn evaluate_add_op(s: State, a: &Val, b: &Val) -> Result<Val> {21pub fn evaluate_add_op(a: &Val, b: &Val) -> Result<Val> {22 use Val::*;22 use Val::*;23 Ok(match (a, b) {23 Ok(match (a, b) {24 (Str(a), Str(b)) if a.is_empty() => Val::Str(b.clone()),24 (Str(a), Str(b)) if a.is_empty() => Val::Str(b.clone()),29 (Num(a), Str(b)) => Str(format!("{a}{b}").into()),29 (Num(a), Str(b)) => Str(format!("{a}{b}").into()),30 (Str(a), Num(b)) => Str(format!("{a}{b}").into()),30 (Str(a), Num(b)) => Str(format!("{a}{b}").into()),313132 (Str(a), o) | (o, Str(a)) if a.is_empty() => Val::Str(o.clone().to_string(s)?),32 (Str(a), o) | (o, Str(a)) if a.is_empty() => Val::Str(o.clone().to_string()?),33 (Str(a), o) => Str(format!("{a}{}", o.clone().to_string(s)?).into()),33 (Str(a), o) => Str(format!("{a}{}", o.clone().to_string()?).into()),34 (o, Str(a)) => Str(format!("{}{a}", o.clone().to_string(s)?).into()),34 (o, Str(a)) => Str(format!("{}{a}", o.clone().to_string()?).into()),353536 (Obj(v1), Obj(v2)) => Obj(v2.extend_from(v1.clone())),36 (Obj(v1), Obj(v2)) => Obj(v2.extend_from(v1.clone())),37 (Arr(a), Arr(b)) => {37 (Arr(a), Arr(b)) => {49 })49 })50}50}515152pub fn evaluate_mod_op(s: State, a: &Val, b: &Val) -> Result<Val> {52pub fn evaluate_mod_op(a: &Val, b: &Val) -> Result<Val> {53 use Val::*;53 use Val::*;54 match (a, b) {54 match (a, b) {55 (Num(a), Num(b)) => {55 (Num(a), Num(b)) => {58 }58 }59 Ok(Num(a % b))59 Ok(Num(a % b))60 }60 }61 (Str(str), vals) => {61 (Str(str), vals) => String::into_untyped(std_format(str.clone(), vals.clone())?),62 String::into_untyped(std_format(s.clone(), str.clone(), vals.clone())?, s)63 }64 (a, b) => throw!(BinaryOperatorDoesNotOperateOnValues(62 (a, b) => throw!(BinaryOperatorDoesNotOperateOnValues(65 BinaryOpType::Mod,63 BinaryOpType::Mod,66 a.value_type(),64 a.value_type(),70}68}716972pub fn evaluate_binary_op_special(70pub fn evaluate_binary_op_special(73 s: State,74 ctx: Context,71 ctx: Context,75 a: &LocExpr,72 a: &LocExpr,76 op: BinaryOpType,73 op: BinaryOpType,77 b: &LocExpr,74 b: &LocExpr,78) -> Result<Val> {75) -> Result<Val> {79 use BinaryOpType::*;76 use BinaryOpType::*;80 use Val::*;77 use Val::*;81 Ok(match (evaluate(s.clone(), ctx.clone(), a)?, op, b) {78 Ok(match (evaluate(ctx.clone(), a)?, op, b) {82 (Bool(true), Or, _o) => Val::Bool(true),79 (Bool(true), Or, _o) => Val::Bool(true),83 (Bool(false), And, _o) => Val::Bool(false),80 (Bool(false), And, _o) => Val::Bool(false),84 (a, op, eb) => evaluate_binary_op_normal(s.clone(), &a, op, &evaluate(s, ctx, eb)?)?,81 (a, op, eb) => evaluate_binary_op_normal(&a, op, &evaluate(ctx, eb)?)?,85 })82 })86}83}878488pub fn evaluate_compare_op(s: State, a: &Val, op: BinaryOpType, b: &Val) -> Result<Ordering> {85pub fn evaluate_compare_op(a: &Val, op: BinaryOpType, b: &Val) -> Result<Ordering> {89 use Val::*;86 use Val::*;90 Ok(match (a, b) {87 Ok(match (a, b) {91 (Str(a), Str(b)) => a.cmp(b),88 (Str(a), Str(b)) => a.cmp(b),92 (Num(a), Num(b)) => a.partial_cmp(b).expect("jsonnet numbers are non NaN"),89 (Num(a), Num(b)) => a.partial_cmp(b).expect("jsonnet numbers are non NaN"),93 (Arr(a), Arr(b)) => {90 (Arr(a), Arr(b)) => {94 let ai = a.iter(s.clone());91 let ai = a.iter();95 let bi = b.iter(s.clone());92 let bi = b.iter();969397 for (a, b) in ai.zip(bi) {94 for (a, b) in ai.zip(bi) {98 let ord = evaluate_compare_op(s.clone(), &a?, op, &b?)?;95 let ord = evaluate_compare_op(&a?, op, &b?)?;99 if !ord.is_eq() {96 if !ord.is_eq() {100 return Ok(ord);97 return Ok(ord);101 }98 }111 })108 })112}109}113110114pub fn evaluate_binary_op_normal(s: State, a: &Val, op: BinaryOpType, b: &Val) -> Result<Val> {111pub fn evaluate_binary_op_normal(a: &Val, op: BinaryOpType, b: &Val) -> Result<Val> {115 use BinaryOpType::*;112 use BinaryOpType::*;116 use Val::*;113 use Val::*;117 Ok(match (a, op, b) {114 Ok(match (a, op, b) {118 (a, Add, b) => evaluate_add_op(s, a, b)?,115 (a, Add, b) => evaluate_add_op(a, b)?,119116120 (a, Eq, b) => Bool(equals(s, a, b)?),117 (a, Eq, b) => Bool(equals(a, b)?),121 (a, Neq, b) => Bool(!equals(s, a, b)?),118 (a, Neq, b) => Bool(!equals(a, b)?),122119123 (a, Lt, b) => Bool(evaluate_compare_op(s, a, Lt, b)?.is_lt()),120 (a, Lt, b) => Bool(evaluate_compare_op(a, Lt, b)?.is_lt()),124 (a, Gt, b) => Bool(evaluate_compare_op(s, a, Gt, b)?.is_gt()),121 (a, Gt, b) => Bool(evaluate_compare_op(a, Gt, b)?.is_gt()),125 (a, Lte, b) => Bool(evaluate_compare_op(s, a, Lte, b)?.is_le()),122 (a, Lte, b) => Bool(evaluate_compare_op(a, Lte, b)?.is_le()),126 (a, Gte, b) => Bool(evaluate_compare_op(s, a, Gte, b)?.is_ge()),123 (a, Gte, b) => Bool(evaluate_compare_op(a, Gte, b)?.is_ge()),127124128 (Str(a), In, Obj(obj)) => Bool(obj.has_field_ex(a.clone(), true)),125 (Str(a), In, Obj(obj)) => Bool(obj.has_field_ex(a.clone(), true)),129 (a, Mod, b) => evaluate_mod_op(s, a, b)?,126 (a, Mod, b) => evaluate_mod_op(a, b)?,130127131 (Str(v1), Mul, Num(v2)) => Str(v1.repeat(*v2 as usize).into()),128 (Str(v1), Mul, Num(v2)) => Str(v1.repeat(*v2 as usize).into()),132129crates/jrsonnet-evaluator/src/function/arglike.rsdiffbeforeafterboth5use jrsonnet_parser::{ArgsDesc, LocExpr};5use jrsonnet_parser::{ArgsDesc, LocExpr};667use crate::{7use crate::{error::Result, evaluate, tb, typed::Typed, val::ThunkValue, Context, Thunk, Val};8 error::Result, evaluate, tb, typed::Typed, val::ThunkValue, Context, State, Thunk, Val,89};9/// Marker for arguments, which can be evaluated with context set to None10pub trait OptionalContext {}101111#[derive(Trace)]12#[derive(Trace)]12struct EvaluateThunk {13struct EvaluateThunk {15}16}16impl ThunkValue for EvaluateThunk {17impl ThunkValue for EvaluateThunk {17 type Output = Val;18 type Output = Val;18 fn get(self: Box<Self>, s: State) -> Result<Val> {19 fn get(self: Box<Self>) -> Result<Val> {19 evaluate(s, self.ctx, &self.expr)20 evaluate(self.ctx, &self.expr)20 }21 }21}22}222323pub trait ArgLike {24pub trait ArgLike {24 fn evaluate_arg(&self, s: State, ctx: Context, tailstrict: bool) -> Result<Thunk<Val>>;25 fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<Thunk<Val>>;25}26}262727impl ArgLike for &LocExpr {28impl ArgLike for &LocExpr {28 fn evaluate_arg(&self, s: State, ctx: Context, tailstrict: bool) -> Result<Thunk<Val>> {29 fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<Thunk<Val>> {29 Ok(if tailstrict {30 Ok(if tailstrict {30 Thunk::evaluated(evaluate(s, ctx, self)?)31 Thunk::evaluated(evaluate(ctx, self)?)31 } else {32 } else {32 Thunk::new(tb!(EvaluateThunk {33 Thunk::new(tb!(EvaluateThunk {33 ctx,34 ctx,41where42where42 T: Typed + Clone,43 T: Typed + Clone,43{44{44 fn evaluate_arg(&self, s: State, _ctx: Context, _tailstrict: bool) -> Result<Thunk<Val>> {45 fn evaluate_arg(&self, _ctx: Context, _tailstrict: bool) -> Result<Thunk<Val>> {45 let val = T::into_untyped(self.clone(), s)?;46 let val = T::into_untyped(self.clone())?;46 Ok(Thunk::evaluated(val))47 Ok(Thunk::evaluated(val))47 }48 }48}49}50impl<T> OptionalContext for T where T: Typed + Clone {}495150#[derive(Clone)]52#[derive(Clone, Trace)]51pub enum TlaArg {53pub enum TlaArg {52 String(IStr),54 String(IStr),53 Code(LocExpr),55 Code(LocExpr),54 Val(Val),56 Val(Val),55}57}56impl ArgLike for TlaArg {58impl ArgLike for TlaArg {57 fn evaluate_arg(&self, s: State, ctx: Context, tailstrict: bool) -> Result<Thunk<Val>> {59 fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<Thunk<Val>> {58 match self {60 match self {59 TlaArg::String(s) => Ok(Thunk::evaluated(Val::Str(s.clone()))),61 TlaArg::String(s) => Ok(Thunk::evaluated(Val::Str(s.clone()))),60 TlaArg::Code(code) => Ok(if tailstrict {62 TlaArg::Code(code) => Ok(if tailstrict {61 Thunk::evaluated(evaluate(s, ctx, code)?)63 Thunk::evaluated(evaluate(ctx, code)?)62 } else {64 } else {63 Thunk::new(tb!(EvaluateThunk {65 Thunk::new(tb!(EvaluateThunk {64 ctx,66 ctx,81 fn unnamed_len(&self) -> usize;83 fn unnamed_len(&self) -> usize;82 fn unnamed_iter(84 fn unnamed_iter(83 &self,85 &self,84 s: State,85 ctx: Context,86 ctx: Context,86 tailstrict: bool,87 tailstrict: bool,87 handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,88 handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,88 ) -> Result<()>;89 ) -> Result<()>;89 fn named_iter(90 fn named_iter(90 &self,91 &self,91 s: State,92 ctx: Context,92 ctx: Context,93 tailstrict: bool,93 tailstrict: bool,94 handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,94 handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,102 }102 }103 fn unnamed_iter(103 fn unnamed_iter(104 &self,104 &self,105 _s: State,106 _ctx: Context,105 _ctx: Context,107 _tailstrict: bool,106 _tailstrict: bool,108 handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,107 handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,114 }113 }115 fn named_iter(114 fn named_iter(116 &self,115 &self,117 _s: State,118 _ctx: Context,116 _ctx: Context,119 _tailstrict: bool,117 _tailstrict: bool,120 _handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,118 _handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,123 }121 }124 fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}122 fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}125}123}124impl OptionalContext for Vec<Val> {}126125127impl ArgsLike for ArgsDesc {126impl ArgsLike for ArgsDesc {128 fn unnamed_len(&self) -> usize {127 fn unnamed_len(&self) -> usize {131130132 fn unnamed_iter(131 fn unnamed_iter(133 &self,132 &self,134 s: State,135 ctx: Context,133 ctx: Context,136 tailstrict: bool,134 tailstrict: bool,137 handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,135 handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,140 handler(138 handler(141 id,139 id,142 if tailstrict {140 if tailstrict {143 Thunk::evaluated(evaluate(s.clone(), ctx.clone(), arg)?)141 Thunk::evaluated(evaluate(ctx.clone(), arg)?)144 } else {142 } else {145 Thunk::new(tb!(EvaluateThunk {143 Thunk::new(tb!(EvaluateThunk {146 ctx: ctx.clone(),144 ctx: ctx.clone(),154152155 fn named_iter(153 fn named_iter(156 &self,154 &self,157 s: State,158 ctx: Context,155 ctx: Context,159 tailstrict: bool,156 tailstrict: bool,160 handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,157 handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,163 handler(160 handler(164 name,161 name,165 if tailstrict {162 if tailstrict {166 Thunk::evaluated(evaluate(s.clone(), ctx.clone(), arg)?)163 Thunk::evaluated(evaluate(ctx.clone(), arg)?)167 } else {164 } else {168 Thunk::new(tb!(EvaluateThunk {165 Thunk::new(tb!(EvaluateThunk {169 ctx: ctx.clone(),166 ctx: ctx.clone(),190187191 fn unnamed_iter(188 fn unnamed_iter(192 &self,189 &self,193 _s: State,194 _ctx: Context,190 _ctx: Context,195 _tailstrict: bool,191 _tailstrict: bool,196 _handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,192 _handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,200196201 fn named_iter(197 fn named_iter(202 &self,198 &self,203 s: State,204 ctx: Context,199 ctx: Context,205 tailstrict: bool,200 tailstrict: bool,206 handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,201 handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,207 ) -> Result<()> {202 ) -> Result<()> {208 for (name, value) in self.iter() {203 for (name, value) in self.iter() {209 handler(204 handler(name, value.evaluate_arg(ctx.clone(), tailstrict)?)?;210 name,211 value.evaluate_arg(s.clone(), ctx.clone(), tailstrict)?,212 )?;213 }205 }214 Ok(())206 Ok(())220 }212 }221 }213 }222}214}215impl<A, S> OptionalContext for HashMap<IStr, A, S> where A: ArgLike + OptionalContext {}223216224macro_rules! impl_args_like {217macro_rules! impl_args_like {225 ($count:expr; $($gen:ident)*) => {218 ($count:expr; $($gen:ident)*) => {231 #[allow(non_snake_case, unused_assignments)]224 #[allow(non_snake_case, unused_assignments)]232 fn unnamed_iter(225 fn unnamed_iter(233 &self,226 &self,234 s: State,235 ctx: Context,227 ctx: Context,236 tailstrict: bool,228 tailstrict: bool,237 handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,229 handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,238 ) -> Result<()> {230 ) -> Result<()> {239 let mut i = 0usize;231 let mut i = 0usize;240 let ($($gen,)*) = self;232 let ($($gen,)*) = self;241 $(233 $(242 handler(i, $gen.evaluate_arg(s.clone(), ctx.clone(), tailstrict)?)?;234 handler(i, $gen.evaluate_arg(ctx.clone(), tailstrict)?)?;243 i+=1;235 i+=1;244 )*236 )*245 Ok(())237 Ok(())246 }238 }247 fn named_iter(239 fn named_iter(248 &self,240 &self,249 _s: State,250 _ctx: Context,241 _ctx: Context,251 _tailstrict: bool,242 _tailstrict: bool,252 _handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,243 _handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,255 }246 }256 fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}247 fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}257 }248 }258 impl<$($gen: ArgLike,)*> sealed::Named for ($((IStr, $gen),)*) {}249 impl<$($gen: ArgLike,)*> OptionalContext for ($($gen,)*) where $($gen: OptionalContext),* {}250251 impl<$($gen: ArgLike,)*> sealed::Named for ($((IStr, $gen),)*) {}259 impl<$($gen: ArgLike,)*> ArgsLike for ($((IStr, $gen),)*) {252 impl<$($gen: ArgLike,)*> ArgsLike for ($((IStr, $gen),)*) {260 fn unnamed_len(&self) -> usize {253 fn unnamed_len(&self) -> usize {261 0254 0262 }255 }263 fn unnamed_iter(256 fn unnamed_iter(264 &self,257 &self,265 _s: State,266 _ctx: Context,258 _ctx: Context,267 _tailstrict: bool,259 _tailstrict: bool,268 _handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,260 _handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,272 #[allow(non_snake_case)]264 #[allow(non_snake_case)]273 fn named_iter(265 fn named_iter(274 &self,266 &self,275 s: State,276 ctx: Context,267 ctx: Context,277 tailstrict: bool,268 tailstrict: bool,278 handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,269 handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,279 ) -> Result<()> {270 ) -> Result<()> {280 let ($($gen,)*) = self;271 let ($($gen,)*) = self;281 $(272 $(282 handler(&$gen.0, $gen.1.evaluate_arg(s.clone(), ctx.clone(), tailstrict)?)?;273 handler(&$gen.0, $gen.1.evaluate_arg(ctx.clone(), tailstrict)?)?;283 )*274 )*284 Ok(())275 Ok(())285 }276 }291 )*282 )*292 }283 }293 }284 }285 impl<$($gen: ArgLike,)*> OptionalContext for ($((IStr, $gen),)*) where $($gen: OptionalContext),* {}294 };286 };295 ($count:expr; $($cur:ident)* @ $c:ident $($rest:ident)*) => {287 ($count:expr; $($cur:ident)* @ $c:ident $($rest:ident)*) => {296 impl_args_like!($count; $($cur)*);288 impl_args_like!($count; $($cur)*);312304313 fn unnamed_iter(305 fn unnamed_iter(314 &self,306 &self,315 _s: State,316 _ctx: Context,307 _ctx: Context,317 _tailstrict: bool,308 _tailstrict: bool,318 _handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,309 _handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,322313323 fn named_iter(314 fn named_iter(324 &self,315 &self,325 _s: State,326 _ctx: Context,316 _ctx: Context,327 _tailstrict: bool,317 _tailstrict: bool,328 _handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,318 _handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,332322333 fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}323 fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}334}324}325impl OptionalContext for () {}335326crates/jrsonnet-evaluator/src/function/builtin.rsdiffbeforeafterboth3use jrsonnet_gcmodule::Trace;3use jrsonnet_gcmodule::Trace;445use super::{arglike::ArgsLike, parse::parse_builtin_call, CallLocation};5use super::{arglike::ArgsLike, parse::parse_builtin_call, CallLocation};6use crate::{error::Result, gc::TraceBox, Context, State, Val};6use crate::{error::Result, gc::TraceBox, Context, Val};778pub type BuiltinParamName = Cow<'static, str>;8pub type BuiltinParamName = Cow<'static, str>;9926 /// Call the builtin26 /// Call the builtin27 fn call(27 fn call(&self, ctx: Context, loc: CallLocation<'_>, args: &dyn ArgsLike) -> Result<Val>;28 &self,29 s: State,30 ctx: Context,31 loc: CallLocation<'_>,32 args: &dyn ArgsLike,33 ) -> Result<Val>;34}28}3529787279 fn call(73 fn call(&self, ctx: Context, _loc: CallLocation<'_>, args: &dyn ArgsLike) -> Result<Val> {80 &self,81 s: State,82 ctx: Context,83 _loc: CallLocation<'_>,84 args: &dyn ArgsLike,85 ) -> Result<Val> {86 let args = parse_builtin_call(s.clone(), ctx, &self.params, args, true)?;74 let args = parse_builtin_call(ctx, &self.params, args, true)?;87 let args = args75 let args = args88 .into_iter()76 .into_iter()89 .map(|a| a.expect("legacy natives have no default params"))77 .map(|a| a.expect("legacy natives have no default params"))90 .map(|a| a.evaluate(s.clone()))78 .map(|a| a.evaluate())91 .collect::<Result<Vec<Val>>>()?;79 .collect::<Result<Vec<Val>>>()?;92 self.handler.call(s, &args)80 self.handler.call(&args)93 }81 }94}82}958396pub trait NativeCallbackHandler: Trace {84pub trait NativeCallbackHandler: Trace {97 fn call(&self, s: State, args: &[Val]) -> Result<Val>;85 fn call(&self, args: &[Val]) -> Result<Val>;98}86}9987crates/jrsonnet-evaluator/src/function/mod.rsdiffbeforeafterboth7use jrsonnet_parser::{Destruct, Expr, ExprLocation, LocExpr, ParamsDesc};7use jrsonnet_parser::{Destruct, Expr, ExprLocation, LocExpr, ParamsDesc};889use self::{9use self::{10 arglike::OptionalContext,10 builtin::{Builtin, StaticBuiltin},11 builtin::{Builtin, StaticBuiltin},11 native::NativeDesc,12 native::NativeDesc,12 parse::{parse_default_function_call, parse_function_call},13 parse::{parse_default_function_call, parse_function_call},13};14};14use crate::{evaluate, gc::TraceBox, typed::Any, Context, Result, State, Val};15use crate::{evaluate, gc::TraceBox, typed::Any, Context, ContextBuilder, Result, Val};151616pub mod arglike;17pub mod arglike;17pub mod builtin;18pub mod builtin;73 /// Create context, with which body code will run74 /// Create context, with which body code will run74 pub fn call_body_context(75 pub fn call_body_context(75 &self,76 &self,76 s: State,77 call_ctx: Context,77 call_ctx: Context,78 args: &dyn ArgsLike,78 args: &dyn ArgsLike,79 tailstrict: bool,79 tailstrict: bool,80 ) -> Result<Context> {80 ) -> Result<Context> {81 parse_function_call(81 parse_function_call(call_ctx, self.ctx.clone(), &self.params, args, tailstrict)82 s,83 call_ctx,84 self.ctx.clone(),85 &self.params,86 args,87 tailstrict,88 )89 }82 }90}83}140 /// If `tailstrict` is specified - then arguments will be evaluated before being passed to function body.133 /// If `tailstrict` is specified - then arguments will be evaluated before being passed to function body.141 pub fn evaluate(134 pub fn evaluate(142 &self,135 &self,143 s: State,144 call_ctx: Context,136 call_ctx: Context,145 loc: CallLocation<'_>,137 loc: CallLocation<'_>,146 args: &dyn ArgsLike,138 args: &dyn ArgsLike,155 }147 }156 static ID: &builtin_id = &builtin_id {};148 static ID: &builtin_id = &builtin_id {};157149158 ID.call(s, call_ctx, loc, args)150 ID.call(call_ctx, loc, args)159 }151 }160 Self::Normal(func) => {152 Self::Normal(func) => {161 let body_ctx = func.call_body_context(s.clone(), call_ctx, args, tailstrict)?;153 let body_ctx = func.call_body_context(call_ctx, args, tailstrict)?;162 evaluate(s, body_ctx, &func.body)154 evaluate(body_ctx, &func.body)163 }155 }164 Self::StaticBuiltin(b) => b.call(s, call_ctx, loc, args),156 Self::StaticBuiltin(b) => b.call(call_ctx, loc, args),165 Self::Builtin(b) => b.call(s, call_ctx, loc, args),157 Self::Builtin(b) => b.call(call_ctx, loc, args),166 }158 }167 }159 }168 /// Helper method, which calls [`Self::evaluate`] with sensible defaults for native code.169 pub fn evaluate_simple(&self, s: State, args: &dyn ArgsLike) -> Result<Val> {160 pub fn evaluate_simple<A: ArgsLike + OptionalContext>(&self, args: &A) -> Result<Val> {170 self.evaluate(s, Context::default(), CallLocation::native(), args, true)161 self.evaluate(162 ContextBuilder::dangerous_empty_state().build(),163 CallLocation::native(),164 args,165 true,180 ///176 ///181 /// This function should only be used for optimization, not for the conditional logic, i.e code should work with syntetic identity function too177 /// This function should only be used for optimization, not for the conditional logic, i.e code should work with syntetic identity function too182 pub fn is_identity(&self) -> bool {178 pub fn is_identity(&self) -> bool {183 if matches!(self, Self::Id) {184 return true;185 }186187 match self {179 match self {188 Self::Id => true,180 Self::Id => true,crates/jrsonnet-evaluator/src/function/native.rsdiffbeforeafterboth1use super::{arglike::ArgLike, CallLocation, FuncVal};1use super::{2 arglike::{ArgLike, OptionalContext},3 FuncVal,4};2use crate::{error::Result, typed::Typed, Context, State};5use crate::{error::Result, typed::Typed};364pub trait NativeDesc {7pub trait NativeDesc {5 type Value;8 type Value;9 ($($gen:ident)*) => {12 ($($gen:ident)*) => {10 impl<$($gen,)* O> NativeDesc for (($($gen,)*), O)13 impl<$($gen,)* O> NativeDesc for (($($gen,)*), O)11 where14 where12 $($gen: ArgLike,)*15 $($gen: ArgLike + OptionalContext,)*13 O: Typed,16 O: Typed,14 {17 {15 type Value = Box<dyn Fn(State, $($gen,)*) -> Result<O>>;18 type Value = Box<dyn Fn($($gen,)*) -> Result<O>>;161917 #[allow(non_snake_case)]20 #[allow(non_snake_case)]18 fn into_native(val: FuncVal) -> Self::Value {21 fn into_native(val: FuncVal) -> Self::Value {19 Box::new(move |s: State, $($gen),*| {22 Box::new(move |$($gen),*| {20 let val = val.evaluate(23 let val = val.evaluate_simple(21 s.clone(),22 // This isn't intended to be used with ArgsDesc23 Context::default(),24 CallLocation::native(),25 &($($gen,)*),24 &($($gen,)*),26 true27 )?;25 )?;28 O::from_untyped(val, s.clone())26 O::from_untyped(val)29 })27 })30 }28 }31 }29 }crates/jrsonnet-evaluator/src/function/parse.rsdiffbeforeafterboth12 gc::GcHashMap,12 gc::GcHashMap,13 tb, throw,13 tb, throw,14 val::ThunkValue,14 val::ThunkValue,15 Context, Pending, State, Thunk, Val,15 Context, Pending, Thunk, Val,16};16};171718#[derive(Trace)]18#[derive(Trace)]242425impl ThunkValue for EvaluateNamedThunk {25impl ThunkValue for EvaluateNamedThunk {26 type Output = Val;26 type Output = Val;27 fn get(self: Box<Self>, s: State) -> Result<Val> {27 fn get(self: Box<Self>) -> Result<Val> {28 evaluate_named(s, self.ctx.unwrap(), &self.value, self.name)28 evaluate_named(self.ctx.unwrap(), &self.value, self.name)29 }29 }30}30}313138/// * `args`: passed function arguments38/// * `args`: passed function arguments39/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily39/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily40pub fn parse_function_call(40pub fn parse_function_call(41 s: State,42 ctx: Context,41 ctx: Context,43 body_ctx: Context,42 body_ctx: Context,44 params: &ParamsDesc,43 params: &ParamsDesc,56 let mut filled_named = 0;55 let mut filled_named = 0;57 let mut filled_positionals = 0;56 let mut filled_positionals = 0;585759 args.unnamed_iter(s.clone(), ctx.clone(), tailstrict, &mut |id, arg| {58 args.unnamed_iter(ctx.clone(), tailstrict, &mut |id, arg| {60 let name = params[id].0.clone();59 let name = params[id].0.clone();61 destruct(60 destruct(62 &name,61 &name,68 Ok(())67 Ok(())69 })?;68 })?;706971 args.named_iter(s, ctx, tailstrict, &mut |name, value| {70 args.named_iter(ctx, tailstrict, &mut |name, value| {72 // FIXME: O(n) for arg existence check71 // FIXME: O(n) for arg existence check73 if !params.iter().any(|p| p.0.name().as_ref() == Some(name)) {72 if !params.iter().any(|p| p.0.name().as_ref() == Some(name)) {74 throw!(UnknownFunctionParameter((name as &str).to_owned()));73 throw!(UnknownFunctionParameter((name as &str).to_owned()));150/// * `args`: passed function arguments149/// * `args`: passed function arguments151/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily150/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily152pub fn parse_builtin_call(151pub fn parse_builtin_call(153 s: State,154 ctx: Context,152 ctx: Context,155 params: &[BuiltinParam],153 params: &[BuiltinParam],156 args: &dyn ArgsLike,154 args: &dyn ArgsLike,169167170 let mut filled_args = 0;168 let mut filled_args = 0;171169172 args.unnamed_iter(s.clone(), ctx.clone(), tailstrict, &mut |id, arg| {170 args.unnamed_iter(ctx.clone(), tailstrict, &mut |id, arg| {173 passed_args[id] = Some(arg);171 passed_args[id] = Some(arg);174 filled_args += 1;172 filled_args += 1;175 Ok(())173 Ok(())176 })?;174 })?;177175178 args.named_iter(s, ctx, tailstrict, &mut |name, arg| {176 args.named_iter(ctx, tailstrict, &mut |name, arg| {179 // FIXME: O(n) for arg existence check177 // FIXME: O(n) for arg existence check180 let id = params178 let id = params181 .iter()179 .iter()232 struct DependsOnUnbound(IStr, ParamsDesc);230 struct DependsOnUnbound(IStr, ParamsDesc);233 impl ThunkValue for DependsOnUnbound {231 impl ThunkValue for DependsOnUnbound {234 type Output = Val;232 type Output = Val;235 fn get(self: Box<Self>, _: State) -> Result<Val> {233 fn get(self: Box<Self>) -> Result<Val> {236 Err(FunctionParameterNotBoundInCall(234 Err(FunctionParameterNotBoundInCall(237 Some(self.0.clone()),235 Some(self.0.clone()),238 self.1.iter().map(|p| (p.0.name(), p.1.is_some())).collect(),236 self.1.iter().map(|p| (p.0.name(), p.1.is_some())).collect(),crates/jrsonnet-evaluator/src/import.rsdiffbeforeafterboth8};8};9910use fs::File;10use fs::File;11use jrsonnet_gcmodule::Trace;11use jrsonnet_parser::{SourceDirectory, SourceFile, SourcePath};12use jrsonnet_parser::{SourceDirectory, SourceFile, SourcePath};121313use crate::{14use crate::{19};20};202121/// Implements file resolution logic for `import` and `importStr`22/// Implements file resolution logic for `import` and `importStr`22pub trait ImportResolver {23pub trait ImportResolver: Trace {23 /// Resolves file path, e.g. `(/home/user/manifests, b.libjsonnet)` can correspond24 /// Resolves file path, e.g. `(/home/user/manifests, b.libjsonnet)` can correspond24 /// both to `/home/user/manifests/b.libjsonnet` and to `/home/user/${vendor}/b.libjsonnet`25 /// both to `/home/user/manifests/b.libjsonnet` and to `/home/user/${vendor}/b.libjsonnet`25 /// where `${vendor}` is a library path.26 /// where `${vendor}` is a library path.47}48}484949/// Dummy resolver, can't resolve/load any file50/// Dummy resolver, can't resolve/load any file51#[derive(Trace)]50pub struct DummyImportResolver;52pub struct DummyImportResolver;51impl ImportResolver for DummyImportResolver {53impl ImportResolver for DummyImportResolver {52 fn load_file_contents(&self, _resolved: &SourcePath) -> Result<Vec<u8>> {54 fn load_file_contents(&self, _resolved: &SourcePath) -> Result<Vec<u8>> {65}67}666867/// File resolver, can load file from both FS and library paths69/// File resolver, can load file from both FS and library paths68#[derive(Default)]70#[derive(Default, Trace)]69pub struct FileImportResolver {71pub struct FileImportResolver {70 /// Library directories to search for file.72 /// Library directories to search for file.71 /// Referred to as `jpath` in original jsonnet implementation.73 /// Referred to as `jpath` in original jsonnet implementation.crates/jrsonnet-evaluator/src/integrations/serde.rsdiffbeforeafterboth3use jrsonnet_gcmodule::Cc;3use jrsonnet_gcmodule::Cc;4use serde::{de::Visitor, ser::Error, Deserialize, Serialize};4use serde::{5 de::Visitor,6 ser::{Error, SerializeMap, SerializeSeq},7 Deserialize, Serialize,8};596use crate::{error::Result, val::ArrValue, ObjValueBuilder, State, Val};10use crate::{error::Result, val::ArrValue, ObjValueBuilder, State, Val};152 Val::Num(n) => serializer.serialize_f64(*n),156 Val::Num(n) => serializer.serialize_f64(*n),153 Val::Arr(arr) => {157 Val::Arr(arr) => {154 let mut seq = serializer.serialize_seq(Some(arr.len()))?;158 let mut seq = serializer.serialize_seq(Some(arr.len()))?;155 for element in arr.iter(State::default()) {159 for (i, element) in arr.iter().enumerate() {156 // seq.serialize_element()160 let mut serde_error = None;161 // TODO: rewrite using try{} after stabilization162 State::push_description(163 || format!("array index [{i}]"),164 || {165 let e = element?;166 if let Err(e) = seq.serialize_element(&e) {167 serde_error = Some(e);168 }169 Ok(())170 },171 )172 .map_err(|e| S::Error::custom(e.to_string()))?;173 if let Some(e) = serde_error {174 return Err(e);157 }175 }176 }158 todo!()177 seq.end()159 }178 }160 Val::Obj(_) => todo!(),179 Val::Obj(obj) => {180 let mut map = serializer.serialize_map(Some(obj.len()))?;181 for (field, value) in obj.iter() {182 let mut serde_error = None;183 // TODO: rewrite using try{} after stabilization184 State::push_description(185 || format!("object field {field:?}"),186 || {187 let v = value?;188 if let Err(e) = map.serialize_entry(field.as_str(), &v) {189 serde_error = Some(e);190 }191 Ok(())192 },193 )194 .map_err(|e| S::Error::custom(e.to_string()))?;195 if let Some(e) = serde_error {196 return Err(e);197 }198 }199 map.end()200 }161 Val::Func(_) => Err(S::Error::custom("tried to manifest function")),201 Val::Func(_) => Err(S::Error::custom("tried to manifest function")),162 }202 }163 }203 }crates/jrsonnet-evaluator/src/lib.rsdiffbeforeafterboth36 clippy::use_self,36 clippy::use_self,37 // https://github.com/rust-lang/rust-clippy/issues/853937 // https://github.com/rust-lang/rust-clippy/issues/853938 clippy::iter_with_drain,38 clippy::iter_with_drain,39 // ci is being run with nightly, but library should work on stable40 clippy::missing_const_for_fn,39)]41)]404241// For jrsonnet-macros43// For jrsonnet-macros63 collections::HashMap,65 collections::HashMap,64 fmt::{self, Debug},66 fmt::{self, Debug},65 path::Path,67 path::Path,66 rc::Rc,67};68};686969pub use ctx::*;70pub use ctx::*;89 /// Type of value after object context is bound90 /// Type of value after object context is bound90 type Bound;91 type Bound;91 /// Create value bound to specified object context92 /// Create value bound to specified object context92 fn bind(&self, s: State, sup: Option<ObjValue>, this: Option<ObjValue>) -> Result<Self::Bound>;93 fn bind(&self, sup: Option<ObjValue>, this: Option<ObjValue>) -> Result<Self::Bound>;93}94}949595/// Object fields may, or may not depend on `this`/`super`, this enum allows cheaper reuse of object-independent fields for native code96/// Object fields may, or may not depend on `this`/`super`, this enum allows cheaper reuse of object-independent fields for native code111 /// Attach object context to value, if required112 /// Attach object context to value, if required112 pub fn evaluate(113 pub fn evaluate(&self, sup: Option<ObjValue>, this: Option<ObjValue>) -> Result<Thunk<Val>> {113 &self,114 s: State,115 sup: Option<ObjValue>,116 this: Option<ObjValue>,117 ) -> Result<Thunk<Val>> {118 match self {114 match self {119 Self::Unbound(v) => v.bind(s, sup, this),115 Self::Unbound(v) => v.bind(sup, this),120 Self::Bound(v) => Ok(v.clone()),116 Self::Bound(v) => Ok(v.clone()),121 }117 }122 }118 }123}119}124120125/// During import, this trait will be called to create initial context for file.121/// During import, this trait will be called to create initial context for file.126/// It may initialize global variables, stdlib for example.122/// It may initialize global variables, stdlib for example.127pub trait ContextInitializer {123pub trait ContextInitializer: Trace {128 /// Initialize default file context.124 /// Initialize default file context.129 fn initialize(&self, state: State, for_file: Source) -> Context;125 fn initialize(&self, state: State, for_file: Source) -> Context;130 /// Allows upcasting from abstract to concrete context initializer.126 /// Allows upcasting from abstract to concrete context initializer.133}129}134130135/// Context initializer which adds nothing.131/// Context initializer which adds nothing.132#[derive(Trace)]136pub struct DummyContextInitializer;133pub struct DummyContextInitializer;137impl ContextInitializer for DummyContextInitializer {134impl ContextInitializer for DummyContextInitializer {138 fn initialize(&self, _state: State, _for_file: Source) -> Context {135 fn initialize(&self, state: State, _for_file: Source) -> Context {139 Context::default()136 ContextBuilder::new(state).build()140 }137 }141 fn as_any(&self) -> &dyn Any {138 fn as_any(&self) -> &dyn Any {142 self139 self143 }140 }144}141}145142146/// Dynamically reconfigurable evaluation settings143/// Dynamically reconfigurable evaluation settings144#[derive(Trace)]147pub struct EvaluationSettings {145pub struct EvaluationSettings {148 /// Limits amount of stack trace items preserved146 /// Limits amount of stack trace items preserved149 pub max_trace: usize,147 pub max_trace: usize,150 /// TLA vars148 /// TLA vars151 pub tla_vars: HashMap<IStr, TlaArg>,149 pub tla_vars: HashMap<IStr, TlaArg>,152 /// Context initializer, which will be used for imports and everything150 /// Context initializer, which will be used for imports and everything153 /// [`NoopContextInitializer`] is used by default, most likely you want to have `jrsonnet-stdlib`151 /// [`NoopContextInitializer`] is used by default, most likely you want to have `jrsonnet-stdlib`154 pub context_initializer: Box<dyn ContextInitializer>,152 pub context_initializer: TraceBox<dyn ContextInitializer>,155 /// Used to resolve file locations/contents153 /// Used to resolve file locations/contents156 pub import_resolver: Box<dyn ImportResolver>,154 pub import_resolver: TraceBox<dyn ImportResolver>,157 /// Used in manifestification functions155 /// Used in manifestification functions158 pub manifest_format: ManifestFormat,156 pub manifest_format: ManifestFormat,159 /// Used for bindings157 /// Used for bindings160 pub trace_format: Box<dyn TraceFormat>,158 pub trace_format: TraceBox<dyn TraceFormat>,161}159}162impl Default for EvaluationSettings {160impl Default for EvaluationSettings {163 fn default() -> Self {161 fn default() -> Self {164 Self {162 Self {165 max_trace: 20,163 max_trace: 20,166 context_initializer: Box::new(DummyContextInitializer),164 context_initializer: tb!(DummyContextInitializer),167 tla_vars: HashMap::default(),165 tla_vars: HashMap::default(),168 import_resolver: Box::new(DummyImportResolver),166 import_resolver: tb!(DummyImportResolver),169 manifest_format: ManifestFormat::Json {167 manifest_format: ManifestFormat::Json {170 padding: 4,168 padding: 4,171 #[cfg(feature = "exp-preserve-order")]169 #[cfg(feature = "exp-preserve-order")]172 preserve_order: false,170 preserve_order: false,173 },171 },174 trace_format: Box::new(CompactFormat {172 trace_format: tb!(CompactFormat {175 padding: 4,173 padding: 4,176 resolver: trace::PathResolver::Absolute,174 resolver: trace::PathResolver::Absolute,177 }),175 }),178 }176 }179 }177 }180}178}181179180#[derive(Trace)]182struct FileData {181struct FileData {183 string: Option<IStr>,182 string: Option<IStr>,184 bytes: Option<IBytes>,183 bytes: Option<IBytes>,208 }207 }209}208}210209211#[derive(Default)]210#[derive(Default, Trace)]212pub struct EvaluationStateInternals {211pub struct EvaluationStateInternals {213 /// Internal state212 /// Internal state214 file_cache: RefCell<GcHashMap<SourcePath, FileData>>,213 file_cache: RefCell<GcHashMap<SourcePath, FileData>>,217}216}218217219/// Maintains stack trace and import resolution218/// Maintains stack trace and import resolution220#[derive(Default, Clone)]219#[derive(Default, Clone, Trace)]221pub struct State(Rc<EvaluationStateInternals>);220pub struct State(Cc<EvaluationStateInternals>);222221223impl State {222impl State {224 /// Should only be called with path retrieved from [`resolve_path`], may panic otherwise223 /// Should only be called with path retrieved from [`resolve_path`], may panic otherwise341 // Dropping file cache guard here, as evaluation may use this map too340 // Dropping file cache guard here, as evaluation may use this map too342 drop(file_cache);341 drop(file_cache);343 let res = evaluate(342 let res = evaluate(self.create_default_context(file_name), &parsed);344 self.clone(),345 self.create_default_context(file_name),346 &parsed,347 );348343349 let mut file_cache = self.file_cache();344 let mut file_cache = self.file_cache();425 pub fn manifest(&self, val: Val) -> Result<IStr> {420 pub fn manifest(&self, val: Val) -> Result<IStr> {426 Self::push_description(421 Self::push_description(427 || "manifestification".to_string(),422 || "manifestification".to_string(),428 || val.manifest(self.clone(), &self.manifest_format()),423 || val.manifest(&self.manifest_format()),429 )424 )430 }425 }431 pub fn manifest_multi(&self, val: Val) -> Result<Vec<(IStr, IStr)>> {426 pub fn manifest_multi(&self, val: Val) -> Result<Vec<(IStr, IStr)>> {432 val.manifest_multi(self.clone(), &self.manifest_format())427 val.manifest_multi(&self.manifest_format())433 }428 }434 pub fn manifest_stream(&self, val: Val) -> Result<Vec<IStr>> {429 pub fn manifest_stream(&self, val: Val) -> Result<Vec<IStr>> {435 val.manifest_stream(self.clone(), &self.manifest_format())430 val.manifest_stream(&self.manifest_format())436 }431 }437432438 /// If passed value is function then call with set TLA433 /// If passed value is function then call with set TLA442 || "during TLA call".to_owned(),437 || "during TLA call".to_owned(),443 || {438 || {444 func.evaluate(439 func.evaluate(445 self.clone(),446 self.create_default_context(Source::new_virtual(440 self.create_default_context(Source::new_virtual(447 "<tla>".into(),441 "<tla>".into(),448 IStr::empty(),442 IStr::empty(),487 path: source.clone(),481 path: source.clone(),488 error: Box::new(e),482 error: Box::new(e),489 })?;483 })?;490 evaluate(self.clone(), self.create_default_context(source), &parsed)484 evaluate(self.create_default_context(source), &parsed)491 }485 }492}486}493487537 Ref::map(self.settings(), |s| &*s.import_resolver)531 Ref::map(self.settings(), |s| &*s.import_resolver)538 }532 }539 pub fn set_import_resolver(&self, resolver: Box<dyn ImportResolver>) {533 pub fn set_import_resolver(&self, resolver: Box<dyn ImportResolver>) {540 self.settings_mut().import_resolver = resolver;534 self.settings_mut().import_resolver = TraceBox(resolver);541 }535 }542 pub fn context_initializer(&self) -> Ref<'_, dyn ContextInitializer> {536 pub fn context_initializer(&self) -> Ref<'_, dyn ContextInitializer> {543 Ref::map(self.settings(), |s| &*s.context_initializer)537 Ref::map(self.settings(), |s| &*s.context_initializer)553 pub fn trace_format(&self) -> Ref<'_, dyn TraceFormat> {547 pub fn trace_format(&self) -> Ref<'_, dyn TraceFormat> {554 Ref::map(self.settings(), |s| &*s.trace_format)548 Ref::map(self.settings(), |s| &*s.trace_format)555 }549 }556 pub fn set_trace_format(&self, format: Box<dyn TraceFormat>) {550 pub fn set_trace_format(&self, format: impl TraceFormat) {557 self.settings_mut().trace_format = format;551 self.settings_mut().trace_format = tb!(format);558 }552 }559553560 pub fn max_trace(&self) -> usize {554 pub fn max_trace(&self) -> usize {crates/jrsonnet-evaluator/src/obj.rsdiffbeforeafterboth105}105}106106107pub trait ObjectAssertion: Trace {107pub trait ObjectAssertion: Trace {108 fn run(&self, s: State, super_obj: Option<ObjValue>, this: Option<ObjValue>) -> Result<()>;108 fn run(&self, super_obj: Option<ObjValue>, this: Option<ObjValue>) -> Result<()>;109}109}110110111// Field => This111// Field => This368 .map_or(false, |v| v.is_visible())368 .map_or(false, |v| v.is_visible())369 }369 }370371 pub fn iter(&self) -> impl Iterator<Item = (IStr, Result<Val>)> + '_ {372 let fields = self.fields();373 fields.into_iter().map(|field| {374 (375 field.clone(),376 self.get(field)377 .map(|opt| opt.expect("iterating over keys, field exists")),378 )379 })380 }370381371 pub fn get(&self, s: State, key: IStr) -> Result<Option<Val>> {382 pub fn get(&self, key: IStr) -> Result<Option<Val>> {372 self.run_assertions(s.clone())?;383 self.run_assertions()?;373 if let Some(v) = self.0.value_cache.borrow().get(&key) {384 if let Some(v) = self.0.value_cache.borrow().get(&key) {374 return Ok(match v {385 return Ok(match v {375 CacheValue::Cached(v) => Some(v.clone()),386 CacheValue::Cached(v) => Some(v.clone()),384 .insert(key.clone(), CacheValue::Pending);395 .insert(key.clone(), CacheValue::Pending);385 let value = self396 let value = self386 .get_raw(397 .get_raw(387 s,388 key.clone(),398 key.clone(),389 self.0.this.clone().unwrap_or_else(|| self.clone()),399 self.0.this.clone().unwrap_or_else(|| self.clone()),390 )400 )404 Ok(value)414 Ok(value)405 }415 }406416407 fn get_raw(&self, s: State, key: IStr, real_this: Self) -> Result<Option<Val>> {417 fn get_raw(&self, key: IStr, real_this: Self) -> Result<Option<Val>> {408 match (self.0.this_entries.get(&key), &self.0.sup) {418 match (self.0.this_entries.get(&key), &self.0.sup) {409 (Some(k), None) => Ok(Some(self.evaluate_this(s, k, real_this)?)),419 (Some(k), None) => Ok(Some(self.evaluate_this(k, real_this)?)),410 (Some(k), Some(super_obj)) => {420 (Some(k), Some(super_obj)) => {411 let our = self.evaluate_this(s.clone(), k, real_this.clone())?;421 let our = self.evaluate_this(k, real_this.clone())?;412 if k.add {422 if k.add {413 super_obj423 super_obj414 .get_raw(s.clone(), key, real_this)?424 .get_raw(key, real_this)?415 .map_or(Ok(Some(our.clone())), |v| {425 .map_or(Ok(Some(our.clone())), |v| {416 Ok(Some(evaluate_add_op(s.clone(), &v, &our)?))426 Ok(Some(evaluate_add_op(&v, &our)?))417 })427 })418 } else {428 } else {419 Ok(Some(our))429 Ok(Some(our))420 }430 }421 }431 }422 (None, Some(super_obj)) => super_obj.get_raw(s, key, real_this),432 (None, Some(super_obj)) => super_obj.get_raw(key, real_this),423 (None, None) => Ok(None),433 (None, None) => Ok(None),424 }434 }425 }435 }426 fn evaluate_this(&self, s: State, v: &ObjMember, real_this: Self) -> Result<Val> {436 fn evaluate_this(&self, v: &ObjMember, real_this: Self) -> Result<Val> {427 v.invoke437 v.invoke428 .evaluate(s.clone(), self.0.sup.clone(), Some(real_this))?438 .evaluate(self.0.sup.clone(), Some(real_this))?429 .evaluate(s)439 .evaluate()430 }440 }431441432 fn run_assertions_raw(&self, s: State, real_this: &Self) -> Result<()> {442 fn run_assertions_raw(&self, real_this: &Self) -> Result<()> {433 if self.0.assertions_ran.borrow_mut().insert(real_this.clone()) {443 if self.0.assertions_ran.borrow_mut().insert(real_this.clone()) {434 for assertion in self.0.assertions.iter() {444 for assertion in self.0.assertions.iter() {435 if let Err(e) =445 if let Err(e) = assertion.run(self.0.sup.clone(), Some(real_this.clone())) {436 assertion.run(s.clone(), self.0.sup.clone(), Some(real_this.clone()))437 {438 self.0.assertions_ran.borrow_mut().remove(real_this);446 self.0.assertions_ran.borrow_mut().remove(real_this);439 return Err(e);447 return Err(e);440 }448 }441 }449 }442 if let Some(super_obj) = &self.0.sup {450 if let Some(super_obj) = &self.0.sup {443 super_obj.run_assertions_raw(s, real_this)?;451 super_obj.run_assertions_raw(real_this)?;444 }452 }445 }453 }446 Ok(())454 Ok(())447 }455 }448 pub fn run_assertions(&self, s: State) -> Result<()> {456 pub fn run_assertions(&self) -> Result<()> {449 self.run_assertions_raw(s, self)457 self.run_assertions_raw(self)450 }458 }451459452 pub fn ptr_eq(a: &Self, b: &Self) -> bool {460 pub fn ptr_eq(a: &Self, b: &Self) -> bool {crates/jrsonnet-evaluator/src/stdlib/format.rsdiffbeforeafterboth6use jrsonnet_types::ValType;6use jrsonnet_types::ValType;7use thiserror::Error;7use thiserror::Error;889use crate::{error::Error::*, throw, typed::Typed, LocError, ObjValue, Result, State, Val};9use crate::{error::Error::*, throw, typed::Typed, LocError, ObjValue, Result, Val};101011#[derive(Debug, Clone, Error, Trace)]11#[derive(Debug, Clone, Error, Trace)]12pub enum FormatError {12pub enum FormatError {472472473#[allow(clippy::too_many_lines)]473#[allow(clippy::too_many_lines)]474pub fn format_code(474pub fn format_code(475 s: State,476 out: &mut String,475 out: &mut String,477 value: &Val,476 value: &Val,478 code: &Code<'_>,477 code: &Code<'_>,491 let mut tmp_out = String::new();490 let mut tmp_out = String::new();492491493 match code.convtype {492 match code.convtype {494 ConvTypeV::String => tmp_out.push_str(&value.clone().to_string(s)?),493 ConvTypeV::String => tmp_out.push_str(&value.clone().to_string()?),495 ConvTypeV::Decimal => {494 ConvTypeV::Decimal => {496 let value = f64::from_untyped(value.clone(), s)?;495 let value = f64::from_untyped(value.clone())?;497 render_decimal(496 render_decimal(498 &mut tmp_out,497 &mut tmp_out,499 value,498 value,504 );503 );505 }504 }506 ConvTypeV::Octal => {505 ConvTypeV::Octal => {507 let value = f64::from_untyped(value.clone(), s)?;506 let value = f64::from_untyped(value.clone())?;508 render_octal(507 render_octal(509 &mut tmp_out,508 &mut tmp_out,510 value,509 value,516 );515 );517 }516 }518 ConvTypeV::Hexadecimal => {517 ConvTypeV::Hexadecimal => {519 let value = f64::from_untyped(value.clone(), s)?;518 let value = f64::from_untyped(value.clone())?;520 render_hexadecimal(519 render_hexadecimal(521 &mut tmp_out,520 &mut tmp_out,522 value,521 value,529 );528 );530 }529 }531 ConvTypeV::Scientific => {530 ConvTypeV::Scientific => {532 let value = f64::from_untyped(value.clone(), s)?;531 let value = f64::from_untyped(value.clone())?;533 render_float_sci(532 render_float_sci(534 &mut tmp_out,533 &mut tmp_out,535 value,534 value,543 );542 );544 }543 }545 ConvTypeV::Float => {544 ConvTypeV::Float => {546 let value = f64::from_untyped(value.clone(), s)?;545 let value = f64::from_untyped(value.clone())?;547 render_float(546 render_float(548 &mut tmp_out,547 &mut tmp_out,549 value,548 value,556 );555 );557 }556 }558 ConvTypeV::Shorter => {557 ConvTypeV::Shorter => {559 let value = f64::from_untyped(value.clone(), s)?;558 let value = f64::from_untyped(value.clone())?;560 let exponent = value.log10().floor();559 let exponent = value.log10().floor();561 if exponent < -4.0 || exponent >= fpprec as f64 {560 if exponent < -4.0 || exponent >= fpprec as f64 {562 render_float_sci(561 render_float_sci(623 Ok(())622 Ok(())624}623}625624626pub fn format_arr(s: State, str: &str, mut values: &[Val]) -> Result<String> {625pub fn format_arr(str: &str, mut values: &[Val]) -> Result<String> {627 let codes = parse_codes(str)?;626 let codes = parse_codes(str)?;628 let mut out = String::new();627 let mut out = String::new();629628640 }639 }641 let value = &values[0];640 let value = &values[0];642 values = &values[1..];641 values = &values[1..];643 usize::from_untyped(value.clone(), s.clone())?642 usize::from_untyped(value.clone())?644 }643 }645 Width::Fixed(n) => n,644 Width::Fixed(n) => n,646 };645 };651 }650 }652 let value = &values[0];651 let value = &values[0];653 values = &values[1..];652 values = &values[1..];654 Some(usize::from_untyped(value.clone(), s.clone())?)653 Some(usize::from_untyped(value.clone())?)655 }654 }656 Some(Width::Fixed(n)) => Some(n),655 Some(Width::Fixed(n)) => Some(n),657 None => None,656 None => None,669 value668 value670 };669 };671670672 format_code(s.clone(), &mut out, value, &c, width, precision)?;671 format_code(&mut out, value, &c, width, precision)?;673 }672 }674 }673 }675 }674 }676675677 Ok(out)676 Ok(out)678}677}679678680pub fn format_obj(s: State, str: &str, values: &ObjValue) -> Result<String> {679pub fn format_obj(str: &str, values: &ObjValue) -> Result<String> {681 let codes = parse_codes(str)?;680 let codes = parse_codes(str)?;682 let mut out = String::new();681 let mut out = String::new();683682709 if f.is_empty() {708 if f.is_empty() {710 throw!(MappingKeysRequired);709 throw!(MappingKeysRequired);711 }710 }712 if let Some(v) = values.get(s.clone(), f.clone())? {711 if let Some(v) = values.get(f.clone())? {713 v712 v714 } else {713 } else {715 throw!(NoSuchFormatField(f));714 throw!(NoSuchFormatField(f));716 }715 }717 };716 };718717719 format_code(s.clone(), &mut out, &value, &c, width, precision)?;718 format_code(&mut out, &value, &c, width, precision)?;720 }719 }721 }720 }722 }721 }742741743 #[test]742 #[test]744 fn octals() {743 fn octals() {745 let s = State::default();746 assert_eq!(744 assert_eq!(format_arr("%#o", &[Val::Num(8.0)]).unwrap(), "010");747 format_arr(s.clone(), "%#o", &[Val::Num(8.0)]).unwrap(),748 "010"749 );750 assert_eq!(745 assert_eq!(format_arr("%#4o", &[Val::Num(8.0)]).unwrap(), " 010");751 format_arr(s.clone(), "%#4o", &[Val::Num(8.0)]).unwrap(),752 " 010"753 );754 assert_eq!(746 assert_eq!(format_arr("%4o", &[Val::Num(8.0)]).unwrap(), " 10");755 format_arr(s.clone(), "%4o", &[Val::Num(8.0)]).unwrap(),756 " 10"757 );758 assert_eq!(747 assert_eq!(format_arr("%04o", &[Val::Num(8.0)]).unwrap(), "0010");759 format_arr(s.clone(), "%04o", &[Val::Num(8.0)]).unwrap(),760 "0010"761 );762 assert_eq!(748 assert_eq!(format_arr("%+4o", &[Val::Num(8.0)]).unwrap(), " +10");763 format_arr(s.clone(), "%+4o", &[Val::Num(8.0)]).unwrap(),764 " +10"765 );766 assert_eq!(749 assert_eq!(format_arr("%+04o", &[Val::Num(8.0)]).unwrap(), "+010");767 format_arr(s.clone(), "%+04o", &[Val::Num(8.0)]).unwrap(),768 "+010"769 );770 assert_eq!(750 assert_eq!(format_arr("%-4o", &[Val::Num(8.0)]).unwrap(), "10 ");771 format_arr(s.clone(), "%-4o", &[Val::Num(8.0)]).unwrap(),772 "10 "773 );774 assert_eq!(751 assert_eq!(format_arr("%+-4o", &[Val::Num(8.0)]).unwrap(), "+10 ");775 format_arr(s.clone(), "%+-4o", &[Val::Num(8.0)]).unwrap(),776 "+10 "777 );778 assert_eq!(format_arr(s, "%+-04o", &[Val::Num(8.0)]).unwrap(), "+10 ");752 assert_eq!(format_arr("%+-04o", &[Val::Num(8.0)]).unwrap(), "+10 ");779 }753 }780754781 #[test]755 #[test]782 fn percent_doesnt_consumes_values() {756 fn percent_doesnt_consumes_values() {783 let s = State::default();784 assert_eq!(757 assert_eq!(785 format_arr(758 format_arr(786 s,787 "How much error budget is left looking at our %.3f%% availability gurantees?",759 "How much error budget is left looking at our %.3f%% availability gurantees?",788 &[Val::Num(4.0)]760 &[Val::Num(4.0)]789 )761 )crates/jrsonnet-evaluator/src/stdlib/manifest.rsdiffbeforeafterboth25 pub preserve_order: bool,25 pub preserve_order: bool,26}26}272728pub fn manifest_json_ex(s: State, val: &Val, options: &ManifestJsonOptions<'_>) -> Result<String> {28pub fn manifest_json_ex(val: &Val, options: &ManifestJsonOptions<'_>) -> Result<String> {29 let mut out = String::new();29 let mut out = String::new();30 manifest_json_ex_buf(s, val, &mut out, &mut String::new(), options)?;30 manifest_json_ex_buf(val, &mut out, &mut String::new(), options)?;31 Ok(out)31 Ok(out)32}32}33fn manifest_json_ex_buf(33fn manifest_json_ex_buf(34 s: State,35 val: &Val,34 val: &Val,36 buf: &mut String,35 buf: &mut String,37 cur_padding: &mut String,36 cur_padding: &mut String,595860 let old_len = cur_padding.len();59 let old_len = cur_padding.len();61 cur_padding.push_str(options.padding);60 cur_padding.push_str(options.padding);62 for (i, item) in items.iter(s.clone()).enumerate() {61 for (i, item) in items.iter().enumerate() {63 if i != 0 {62 if i != 0 {64 buf.push(',');63 buf.push(',');65 if mtype == ManifestType::ToString {64 if mtype == ManifestType::ToString {69 }68 }70 }69 }71 buf.push_str(cur_padding);70 buf.push_str(cur_padding);72 manifest_json_ex_buf(s.clone(), &item?, buf, cur_padding, options)?;71 manifest_json_ex_buf(&item?, buf, cur_padding, options)?;73 }72 }74 cur_padding.truncate(old_len);73 cur_padding.truncate(old_len);757486 buf.push(']');85 buf.push(']');87 }86 }88 Val::Obj(obj) => {87 Val::Obj(obj) => {89 obj.run_assertions(s.clone())?;88 obj.run_assertions()?;90 buf.push('{');89 buf.push('{');91 let fields = obj.fields(90 let fields = obj.fields(92 #[cfg(feature = "exp-preserve-order")]91 #[cfg(feature = "exp-preserve-order")]114 State::push_description(113 State::push_description(115 || format!("field <{}> manifestification", field.clone()),114 || format!("field <{}> manifestification", field.clone()),116 || {115 || {117 let value = obj.get(s.clone(), field.clone())?.unwrap();116 let value = obj.get(field.clone())?.unwrap();118 manifest_json_ex_buf(s.clone(), &value, buf, cur_padding, options)?;117 manifest_json_ex_buf(&value, buf, cur_padding, options)?;119 Ok(())118 Ok(())120 },119 },121 )?;120 )?;222 || string.parse::<f64>().is_ok()221 || string.parse::<f64>().is_ok()223}222}224223225pub fn manifest_yaml_ex(s: State, val: &Val, options: &ManifestYamlOptions<'_>) -> Result<String> {224pub fn manifest_yaml_ex(val: &Val, options: &ManifestYamlOptions<'_>) -> Result<String> {226 let mut out = String::new();225 let mut out = String::new();227 manifest_yaml_ex_buf(s, val, &mut out, &mut String::new(), options)?;226 manifest_yaml_ex_buf(val, &mut out, &mut String::new(), options)?;228 Ok(out)227 Ok(out)229}228}230229231#[allow(clippy::too_many_lines)]230#[allow(clippy::too_many_lines)]232fn manifest_yaml_ex_buf(231fn manifest_yaml_ex_buf(233 s: State,234 val: &Val,232 val: &Val,235 buf: &mut String,233 buf: &mut String,236 cur_padding: &mut String,234 cur_padding: &mut String,268 if a.is_empty() {266 if a.is_empty() {269 buf.push_str("[]");267 buf.push_str("[]");270 } else {268 } else {271 for (i, item) in a.iter(s.clone()).enumerate() {269 for (i, item) in a.iter().enumerate() {272 if i != 0 {270 if i != 0 {273 buf.push('\n');271 buf.push('\n');274 buf.push_str(cur_padding);272 buf.push_str(cur_padding);292 if extra_padding {290 if extra_padding {293 cur_padding.push_str(options.padding);291 cur_padding.push_str(options.padding);294 }292 }295 manifest_yaml_ex_buf(s.clone(), &item, buf, cur_padding, options)?;293 manifest_yaml_ex_buf(&item, buf, cur_padding, options)?;296 cur_padding.truncate(prev_len);294 cur_padding.truncate(prev_len);297 }295 }298 }296 }320 }318 }321 buf.push(':');319 buf.push(':');322 let prev_len = cur_padding.len();320 let prev_len = cur_padding.len();323 let item = o.get(s.clone(), key.clone())?.expect("field exists");321 let item = o.get(key.clone())?.expect("field exists");324 match &item {322 match &item {325 Val::Arr(a) if !a.is_empty() => {323 Val::Arr(a) if !a.is_empty() => {326 buf.push('\n');324 buf.push('\n');336 }334 }337 _ => buf.push(' '),335 _ => buf.push(' '),338 }336 }339 manifest_yaml_ex_buf(s.clone(), &item, buf, cur_padding, options)?;337 manifest_yaml_ex_buf(&item, buf, cur_padding, options)?;340 cur_padding.truncate(prev_len);338 cur_padding.truncate(prev_len);341 }339 }342 }340 }crates/jrsonnet-evaluator/src/stdlib/mod.rsdiffbeforeafterboth9pub mod format;9pub mod format;10pub mod manifest;10pub mod manifest;111112pub fn std_format(s: State, str: IStr, vals: Val) -> Result<String> {12pub fn std_format(str: IStr, vals: Val) -> Result<String> {13 State::push(13 State::push(14 CallLocation::native(),14 CallLocation::native(),15 || format!("std.format of {str}"),15 || format!("std.format of {str}"),16 || {16 || {17 Ok(match vals {17 Ok(match vals {18 Val::Arr(vals) => format_arr(s.clone(), &str, &vals.evaluated(s.clone())?)?,18 Val::Arr(vals) => format_arr(&str, &vals.evaluated()?)?,19 Val::Obj(obj) => format_obj(s.clone(), &str, &obj)?,19 Val::Obj(obj) => format_obj(&str, &obj)?,20 o => format_arr(s.clone(), &str, &[o])?,20 o => format_arr(&str, &[o])?,21 })21 })22 },22 },23 )23 )crates/jrsonnet-evaluator/src/trace/mod.rsdiffbeforeafterboth1use std::path::{Path, PathBuf};1use std::path::{Path, PathBuf};223use jrsonnet_gcmodule::Trace;3use jrsonnet_parser::{CodeLocation, Source};4use jrsonnet_parser::{CodeLocation, Source};455use crate::{error::Error, LocError, State};6use crate::{error::Error, LocError, State};677/// The way paths should be displayed8/// The way paths should be displayed8#[derive(Clone)]9#[derive(Clone, Trace)]9pub enum PathResolver {10pub enum PathResolver {10 /// Only filename11 /// Only filename11 FileName,12 FileName,434444/// Implements pretty-printing of traces45/// Implements pretty-printing of traces45#[allow(clippy::module_name_repetitions)]46#[allow(clippy::module_name_repetitions)]46pub trait TraceFormat {47pub trait TraceFormat: Trace {47 fn write_trace(48 fn write_trace(48 &self,49 &self,49 out: &mut dyn std::fmt::Write,50 out: &mut dyn std::fmt::Write,77}78}787979/// vanilla-like jsonnet formatting80/// vanilla-like jsonnet formatting81#[derive(Trace)]80pub struct CompactFormat {82pub struct CompactFormat {81 pub resolver: PathResolver,83 pub resolver: PathResolver,82 pub padding: usize,84 pub padding: usize,168 }170 }169}171}170172173#[derive(Trace)]171pub struct JsFormat;174pub struct JsFormat;172impl TraceFormat for JsFormat {175impl TraceFormat for JsFormat {173 fn write_trace(176 fn write_trace(202205203/// rustc-like trace displaying206/// rustc-like trace displaying204#[cfg(feature = "explaining-traces")]207#[cfg(feature = "explaining-traces")]208#[derive(Trace)]205pub struct ExplainingFormat {209pub struct ExplainingFormat {206 pub resolver: PathResolver,210 pub resolver: PathResolver,207}211}222 .into_iter()226 .into_iter()223 .next()227 .next()224 .unwrap();228 .unwrap();225 let mut end_location = location.clone();229 let mut end_location = location;226 end_location.offset += 1;230 end_location.offset += 1;227231228 self.print_snippet(232 self.print_snippet(crates/jrsonnet-evaluator/src/typed/conversions.rsdiffbeforeafterboth778use crate::{8use crate::{9 error::Result,9 error::Result,10 function::{FuncDesc, FuncVal},10 function::{native::NativeDesc, FuncDesc, FuncVal},11 throw,11 throw,12 typed::CheckType,12 typed::CheckType,13 val::{ArrValue, IndexableVal},13 val::{ArrValue, IndexableVal},14 ObjValue, ObjValueBuilder, State, Val,14 ObjValue, ObjValueBuilder, Val,15};15};161617pub trait TypedObj: Typed {17pub trait TypedObj: Typed {18 fn serialize(self, s: State, out: &mut ObjValueBuilder) -> Result<()>;18 fn serialize(self, out: &mut ObjValueBuilder) -> Result<()>;19 fn parse(obj: &ObjValue, s: State) -> Result<Self>;19 fn parse(obj: &ObjValue) -> Result<Self>;20 fn into_object(self, s: State) -> Result<ObjValue> {20 fn into_object(self) -> Result<ObjValue> {21 let mut builder = ObjValueBuilder::new();21 let mut builder = ObjValueBuilder::new();22 self.serialize(s, &mut builder)?;22 self.serialize(&mut builder)?;23 Ok(builder.build())23 Ok(builder.build())24 }24 }25}25}262627pub trait Typed: Sized {27pub trait Typed: Sized {28 const TYPE: &'static ComplexValType;28 const TYPE: &'static ComplexValType;29 fn into_untyped(typed: Self, s: State) -> Result<Val>;29 fn into_untyped(typed: Self) -> Result<Val>;30 fn from_untyped(untyped: Val, s: State) -> Result<Self>;30 fn from_untyped(untyped: Val) -> Result<Self>;31}31}323233macro_rules! impl_int {33macro_rules! impl_int {34 ($($ty:ty)*) => {$(34 ($($ty:ty)*) => {$(35 impl Typed for $ty {35 impl Typed for $ty {36 const TYPE: &'static ComplexValType =36 const TYPE: &'static ComplexValType =37 &ComplexValType::BoundedNumber(Some(Self::MIN as f64), Some(Self::MAX as f64));37 &ComplexValType::BoundedNumber(Some(Self::MIN as f64), Some(Self::MAX as f64));38 fn from_untyped(value: Val, s: State) -> Result<Self> {38 fn from_untyped(value: Val) -> Result<Self> {39 <Self as Typed>::TYPE.check(s, &value)?;39 <Self as Typed>::TYPE.check(&value)?;40 match value {40 match value {41 Val::Num(n) => {41 Val::Num(n) => {42 #[allow(clippy::float_cmp)]42 #[allow(clippy::float_cmp)]51 _ => unreachable!(),51 _ => unreachable!(),52 }52 }53 }53 }54 fn into_untyped(value: Self, _: State) -> Result<Val> {54 fn into_untyped(value: Self) -> Result<Val> {55 Ok(Val::Num(value as f64))55 Ok(Val::Num(value as f64))56 }56 }57 }57 }90 Some(MAX as f64),90 Some(MAX as f64),91 );91 );929293 fn from_untyped(value: Val, s: State) -> Result<Self> {93 fn from_untyped(value: Val) -> Result<Self> {94 <Self as Typed>::TYPE.check(s, &value)?;94 <Self as Typed>::TYPE.check(&value)?;95 match value {95 match value {96 Val::Num(n) => {96 Val::Num(n) => {97 #[allow(clippy::float_cmp)]97 #[allow(clippy::float_cmp)]107 }107 }108 }108 }109109110 fn into_untyped(value: Self, _: State) -> Result<Val> {110 fn into_untyped(value: Self) -> Result<Val> {111 Ok(Val::Num(value.0 as f64))111 Ok(Val::Num(value.0 as f64))112 }112 }113 }113 }125impl Typed for f64 {125impl Typed for f64 {126 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Num);126 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Num);127127128 fn into_untyped(value: Self, _: State) -> Result<Val> {128 fn into_untyped(value: Self) -> Result<Val> {129 Ok(Val::Num(value))129 Ok(Val::Num(value))130 }130 }131131132 fn from_untyped(value: Val, s: State) -> Result<Self> {132 fn from_untyped(value: Val) -> Result<Self> {133 <Self as Typed>::TYPE.check(s, &value)?;133 <Self as Typed>::TYPE.check(&value)?;134 match value {134 match value {135 Val::Num(n) => Ok(n),135 Val::Num(n) => Ok(n),136 _ => unreachable!(),136 _ => unreachable!(),142impl Typed for PositiveF64 {142impl Typed for PositiveF64 {143 const TYPE: &'static ComplexValType = &ComplexValType::BoundedNumber(Some(0.0), None);143 const TYPE: &'static ComplexValType = &ComplexValType::BoundedNumber(Some(0.0), None);144144145 fn into_untyped(value: Self, _: State) -> Result<Val> {145 fn into_untyped(value: Self) -> Result<Val> {146 Ok(Val::Num(value.0))146 Ok(Val::Num(value.0))147 }147 }148148149 fn from_untyped(value: Val, s: State) -> Result<Self> {149 fn from_untyped(value: Val) -> Result<Self> {150 <Self as Typed>::TYPE.check(s, &value)?;150 <Self as Typed>::TYPE.check(&value)?;151 match value {151 match value {152 Val::Num(n) => Ok(Self(n)),152 Val::Num(n) => Ok(Self(n)),153 _ => unreachable!(),153 _ => unreachable!(),159 const TYPE: &'static ComplexValType =159 const TYPE: &'static ComplexValType =160 &ComplexValType::BoundedNumber(Some(0.0), Some(u32::MAX as f64));160 &ComplexValType::BoundedNumber(Some(0.0), Some(u32::MAX as f64));161161162 fn into_untyped(value: Self, _: State) -> Result<Val> {162 fn into_untyped(value: Self) -> Result<Val> {163 if value > u32::MAX as Self {163 if value > u32::MAX as Self {164 throw!("number is too large")164 throw!("number is too large")165 }165 }166 Ok(Val::Num(value as f64))166 Ok(Val::Num(value as f64))167 }167 }168168169 fn from_untyped(value: Val, s: State) -> Result<Self> {169 fn from_untyped(value: Val) -> Result<Self> {170 <Self as Typed>::TYPE.check(s, &value)?;170 <Self as Typed>::TYPE.check(&value)?;171 match value {171 match value {172 Val::Num(n) => {172 Val::Num(n) => {173 #[allow(clippy::float_cmp)]173 #[allow(clippy::float_cmp)]184impl Typed for IStr {184impl Typed for IStr {185 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Str);185 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Str);186186187 fn into_untyped(value: Self, _: State) -> Result<Val> {187 fn into_untyped(value: Self) -> Result<Val> {188 Ok(Val::Str(value))188 Ok(Val::Str(value))189 }189 }190190191 fn from_untyped(value: Val, s: State) -> Result<Self> {191 fn from_untyped(value: Val) -> Result<Self> {192 <Self as Typed>::TYPE.check(s, &value)?;192 <Self as Typed>::TYPE.check(&value)?;193 match value {193 match value {194 Val::Str(s) => Ok(s),194 Val::Str(s) => Ok(s),195 _ => unreachable!(),195 _ => unreachable!(),200impl Typed for String {200impl Typed for String {201 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Str);201 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Str);202202203 fn into_untyped(value: Self, _: State) -> Result<Val> {203 fn into_untyped(value: Self) -> Result<Val> {204 Ok(Val::Str(value.into()))204 Ok(Val::Str(value.into()))205 }205 }206206207 fn from_untyped(value: Val, s: State) -> Result<Self> {207 fn from_untyped(value: Val) -> Result<Self> {208 <Self as Typed>::TYPE.check(s, &value)?;208 <Self as Typed>::TYPE.check(&value)?;209 match value {209 match value {210 Val::Str(s) => Ok(s.to_string()),210 Val::Str(s) => Ok(s.to_string()),211 _ => unreachable!(),211 _ => unreachable!(),216impl Typed for char {216impl Typed for char {217 const TYPE: &'static ComplexValType = &ComplexValType::Char;217 const TYPE: &'static ComplexValType = &ComplexValType::Char;218218219 fn into_untyped(value: Self, _: State) -> Result<Val> {219 fn into_untyped(value: Self) -> Result<Val> {220 Ok(Val::Str(value.to_string().into()))220 Ok(Val::Str(value.to_string().into()))221 }221 }222222223 fn from_untyped(value: Val, s: State) -> Result<Self> {223 fn from_untyped(value: Val) -> Result<Self> {224 <Self as Typed>::TYPE.check(s, &value)?;224 <Self as Typed>::TYPE.check(&value)?;225 match value {225 match value {226 Val::Str(s) => Ok(s.chars().next().unwrap()),226 Val::Str(s) => Ok(s.chars().next().unwrap()),227 _ => unreachable!(),227 _ => unreachable!(),235{235{236 const TYPE: &'static ComplexValType = &ComplexValType::ArrayRef(T::TYPE);236 const TYPE: &'static ComplexValType = &ComplexValType::ArrayRef(T::TYPE);237237238 fn into_untyped(value: Self, s: State) -> Result<Val> {238 fn into_untyped(value: Self) -> Result<Val> {239 let mut o = Vec::with_capacity(value.len());239 let mut o = Vec::with_capacity(value.len());240 for i in value {240 for i in value {241 o.push(T::into_untyped(i, s.clone())?);241 o.push(T::into_untyped(i)?);242 }242 }243 Ok(Val::Arr(o.into()))243 Ok(Val::Arr(o.into()))244 }244 }245245246 fn from_untyped(value: Val, s: State) -> Result<Self> {246 fn from_untyped(value: Val) -> Result<Self> {247 <Self as Typed>::TYPE.check(s.clone(), &value)?;247 <Self as Typed>::TYPE.check(&value)?;248 match value {248 match value {249 Val::Arr(a) => {249 Val::Arr(a) => {250 let mut o = Self::with_capacity(a.len());250 let mut o = Self::with_capacity(a.len());251 for i in a.iter(s.clone()) {251 for i in a.iter() {252 o.push(T::from_untyped(i?, s.clone())?);252 o.push(T::from_untyped(i?)?);253 }253 }254 Ok(o)254 Ok(o)255 }255 }266impl Typed for Any {266impl Typed for Any {267 const TYPE: &'static ComplexValType = &ComplexValType::Any;267 const TYPE: &'static ComplexValType = &ComplexValType::Any;268268269 fn into_untyped(value: Self, _: State) -> Result<Val> {269 fn into_untyped(value: Self) -> Result<Val> {270 Ok(value.0)270 Ok(value.0)271 }271 }272272273 fn from_untyped(value: Val, _: State) -> Result<Self> {273 fn from_untyped(value: Val) -> Result<Self> {274 Ok(Self(value))274 Ok(Self(value))275 }275 }276}276}281impl Typed for VecVal {281impl Typed for VecVal {282 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Arr);282 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Arr);283283284 fn into_untyped(value: Self, _: State) -> Result<Val> {284 fn into_untyped(value: Self) -> Result<Val> {285 Ok(Val::Arr(ArrValue::Eager(value.0)))285 Ok(Val::Arr(ArrValue::Eager(value.0)))286 }286 }287287288 fn from_untyped(value: Val, s: State) -> Result<Self> {288 fn from_untyped(value: Val) -> Result<Self> {289 <Self as Typed>::TYPE.check(s.clone(), &value)?;289 <Self as Typed>::TYPE.check(&value)?;290 match value {290 match value {291 Val::Arr(a) => Ok(Self(a.evaluated(s)?)),291 Val::Arr(a) => Ok(Self(a.evaluated()?)),292 _ => unreachable!(),292 _ => unreachable!(),293 }293 }294 }294 }299 const TYPE: &'static ComplexValType =299 const TYPE: &'static ComplexValType =300 &ComplexValType::ArrayRef(&ComplexValType::BoundedNumber(Some(0.0), Some(255.0)));300 &ComplexValType::ArrayRef(&ComplexValType::BoundedNumber(Some(0.0), Some(255.0)));301301302 fn into_untyped(value: Self, _: State) -> Result<Val> {302 fn into_untyped(value: Self) -> Result<Val> {303 Ok(Val::Arr(ArrValue::Bytes(value)))303 Ok(Val::Arr(ArrValue::Bytes(value)))304 }304 }305305306 fn from_untyped(value: Val, s: State) -> Result<Self> {306 fn from_untyped(value: Val) -> Result<Self> {307 if let Val::Arr(ArrValue::Bytes(bytes)) = value {307 if let Val::Arr(ArrValue::Bytes(bytes)) = value {308 return Ok(bytes);308 return Ok(bytes);309 }309 }310 <Self as Typed>::TYPE.check(s.clone(), &value)?;310 <Self as Typed>::TYPE.check(&value)?;311 match value {311 match value {312 Val::Arr(a) => {312 Val::Arr(a) => {313 let mut out = Vec::with_capacity(a.len());313 let mut out = Vec::with_capacity(a.len());314 for e in a.iter(s.clone()) {314 for e in a.iter() {315 let r = e?;315 let r = e?;316 out.push(u8::from_untyped(r, s.clone())?);316 out.push(u8::from_untyped(r)?);317 }317 }318 Ok(out.as_slice().into())318 Ok(out.as_slice().into())319 }319 }326impl Typed for M1 {326impl Typed for M1 {327 const TYPE: &'static ComplexValType = &ComplexValType::BoundedNumber(Some(-1.0), Some(-1.0));327 const TYPE: &'static ComplexValType = &ComplexValType::BoundedNumber(Some(-1.0), Some(-1.0));328328329 fn into_untyped(_: Self, _: State) -> Result<Val> {329 fn into_untyped(_: Self) -> Result<Val> {330 Ok(Val::Num(-1.0))330 Ok(Val::Num(-1.0))331 }331 }332332333 fn from_untyped(value: Val, s: State) -> Result<Self> {333 fn from_untyped(value: Val) -> Result<Self> {334 <Self as Typed>::TYPE.check(s, &value)?;334 <Self as Typed>::TYPE.check(&value)?;335 Ok(Self)335 Ok(Self)336 }336 }337}337}338338339macro_rules! decl_either {339macro_rules! decl_either {340 ($($name: ident, $($id: ident)*);*) => {$(340 ($($name: ident, $($id: ident)*);*) => {$(341 #[derive(Clone)]341 pub enum $name<$($id),*> {342 pub enum $name<$($id),*> {342 $($id($id)),*343 $($id($id)),*343 }344 }347 {348 {348 const TYPE: &'static ComplexValType = &ComplexValType::UnionRef(&[$($id::TYPE),*]);349 const TYPE: &'static ComplexValType = &ComplexValType::UnionRef(&[$($id::TYPE),*]);349350350 fn into_untyped(value: Self, s: State) -> Result<Val> {351 fn into_untyped(value: Self) -> Result<Val> {351 match value {$(352 match value {$(352 $name::$id(v) => $id::into_untyped(v, s)353 $name::$id(v) => $id::into_untyped(v)353 ),*}354 ),*}354 }355 }355356356 fn from_untyped(value: Val, s: State) -> Result<Self> {357 fn from_untyped(value: Val) -> Result<Self> {357 $(358 $(358 if $id::TYPE.check(s.clone(), &value).is_ok() {359 if $id::TYPE.check(&value).is_ok() {359 $id::from_untyped(value, s.clone()).map(Self::$id)360 $id::from_untyped(value).map(Self::$id)360 } else361 } else361 )* {362 )* {362 <Self as Typed>::TYPE.check(s, &value)?;363 <Self as Typed>::TYPE.check(&value)?;363 unreachable!()364 unreachable!()364 }365 }365 }366 }392impl Typed for ArrValue {393impl Typed for ArrValue {393 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Arr);394 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Arr);394395395 fn into_untyped(value: Self, _: State) -> Result<Val> {396 fn into_untyped(value: Self) -> Result<Val> {396 Ok(Val::Arr(value))397 Ok(Val::Arr(value))397 }398 }398399399 fn from_untyped(value: Val, s: State) -> Result<Self> {400 fn from_untyped(value: Val) -> Result<Self> {400 <Self as Typed>::TYPE.check(s, &value)?;401 <Self as Typed>::TYPE.check(&value)?;401 match value {402 match value {402 Val::Arr(a) => Ok(a),403 Val::Arr(a) => Ok(a),403 _ => unreachable!(),404 _ => unreachable!(),408impl Typed for FuncVal {409impl Typed for FuncVal {409 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Func);410 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Func);410411411 fn into_untyped(value: Self, _: State) -> Result<Val> {412 fn into_untyped(value: Self) -> Result<Val> {412 Ok(Val::Func(value))413 Ok(Val::Func(value))413 }414 }414415415 fn from_untyped(value: Val, s: State) -> Result<Self> {416 fn from_untyped(value: Val) -> Result<Self> {416 <Self as Typed>::TYPE.check(s, &value)?;417 <Self as Typed>::TYPE.check(&value)?;417 match value {418 match value {418 Val::Func(a) => Ok(a),419 Val::Func(a) => Ok(a),419 _ => unreachable!(),420 _ => unreachable!(),424impl Typed for Cc<FuncDesc> {425impl Typed for Cc<FuncDesc> {425 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Func);426 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Func);426427427 fn into_untyped(value: Self, _: State) -> Result<Val> {428 fn into_untyped(value: Self) -> Result<Val> {428 Ok(Val::Func(FuncVal::Normal(value)))429 Ok(Val::Func(FuncVal::Normal(value)))429 }430 }430431431 fn from_untyped(value: Val, s: State) -> Result<Self> {432 fn from_untyped(value: Val) -> Result<Self> {432 <Self as Typed>::TYPE.check(s, &value)?;433 <Self as Typed>::TYPE.check(&value)?;433 match value {434 match value {434 Val::Func(FuncVal::Normal(desc)) => Ok(desc),435 Val::Func(FuncVal::Normal(desc)) => Ok(desc),435 Val::Func(_) => throw!("expected normal function, not builtin"),436 Val::Func(_) => throw!("expected normal function, not builtin"),441impl Typed for ObjValue {442impl Typed for ObjValue {442 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Obj);443 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Obj);443444444 fn into_untyped(value: Self, _: State) -> Result<Val> {445 fn into_untyped(value: Self) -> Result<Val> {445 Ok(Val::Obj(value))446 Ok(Val::Obj(value))446 }447 }447448448 fn from_untyped(value: Val, s: State) -> Result<Self> {449 fn from_untyped(value: Val) -> Result<Self> {449 <Self as Typed>::TYPE.check(s, &value)?;450 <Self as Typed>::TYPE.check(&value)?;450 match value {451 match value {451 Val::Obj(a) => Ok(a),452 Val::Obj(a) => Ok(a),452 _ => unreachable!(),453 _ => unreachable!(),457impl Typed for bool {458impl Typed for bool {458 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Bool);459 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Bool);459460460 fn into_untyped(value: Self, _: State) -> Result<Val> {461 fn into_untyped(value: Self) -> Result<Val> {461 Ok(Val::Bool(value))462 Ok(Val::Bool(value))462 }463 }463464464 fn from_untyped(value: Val, s: State) -> Result<Self> {465 fn from_untyped(value: Val) -> Result<Self> {465 <Self as Typed>::TYPE.check(s, &value)?;466 <Self as Typed>::TYPE.check(&value)?;466 match value {467 match value {467 Val::Bool(a) => Ok(a),468 Val::Bool(a) => Ok(a),468 _ => unreachable!(),469 _ => unreachable!(),475 &ComplexValType::Simple(ValType::Str),476 &ComplexValType::Simple(ValType::Str),476 ]);477 ]);477478478 fn into_untyped(value: Self, _: State) -> Result<Val> {479 fn into_untyped(value: Self) -> Result<Val> {479 match value {480 match value {480 IndexableVal::Str(s) => Ok(Val::Str(s)),481 IndexableVal::Str(s) => Ok(Val::Str(s)),481 IndexableVal::Arr(a) => Ok(Val::Arr(a)),482 IndexableVal::Arr(a) => Ok(Val::Arr(a)),482 }483 }483 }484 }484485485 fn from_untyped(value: Val, s: State) -> Result<Self> {486 fn from_untyped(value: Val) -> Result<Self> {486 <Self as Typed>::TYPE.check(s, &value)?;487 <Self as Typed>::TYPE.check(&value)?;487 value.into_indexable()488 value.into_indexable()488 }489 }489}490}492impl Typed for Null {493impl Typed for Null {493 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Null);494 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Null);494495495 fn into_untyped(_: Self, _: State) -> Result<Val> {496 fn into_untyped(_: Self) -> Result<Val> {496 Ok(Val::Null)497 Ok(Val::Null)497 }498 }498499499 fn from_untyped(value: Val, s: State) -> Result<Self> {500 fn from_untyped(value: Val) -> Result<Self> {500 <Self as Typed>::TYPE.check(s, &value)?;501 <Self as Typed>::TYPE.check(&value)?;501 Ok(Self)502 Ok(Self)502 }503 }503}504}505506pub struct NativeFn<D: NativeDesc>(D::Value);507impl<D: NativeDesc> Deref for NativeFn<D> {508 type Target = D::Value;509510 fn deref(&self) -> &Self::Target {511 &self.0512 }513}514impl<D: NativeDesc> Typed for NativeFn<D> {515 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Func);516517 fn into_untyped(_typed: Self) -> Result<Val> {518 throw!("can only convert functions from jsonnet to native")519 }520521 fn from_untyped(untyped: Val) -> Result<Self> {522 Ok(Self(523 untyped524 .as_func()525 .expect("shape is checked")526 .into_native::<D>(),527 ))528 }529}504530crates/jrsonnet-evaluator/src/typed/mod.rsdiffbeforeafterboth102102103// TODO: check_fast for fast path of union type checking103// TODO: check_fast for fast path of union type checking104pub trait CheckType {104pub trait CheckType {105 fn check(&self, s: State, value: &Val) -> Result<()>;105 fn check(&self, value: &Val) -> Result<()>;106}106}107107108impl CheckType for ValType {108impl CheckType for ValType {109 fn check(&self, _: State, value: &Val) -> Result<()> {109 fn check(&self, value: &Val) -> Result<()> {110 let got = value.value_type();110 let got = value.value_type();111 if got != *self {111 if got != *self {112 let loc_error: TypeLocError = TypeError::ExpectedGot((*self).into(), got).into();112 let loc_error: TypeLocError = TypeError::ExpectedGot((*self).into(), got).into();145145146impl CheckType for ComplexValType {146impl CheckType for ComplexValType {147 #[allow(clippy::too_many_lines)]147 #[allow(clippy::too_many_lines)]148 fn check(&self, s: State, value: &Val) -> Result<()> {148 fn check(&self, value: &Val) -> Result<()> {149 match self {149 match self {150 Self::Any => Ok(()),150 Self::Any => Ok(()),151 Self::Simple(t) => t.check(s, value),151 Self::Simple(t) => t.check(value),152 Self::Char => match value {152 Self::Char => match value {153 Val::Str(s) if s.len() == 1 || s.chars().count() == 1 => Ok(()),153 Val::Str(s) if s.len() == 1 || s.chars().count() == 1 => Ok(()),154 v => Err(TypeError::ExpectedGot(self.clone(), v.value_type()).into()),154 v => Err(TypeError::ExpectedGot(self.clone(), v.value_type()).into()),167 }167 }168 Self::Array(elem_type) => match value {168 Self::Array(elem_type) => match value {169 Val::Arr(a) => {169 Val::Arr(a) => {170 for (i, item) in a.iter(s.clone()).enumerate() {170 for (i, item) in a.iter().enumerate() {171 push_type_description(171 push_type_description(172 || format!("array index {i}"),172 || format!("array index {i}"),173 || ValuePathItem::Index(i as u64),173 || ValuePathItem::Index(i as u64),174 || elem_type.check(s.clone(), &item.clone()?),174 || elem_type.check(&item.clone()?),175 )?;175 )?;176 }176 }177 Ok(())177 Ok(())180 },180 },181 Self::ArrayRef(elem_type) => match value {181 Self::ArrayRef(elem_type) => match value {182 Val::Arr(a) => {182 Val::Arr(a) => {183 for (i, item) in a.iter(s.clone()).enumerate() {183 for (i, item) in a.iter().enumerate() {184 push_type_description(184 push_type_description(185 || format!("array index {i}"),185 || format!("array index {i}"),186 || ValuePathItem::Index(i as u64),186 || ValuePathItem::Index(i as u64),187 || elem_type.check(s.clone(), &item.clone()?),187 || elem_type.check(&item.clone()?),188 )?;188 )?;189 }189 }190 Ok(())190 Ok(())194 Self::ObjectRef(elems) => match value {194 Self::ObjectRef(elems) => match value {195 Val::Obj(obj) => {195 Val::Obj(obj) => {196 for (k, v) in elems.iter() {196 for (k, v) in elems.iter() {197 if let Some(got_v) = obj.get(s.clone(), (*k).into())? {197 if let Some(got_v) = obj.get((*k).into())? {198 push_type_description(198 push_type_description(199 || format!("property {k}"),199 || format!("property {k}"),200 || ValuePathItem::Field((*k).into()),200 || ValuePathItem::Field((*k).into()),201 || v.check(s.clone(), &got_v),201 || v.check(&got_v),202 )?;202 )?;203 } else {203 } else {204 return Err(204 return Err(213 Self::Union(types) => {213 Self::Union(types) => {214 let mut errors = Vec::new();214 let mut errors = Vec::new();215 for ty in types.iter() {215 for ty in types.iter() {216 match ty.check(s.clone(), value) {216 match ty.check(value) {217 Ok(()) => {217 Ok(()) => {218 return Ok(());218 return Ok(());219 }219 }228 Self::UnionRef(types) => {228 Self::UnionRef(types) => {229 let mut errors = Vec::new();229 let mut errors = Vec::new();230 for ty in types.iter() {230 for ty in types.iter() {231 match ty.check(s.clone(), value) {231 match ty.check(value) {232 Ok(()) => {232 Ok(()) => {233 return Ok(());233 return Ok(());234 }234 }242 }242 }243 Self::Sum(types) => {243 Self::Sum(types) => {244 for ty in types.iter() {244 for ty in types.iter() {245 ty.check(s.clone(), value)?;245 ty.check(value)?;246 }246 }247 Ok(())247 Ok(())248 }248 }249 Self::SumRef(types) => {249 Self::SumRef(types) => {250 for ty in types.iter() {250 for ty in types.iter() {251 ty.check(s.clone(), value)?;251 ty.check(value)?;252 }252 }253 Ok(())253 Ok(())254 }254 }crates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth13 },13 },14 throw,14 throw,15 typed::BoundedUsize,15 typed::BoundedUsize,16 ObjValue, Result, State, Unbound, WeakObjValue,16 ObjValue, Result, Unbound, WeakObjValue,17};17};181819pub trait ThunkValue: Trace {19pub trait ThunkValue: Trace {20 type Output;20 type Output;21 fn get(self: Box<Self>, s: State) -> Result<Self::Output>;21 fn get(self: Box<Self>) -> Result<Self::Output>;22}22}232324#[derive(Trace)]24#[derive(Trace)]43 pub fn evaluated(val: T) -> Self {43 pub fn evaluated(val: T) -> Self {44 Self(Cc::new(RefCell::new(ThunkInner::Computed(val))))44 Self(Cc::new(RefCell::new(ThunkInner::Computed(val))))45 }45 }46 pub fn force(&self, s: State) -> Result<()> {46 pub fn force(&self) -> Result<()> {47 self.evaluate(s)?;47 self.evaluate()?;48 Ok(())48 Ok(())49 }49 }50 pub fn evaluate(&self, s: State) -> Result<T> {50 pub fn evaluate(&self) -> Result<T> {51 match &*self.0.borrow() {51 match &*self.0.borrow() {52 ThunkInner::Computed(v) => return Ok(v.clone()),52 ThunkInner::Computed(v) => return Ok(v.clone()),53 ThunkInner::Errored(e) => return Err(e.clone()),53 ThunkInner::Errored(e) => return Err(e.clone()),61 } else {61 } else {62 unreachable!()62 unreachable!()63 };63 };64 let new_value = match value.0.get(s) {64 let new_value = match value.0.get() {65 Ok(v) => v,65 Ok(v) => v,66 Err(e) => {66 Err(e) => {67 *self.0.borrow_mut() = ThunkInner::Errored(e.clone());67 *self.0.borrow_mut() = ThunkInner::Errored(e.clone());94}94}95impl<I: Unbound<Bound = T>, T: Clone + Trace> Unbound for CachedUnbound<I, T> {95impl<I: Unbound<Bound = T>, T: Clone + Trace> Unbound for CachedUnbound<I, T> {96 type Bound = T;96 type Bound = T;97 fn bind(&self, s: State, sup: Option<ObjValue>, this: Option<ObjValue>) -> Result<T> {97 fn bind(&self, sup: Option<ObjValue>, this: Option<ObjValue>) -> Result<T> {98 let cache_key = (98 let cache_key = (99 sup.as_ref().map(|s| s.clone().downgrade()),99 sup.as_ref().map(|s| s.clone().downgrade()),100 this.as_ref().map(|t| t.clone().downgrade()),100 this.as_ref().map(|t| t.clone().downgrade()),104 return Ok(t.clone());104 return Ok(t.clone());105 }105 }106 }106 }107 let bound = self.value.bind(s, sup, this)?;107 let bound = self.value.bind(sup, this)?;108108109 {109 {110 let mut cache = self.cache.borrow_mut();110 let mut cache = self.cache.borrow_mut();126 }126 }127}127}128128129#[derive(Clone)]129#[derive(Clone, Trace)]130pub enum ManifestFormat {130pub enum ManifestFormat {131 YamlStream(Box<ManifestFormat>),131 YamlStream(Box<ManifestFormat>),132 Yaml {132 Yaml {268 /// Get array element by index, evaluating it, if it is lazy.268 /// Get array element by index, evaluating it, if it is lazy.269 ///269 ///270 /// Returns `None` on out-of-bounds condition.270 /// Returns `None` on out-of-bounds condition.271 pub fn get(&self, s: State, index: usize) -> Result<Option<Val>> {271 pub fn get(&self, index: usize) -> Result<Option<Val>> {272 match self {272 match self {273 Self::Bytes(i) => i273 Self::Bytes(i) => i274 .get(index)274 .get(index)275 .map_or(Ok(None), |v| Ok(Some(Val::Num(f64::from(*v))))),275 .map_or(Ok(None), |v| Ok(Some(Val::Num(f64::from(*v))))),276 Self::Lazy(vec) => {276 Self::Lazy(vec) => {277 if let Some(v) = vec.get(index) {277 if let Some(v) = vec.get(index) {278 Ok(Some(v.evaluate(s)?))278 Ok(Some(v.evaluate()?))279 } else {279 } else {280 Ok(None)280 Ok(None)281 }281 }284 Self::Extended(v) => {284 Self::Extended(v) => {285 let a_len = v.0.len();285 let a_len = v.0.len();286 if a_len > index {286 if a_len > index {287 v.0.get(s, index)287 v.0.get(index)288 } else {288 } else {289 v.1.get(s, index - a_len)289 v.1.get(index - a_len)290 }290 }291 }291 }292 Self::Range(a, _) => {292 Self::Range(a, _) => {300 if index >= len {300 if index >= len {301 return Ok(None);301 return Ok(None);302 }302 }303 v.get(s, len - index - 1)303 v.get(len - index - 1)304 }304 }305 Self::Slice(v) => {305 Self::Slice(v) => {306 let index = v.from() + index * v.step();306 let index = v.from() + index * v.step();307 if index >= v.to() {307 if index >= v.to() {308 return Ok(None);308 return Ok(None);309 }309 }310 v.inner.get(s, index)310 v.inner.get(index)311 }311 }312 }312 }313 }313 }356 }356 }357357358 /// Evaluate all array elements, returning new array.358 /// Evaluate all array elements, returning new array.359 pub fn evaluated(&self, s: State) -> Result<Cc<Vec<Val>>> {359 pub fn evaluated(&self) -> Result<Cc<Vec<Val>>> {360 Ok(match self {360 Ok(match self {361 Self::Bytes(i) => {361 Self::Bytes(i) => {362 let mut out = Vec::with_capacity(i.len());362 let mut out = Vec::with_capacity(i.len());368 Self::Lazy(vec) => {368 Self::Lazy(vec) => {369 let mut out = Vec::with_capacity(vec.len());369 let mut out = Vec::with_capacity(vec.len());370 for item in vec.iter() {370 for item in vec.iter() {371 out.push(item.evaluate(s.clone())?);371 out.push(item.evaluate()?);372 }372 }373 Cc::new(out)373 Cc::new(out)374 }374 }375 Self::Eager(vec) => vec.clone(),375 Self::Eager(vec) => vec.clone(),376 Self::Extended(_v) => {376 Self::Extended(_v) => {377 let mut out = Vec::with_capacity(self.len());377 let mut out = Vec::with_capacity(self.len());378 for item in self.iter(s) {378 for item in self.iter() {379 out.push(item?);379 out.push(item?);380 }380 }381 Cc::new(out)381 Cc::new(out)388 Cc::new(out)388 Cc::new(out)389 }389 }390 Self::Reversed(r) => {390 Self::Reversed(r) => {391 let mut r = r.evaluated(s)?;391 let mut r = r.evaluated()?;392 Cc::update_with(&mut r, |v| v.reverse());392 Cc::update_with(&mut r, |v| v.reverse());393 r393 r394 }394 }401 .take(v.to() - v.from())401 .take(v.to() - v.from())402 .step_by(v.step())402 .step_by(v.step())403 {403 {404 out.push(v.evaluate(s.clone())?);404 out.push(v.evaluate()?);405 }405 }406 Cc::new(out)406 Cc::new(out)407 }407 }408 })408 })409 }409 }410410411 /// Iterate over elements, evaluating them.411 /// Iterate over elements, evaluating them.412 pub fn iter(&self, s: State) -> impl DoubleEndedIterator<Item = Result<Val>> + '_ {412 pub fn iter(&self) -> impl DoubleEndedIterator<Item = Result<Val>> + '_ {413 (0..self.len()).map(move |idx| match self {413 (0..self.len()).map(move |idx| match self {414 Self::Bytes(b) => Ok(Val::Num(f64::from(b[idx]))),414 Self::Bytes(b) => Ok(Val::Num(f64::from(b[idx]))),415 Self::Lazy(l) => l[idx].evaluate(s.clone()),415 Self::Lazy(l) => l[idx].evaluate(),416 Self::Eager(e) => Ok(e[idx].clone()),416 Self::Eager(e) => Ok(e[idx].clone()),417 Self::Extended(..) | Self::Range(..) | Self::Reversed(..) | Self::Slice(..) => {417 Self::Extended(..) | Self::Range(..) | Self::Reversed(..) | Self::Slice(..) => {418 self.get(s.clone(), idx).map(|e| e.expect("idx < len"))418 self.get(idx).map(|e| e.expect("idx < len"))419 }419 }420 })420 })421 }421 }439 }439 }440440441 /// Return a new array, produced by passing every element of current array to specified callback function.441 /// Return a new array, produced by passing every element of current array to specified callback function.442 pub fn map(self, s: State, mapper: impl Fn(Val) -> Result<Val>) -> Result<Self> {442 pub fn map(self, mapper: impl Fn(Val) -> Result<Val>) -> Result<Self> {443 let mut out = Vec::with_capacity(self.len());443 let mut out = Vec::with_capacity(self.len());444444445 for value in self.iter(s) {445 for value in self.iter() {446 out.push(mapper(value?)?);446 out.push(mapper(value?)?);447 }447 }448448449 Ok(Self::Eager(Cc::new(out)))449 Ok(Self::Eager(Cc::new(out)))450 }450 }451451452 /// Return a new array, produced from current array by removing every value, for which specified callback function returns false.452 /// Return a new array, produced from current array by removing every value, for which specified callback function returns false.453 pub fn filter(self, s: State, filter: impl Fn(&Val) -> Result<bool>) -> Result<Self> {453 pub fn filter(self, filter: impl Fn(&Val) -> Result<bool>) -> Result<Self> {454 let mut out = Vec::with_capacity(self.len());454 let mut out = Vec::with_capacity(self.len());455455456 for value in self.iter(s) {456 for value in self.iter() {457 let value = value?;457 let value = value?;458 if filter(&value)? {458 if filter(&value)? {459 out.push(value);459 out.push(value);645 }645 }646 }646 }647647648 pub fn to_string(&self, s: State) -> Result<IStr> {648 pub fn to_string(&self) -> Result<IStr> {649 Ok(match self {649 Ok(match self {650 Self::Bool(true) => "true".into(),650 Self::Bool(true) => "true".into(),651 Self::Bool(false) => "false".into(),651 Self::Bool(false) => "false".into(),652 Self::Null => "null".into(),652 Self::Null => "null".into(),653 Self::Str(s) => s.clone(),653 Self::Str(s) => s.clone(),654 v => manifest_json_ex(654 v => manifest_json_ex(655 s,656 v,655 v,657 &ManifestJsonOptions {656 &ManifestJsonOptions {658 padding: "",657 padding: "",668 }667 }669668670 /// Expects value to be object, outputs (key, manifested value) pairs669 /// Expects value to be object, outputs (key, manifested value) pairs671 pub fn manifest_multi(&self, s: State, ty: &ManifestFormat) -> Result<Vec<(IStr, IStr)>> {670 pub fn manifest_multi(&self, ty: &ManifestFormat) -> Result<Vec<(IStr, IStr)>> {672 let obj = match self {671 let obj = match self {673 Self::Obj(obj) => obj,672 Self::Obj(obj) => obj,674 _ => throw!(MultiManifestOutputIsNotAObject),673 _ => throw!(MultiManifestOutputIsNotAObject),680 let mut out = Vec::with_capacity(keys.len());679 let mut out = Vec::with_capacity(keys.len());681 for key in keys {680 for key in keys {682 let value = obj681 let value = obj683 .get(s.clone(), key.clone())?682 .get(key.clone())?684 .expect("item in object")683 .expect("item in object")685 .manifest(s.clone(), ty)?;684 .manifest(ty)?;686 out.push((key, value));685 out.push((key, value));687 }686 }688 Ok(out)687 Ok(out)689 }688 }690689691 /// Expects value to be array, outputs manifested values690 /// Expects value to be array, outputs manifested values692 pub fn manifest_stream(&self, s: State, ty: &ManifestFormat) -> Result<Vec<IStr>> {691 pub fn manifest_stream(&self, ty: &ManifestFormat) -> Result<Vec<IStr>> {693 let arr = match self {692 let arr = match self {694 Self::Arr(a) => a,693 Self::Arr(a) => a,695 _ => throw!(StreamManifestOutputIsNotAArray),694 _ => throw!(StreamManifestOutputIsNotAArray),696 };695 };697 let mut out = Vec::with_capacity(arr.len());696 let mut out = Vec::with_capacity(arr.len());698 for i in arr.iter(s.clone()) {697 for i in arr.iter() {699 out.push(i?.manifest(s.clone(), ty)?);698 out.push(i?.manifest(ty)?);700 }699 }701 Ok(out)700 Ok(out)702 }701 }703702704 pub fn manifest(&self, s: State, ty: &ManifestFormat) -> Result<IStr> {703 pub fn manifest(&self, ty: &ManifestFormat) -> Result<IStr> {705 Ok(match ty {704 Ok(match ty {706 ManifestFormat::YamlStream(format) => {705 ManifestFormat::YamlStream(format) => {707 let arr = match self {706 let arr = match self {717 };716 };718717719 if !arr.is_empty() {718 if !arr.is_empty() {720 for v in arr.iter(s.clone()) {719 for v in arr.iter() {721 out.push_str("---\n");720 out.push_str("---\n");722 out.push_str(&v?.manifest(s.clone(), format)?);721 out.push_str(&v?.manifest(format)?);723 out.push('\n');722 out.push('\n');724 }723 }725 out.push_str("...");724 out.push_str("...");732 #[cfg(feature = "exp-preserve-order")]731 #[cfg(feature = "exp-preserve-order")]733 preserve_order,732 preserve_order,734 } => self.to_yaml(733 } => self.to_yaml(735 s,736 *padding,734 *padding,737 #[cfg(feature = "exp-preserve-order")]735 #[cfg(feature = "exp-preserve-order")]738 *preserve_order,736 *preserve_order,742 #[cfg(feature = "exp-preserve-order")]740 #[cfg(feature = "exp-preserve-order")]743 preserve_order,741 preserve_order,744 } => self.to_json(742 } => self.to_json(745 s,746 *padding,743 *padding,747 #[cfg(feature = "exp-preserve-order")]744 #[cfg(feature = "exp-preserve-order")]748 *preserve_order,745 *preserve_order,749 )?,746 )?,750 ManifestFormat::ToString => self.to_string(s)?,747 ManifestFormat::ToString => self.to_string()?,751 ManifestFormat::String => match self {748 ManifestFormat::String => match self {752 Self::Str(s) => s.clone(),749 Self::Str(s) => s.clone(),753 _ => throw!(StringManifestOutputIsNotAString),750 _ => throw!(StringManifestOutputIsNotAString),758 /// For manifestification755 /// For manifestification759 pub fn to_json(756 pub fn to_json(760 &self,757 &self,761 s: State,762 padding: usize,758 padding: usize,763 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,759 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,764 ) -> Result<IStr> {760 ) -> Result<IStr> {765 manifest_json_ex(761 manifest_json_ex(766 s,767 self,762 self,768 &ManifestJsonOptions {763 &ManifestJsonOptions {769 padding: &" ".repeat(padding),764 padding: &" ".repeat(padding),784 /// Calls `std.manifestJson`779 /// Calls `std.manifestJson`785 pub fn to_std_json(780 pub fn to_std_json(786 &self,781 &self,787 s: State,788 padding: usize,782 padding: usize,789 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,783 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,790 ) -> Result<Rc<str>> {784 ) -> Result<Rc<str>> {791 manifest_json_ex(785 manifest_json_ex(792 s,793 self,786 self,794 &ManifestJsonOptions {787 &ManifestJsonOptions {795 padding: &" ".repeat(padding),788 padding: &" ".repeat(padding),805798806 pub fn to_yaml(799 pub fn to_yaml(807 &self,800 &self,808 s: State,809 padding: usize,801 padding: usize,810 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,802 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,811 ) -> Result<IStr> {803 ) -> Result<IStr> {812 let padding = &" ".repeat(padding);804 let padding = &" ".repeat(padding);813 manifest_yaml_ex(805 manifest_yaml_ex(814 s,815 self,806 self,816 &ManifestYamlOptions {807 &ManifestYamlOptions {817 padding,808 padding,857}848}858849859/// Native implementation of `std.equals`850/// Native implementation of `std.equals`860pub fn equals(s: State, val_a: &Val, val_b: &Val) -> Result<bool> {851pub fn equals(val_a: &Val, val_b: &Val) -> Result<bool> {861 if val_a.value_type() != val_b.value_type() {852 if val_a.value_type() != val_b.value_type() {862 return Ok(false);853 return Ok(false);863 }854 }869 if a.len() != b.len() {860 if a.len() != b.len() {870 return Ok(false);861 return Ok(false);871 }862 }872 for (a, b) in a.iter(s.clone()).zip(b.iter(s.clone())) {863 for (a, b) in a.iter().zip(b.iter()) {873 if !equals(s.clone(), &a?, &b?)? {864 if !equals(&a?, &b?)? {874 return Ok(false);865 return Ok(false);875 }866 }876 }867 }893 }884 }894 for field in fields {885 for field in fields {895 if !equals(886 if !equals(896 s.clone(),897 &a.get(s.clone(), field.clone())?.expect("field exists"),887 &a.get(field.clone())?.expect("field exists"),898 &b.get(s.clone(), field)?.expect("field exists"),888 &b.get(field)?.expect("field exists"),899 )? {889 )? {900 return Ok(false);890 return Ok(false);901 }891 }crates/jrsonnet-interner/src/lib.rsdiffbeforeafterboth4 clippy::undocumented_unsafe_blocks4 clippy::undocumented_unsafe_blocks5)]5)]6#![warn(clippy::pedantic, clippy::nursery)]6#![warn(clippy::pedantic, clippy::nursery)]7#![allow(clippy::missing_const_for_fn)]7use std::{8use std::{8 borrow::Cow,9 borrow::Cow,9 cell::RefCell,10 cell::RefCell,crates/jrsonnet-macros/src/lib.rsdiffbeforeafterboth130 is_option: bool,130 is_option: bool,131 name: Option<String>,131 name: Option<String>,132 },132 },133 State,133 Context,134 Location,134 Location,135 This,135 This,136}136}146 _ => None,146 _ => None,147 };147 };148 let ty = &arg.ty;148 let ty = &arg.ty;149 if type_is_path(ty, "State").is_some() {149 if type_is_path(ty, "Context").is_some() {150 return Ok(Self::State);150 return Ok(Self::Context);151 } else if type_is_path(ty, "CallLocation").is_some() {151 } else if type_is_path(ty, "CallLocation").is_some() {152 return Ok(Self::Location);152 return Ok(Self::Location);153 } else if type_is_path(ty, "Thunk").is_some() {153 } else if type_is_path(ty, "Thunk").is_some() {273 },273 },274 })274 })275 }275 }276 ArgInfo::State => None,276 ArgInfo::Context => None,277 ArgInfo::Location => None,277 ArgInfo::Location => None,278 ArgInfo::This => None,278 ArgInfo::This => None,279 });279 });287 id += 1;287 id += 1;288 (quote! {#cid}, a)288 (quote! {#cid}, a)289 }289 }290 ArgInfo::State | ArgInfo::Location | ArgInfo::This => {290 ArgInfo::Context | ArgInfo::Location | ArgInfo::This => {291 (quote! {compile_error!("should not use id")}, a)291 (quote! {compile_error!("should not use id")}, a)292 }292 }293 })293 })301 let name = name.as_ref().map(|v| v.as_str()).unwrap_or("<unnamed>");301 let name = name.as_ref().map(|v| v.as_str()).unwrap_or("<unnamed>");302 let eval = quote! {jrsonnet_evaluator::State::push_description(302 let eval = quote! {jrsonnet_evaluator::State::push_description(303 || format!("argument <{}> evaluation", #name),303 || format!("argument <{}> evaluation", #name),304 || <#ty>::from_untyped(value.evaluate(s.clone())?, s.clone()),304 || <#ty>::from_untyped(value.evaluate()?),305 )?};305 )?};306 let value = if *is_option {306 let value = if *is_option {307 quote! {if let Some(value) = &parsed[#id] {307 quote! {if let Some(value) = &parsed[#id] {333 }333 }334 }334 }335 }335 }336 ArgInfo::State => quote! {s.clone(),},336 ArgInfo::Context => quote! {ctx.clone(),},337 ArgInfo::Location => quote! {location,},337 ArgInfo::Location => quote! {location,},338 ArgInfo::This => quote! {self,},338 ArgInfo::This => quote! {self,},339 });339 });394 fn params(&self) -> &[BuiltinParam] {394 fn params(&self) -> &[BuiltinParam] {395 PARAMS395 PARAMS396 }396 }397 fn call(&self, s: State, ctx: Context, location: CallLocation, args: &dyn ArgsLike) -> Result<Val> {397 fn call(&self, ctx: Context, location: CallLocation, args: &dyn ArgsLike) -> Result<Val> {398 let parsed = parse_builtin_call(s.clone(), ctx, &PARAMS, args, false)?;398 let parsed = parse_builtin_call(ctx.clone(), &PARAMS, args, false)?;399399400 let result: #result = #name(#(#pass)*);400 let result: #result = #name(#(#pass)*);401 let result = result?;401 let result = result?;402 <#result_inner>::into_untyped(result, s)402 <#result_inner>::into_untyped(result)403 }403 }404 }404 }405 };405 };535 // optional flatten is handled in same way as serde535 // optional flatten is handled in same way as serde536 return if self.is_option {536 return if self.is_option {537 quote! {537 quote! {538 #ident: <#ty>::parse(&obj, s.clone()).ok(),538 #ident: <#ty>::parse(&obj).ok(),539 }539 }540 } else {540 } else {541 quote! {541 quote! {542 #ident: <#ty>::parse(&obj, s.clone())?,542 #ident: <#ty>::parse(&obj)?,543 }543 }544 };544 };545 };545 };546546547 let name = self.name().unwrap();547 let name = self.name().unwrap();548 let value = if self.is_option {548 let value = if self.is_option {549 quote! {549 quote! {550 if let Some(value) = obj.get(s.clone(), #name.into())? {550 if let Some(value) = obj.get(#name.into())? {551 Some(<#ty>::from_untyped(value, s.clone())?)551 Some(<#ty>::from_untyped(value)?)552 } else {552 } else {553 None553 None554 }554 }555 }555 }556 } else {556 } else {557 quote! {557 quote! {558 <#ty>::from_untyped(obj.get(s.clone(), #name.into())?.ok_or_else(|| Error::NoSuchField(#name.into(), vec![]))?, s.clone())?558 <#ty>::from_untyped(obj.get(#name.into())?.ok_or_else(|| Error::NoSuchField(#name.into(), vec![]))?)?559 }559 }560 };560 };561561570 if self.is_option {570 if self.is_option {571 quote! {571 quote! {572 if let Some(value) = self.#ident {572 if let Some(value) = self.#ident {573 out.member(#name.into()).value(s.clone(), <#ty>::into_untyped(value, s.clone())?)?;573 out.member(#name.into()).value(<#ty>::into_untyped(value)?)?;574 }574 }575 }575 }576 } else {576 } else {577 quote! {577 quote! {578 out.member(#name.into()).value(s.clone(), <#ty>::into_untyped(self.#ident, s.clone())?)?;578 out.member(#name.into()).value(<#ty>::into_untyped(self.#ident)?)?;579 }579 }580 }580 }581 } else if self.is_option {581 } else if self.is_option {582 quote! {582 quote! {583 if let Some(value) = self.#ident {583 if let Some(value) = self.#ident {584 value.serialize(s.clone(), out)?;584 value.serialize(out)?;585 }585 }586 }586 }587 } else {587 } else {588 quote! {588 quote! {589 self.#ident.serialize(s.clone(), out)?;589 self.#ident.serialize(out)?;590 }590 }591 })591 })592 }592 }628 impl Typed for #ident {628 impl Typed for #ident {629 const TYPE: &'static ComplexValType = &ComplexValType::ObjectRef(&ITEMS);629 const TYPE: &'static ComplexValType = &ComplexValType::ObjectRef(&ITEMS);630630631 fn from_untyped(value: Val, s: State) -> Result<Self> {631 fn from_untyped(value: Val) -> Result<Self> {632 let obj = value.as_obj().expect("shape is correct");632 let obj = value.as_obj().expect("shape is correct");633 Self::parse(&obj, s)633 Self::parse(&obj)634 }634 }635635636 fn into_untyped(value: Self, s: State) -> Result<Val> {636 fn into_untyped(value: Self) -> Result<Val> {637 let mut out = ObjValueBuilder::new();637 let mut out = ObjValueBuilder::new();638 value.serialize(s, &mut out)?;638 value.serialize(&mut out)?;639 Ok(Val::Obj(out.build()))639 Ok(Val::Obj(out.build()))640 }640 }641641661 #typed661 #typed662662663 impl TypedObj for #ident {663 impl TypedObj for #ident {664 fn serialize(self, s: State, out: &mut ObjValueBuilder) -> Result<(), LocError> {664 fn serialize(self, out: &mut ObjValueBuilder) -> Result<(), LocError> {665 #(#fields_serialize)*665 #(#fields_serialize)*666666667 Ok(())667 Ok(())668 }668 }669 fn parse(obj: &ObjValue, s: State) -> Result<Self, LocError> {669 fn parse(obj: &ObjValue) -> Result<Self, LocError> {670 Ok(Self {670 Ok(Self {671 #(#fields_parse)*671 #(#fields_parse)*672 })672 })crates/jrsonnet-parser/src/location.rsdiffbeforeafterboth1#[allow(clippy::module_name_repetitions)]1#[allow(clippy::module_name_repetitions)]2#[derive(Clone, PartialEq, Eq, Debug)]2#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]3pub struct CodeLocation {3pub struct CodeLocation {4 pub offset: usize,4 pub offset: usize,5524}24}252526#[allow(clippy::module_name_repetitions)]26#[allow(clippy::module_name_repetitions)]27pub fn offset_to_location(file: &str, offsets: &[u32]) -> Vec<CodeLocation> {27pub fn offset_to_location<const S: usize>(file: &str, offsets: &[u32; S]) -> [CodeLocation; S] {28 if offsets.is_empty() {28 if offsets.is_empty() {29 return vec![];29 return [CodeLocation::default(); S];30 }30 }31 let mut line = 1;31 let mut line = 1;32 let mut column = 1;32 let mut column = 1;40 offset_map.sort_by_key(|v| v.0);40 offset_map.sort_by_key(|v| v.0);41 offset_map.reverse();41 offset_map.reverse();424243 let mut out = vec![43 let mut out = [CodeLocation::default(); S];44 CodeLocation {45 offset: 0,46 column: 0,47 line: 0,48 line_start_offset: 0,49 line_end_offset: 050 };51 offsets.len()52 ];53 let mut with_no_known_line_ending = vec![];44 let mut with_no_known_line_ending = vec![];54 let mut this_line_offset = 0;45 let mut this_line_offset = 0;55 for (pos, ch) in file46 for (pos, ch) in file103 "hello world\n_______________________________________________________",94 "hello world\n_______________________________________________________",104 &[0, 14]95 &[0, 14]105 ),96 ),106 vec![97 [107 CodeLocation {98 CodeLocation {108 offset: 0,99 offset: 0,109 line: 1,100 line: 1,crates/jrsonnet-parser/src/source.rsdiffbeforeafterboth289 &self.0 .0289 &self.0 .0290 }290 }291291292 pub fn map_source_locations(&self, locs: &[u32]) -> Vec<CodeLocation> {292 pub fn map_source_locations<const S: usize>(&self, locs: &[u32; S]) -> [CodeLocation; S] {293 offset_to_location(&self.0 .1, locs)293 offset_to_location(&self.0 .1, locs)294 }294 }295 pub fn map_from_source_location(&self, line: usize, column: usize) -> Option<usize> {295 pub fn map_from_source_location(&self, line: usize, column: usize) -> Option<usize> {crates/jrsonnet-stdlib/src/arrays.rsdiffbeforeafterboth2 error::Result,2 error::Result,3 function::{builtin, FuncVal},3 function::{builtin, FuncVal},4 throw,4 throw,5 typed::{Any, BoundedUsize, Typed, VecVal},5 typed::{Any, BoundedUsize, Either2, NativeFn, Typed, VecVal},6 val::{equals, ArrValue, IndexableVal},6 val::{equals, ArrValue, IndexableVal},7 IStr, State, Val,7 Either, IStr, Val,8};8};9use jrsonnet_gcmodule::Cc;9use jrsonnet_gcmodule::Cc;101011#[builtin]11#[builtin]12pub fn builtin_make_array(s: State, sz: usize, func: FuncVal) -> Result<VecVal> {12pub fn builtin_make_array(sz: usize, func: NativeFn<((f64,), Any)>) -> Result<VecVal> {13 let mut out = Vec::with_capacity(sz);13 let mut out = Vec::with_capacity(sz);14 for i in 0..sz {14 for i in 0..sz {15 out.push(func.evaluate_simple(s.clone(), &(i as f64,))?);15 out.push(func(i as f64)?.0);16 }16 }17 Ok(VecVal(Cc::new(out)))17 Ok(VecVal(Cc::new(out)))18}18}28}28}292930#[builtin]30#[builtin]31pub fn builtin_map(s: State, func: FuncVal, arr: ArrValue) -> Result<ArrValue> {31pub fn builtin_map(func: NativeFn<((Any,), Any)>, arr: ArrValue) -> Result<ArrValue> {32 arr.map(s.clone(), |val| {32 arr.map(|val| Ok(func(Any(val))?.0))33 func.evaluate_simple(s.clone(), &(Any(val),))34 })35}33}363437#[builtin]35#[builtin]38pub fn builtin_flatmap(s: State, func: FuncVal, arr: IndexableVal) -> Result<IndexableVal> {36pub fn builtin_flatmap(37 func: NativeFn<((Either![String, Any],), Any)>,38 arr: IndexableVal,39) -> Result<IndexableVal> {39 match arr {40 match arr {40 IndexableVal::Str(str) => {41 IndexableVal::Str(str) => {41 let mut out = String::new();42 let mut out = String::new();42 for c in str.chars() {43 for c in str.chars() {43 match func.evaluate_simple(s.clone(), &(c.to_string(),))? {44 match func(Either2::A(c.to_string()))?.0 {44 Val::Str(o) => out.push_str(&o),45 Val::Str(o) => out.push_str(&o),45 Val::Null => continue,46 Val::Null => continue,46 _ => throw!("in std.join all items should be strings"),47 _ => throw!("in std.join all items should be strings"),50 }51 }51 IndexableVal::Arr(a) => {52 IndexableVal::Arr(a) => {52 let mut out = Vec::new();53 let mut out = Vec::new();53 for el in a.iter(s.clone()) {54 for el in a.iter() {54 let el = el?;55 let el = el?;55 match func.evaluate_simple(s.clone(), &(Any(el),))? {56 match func(Either2::B(Any(el)))?.0 {56 Val::Arr(o) => {57 Val::Arr(o) => {57 for oe in o.iter(s.clone()) {58 for oe in o.iter() {58 out.push(oe?);59 out.push(oe?);59 }60 }60 }61 }68}69}697070#[builtin]71#[builtin]71pub fn builtin_filter(s: State, func: FuncVal, arr: ArrValue) -> Result<ArrValue> {72pub fn builtin_filter(func: FuncVal, arr: ArrValue) -> Result<ArrValue> {72 arr.filter(s.clone(), |val| {73 arr.filter(|val| bool::from_untyped(func.evaluate_simple(&(Any(val.clone()),))?))73 bool::from_untyped(74 func.evaluate_simple(s.clone(), &(Any(val.clone()),))?,75 s.clone(),76 )77 })78}74}797580#[builtin]76#[builtin]81pub fn builtin_foldl(s: State, func: FuncVal, arr: ArrValue, init: Any) -> Result<Any> {77pub fn builtin_foldl(func: FuncVal, arr: ArrValue, init: Any) -> Result<Any> {82 let mut acc = init.0;78 let mut acc = init.0;83 for i in arr.iter(s.clone()) {79 for i in arr.iter() {84 acc = func.evaluate_simple(s.clone(), &(Any(acc), Any(i?)))?;80 acc = func.evaluate_simple(&(Any(acc), Any(i?)))?;85 }81 }86 Ok(Any(acc))82 Ok(Any(acc))87}83}888489#[builtin]85#[builtin]90pub fn builtin_foldr(s: State, func: FuncVal, arr: ArrValue, init: Any) -> Result<Any> {86pub fn builtin_foldr(func: FuncVal, arr: ArrValue, init: Any) -> Result<Any> {91 let mut acc = init.0;87 let mut acc = init.0;92 for i in arr.iter(s.clone()).rev() {88 for i in arr.iter().rev() {93 acc = func.evaluate_simple(s.clone(), &(Any(i?), Any(acc)))?;89 acc = func.evaluate_simple(&(Any(i?), Any(acc)))?;94 }90 }95 Ok(Any(acc))91 Ok(Any(acc))96}92}104}100}105101106#[builtin]102#[builtin]107pub fn builtin_join(s: State, sep: IndexableVal, arr: ArrValue) -> Result<IndexableVal> {103pub fn builtin_join(sep: IndexableVal, arr: ArrValue) -> Result<IndexableVal> {108 Ok(match sep {104 Ok(match sep {109 IndexableVal::Arr(joiner_items) => {105 IndexableVal::Arr(joiner_items) => {110 let mut out = Vec::new();106 let mut out = Vec::new();111107112 let mut first = true;108 let mut first = true;113 for item in arr.iter(s.clone()) {109 for item in arr.iter() {114 let item = item?.clone();110 let item = item?.clone();115 if let Val::Arr(items) = item {111 if let Val::Arr(items) = item {116 if !first {112 if !first {117 out.reserve(joiner_items.len());113 out.reserve(joiner_items.len());118 // TODO: extend114 // TODO: extend119 for item in joiner_items.iter(s.clone()) {115 for item in joiner_items.iter() {120 out.push(item?);116 out.push(item?);121 }117 }122 }118 }123 first = false;119 first = false;124 out.reserve(items.len());120 out.reserve(items.len());125 for item in items.iter(s.clone()) {121 for item in items.iter() {126 out.push(item?);122 out.push(item?);127 }123 }128 } else if matches!(item, Val::Null) {124 } else if matches!(item, Val::Null) {138 let mut out = String::new();134 let mut out = String::new();139135140 let mut first = true;136 let mut first = true;141 for item in arr.iter(s) {137 for item in arr.iter() {142 let item = item?.clone();138 let item = item?.clone();143 if let Val::Str(item) = item {139 if let Val::Str(item) = item {144 if !first {140 if !first {164}160}165161166#[builtin]162#[builtin]167pub fn builtin_any(s: State, arr: ArrValue) -> Result<bool> {163pub fn builtin_any(arr: ArrValue) -> Result<bool> {168 for v in arr.iter(s.clone()) {164 for v in arr.iter() {169 let v = bool::from_untyped(v?, s.clone())?;165 let v = bool::from_untyped(v?)?;170 if v {166 if v {171 return Ok(true);167 return Ok(true);172 }168 }175}171}176172177#[builtin]173#[builtin]178pub fn builtin_all(s: State, arr: ArrValue) -> Result<bool> {174pub fn builtin_all(arr: ArrValue) -> Result<bool> {179 for v in arr.iter(s.clone()) {175 for v in arr.iter() {180 let v = bool::from_untyped(v?, s.clone())?;176 let v = bool::from_untyped(v?)?;181 if !v {177 if !v {182 return Ok(false);178 return Ok(false);183 }179 }186}182}187183188#[builtin]184#[builtin]189pub fn builtin_member(s: State, arr: IndexableVal, x: Any) -> Result<bool> {185pub fn builtin_member(arr: IndexableVal, x: Any) -> Result<bool> {190 match arr {186 match arr {191 IndexableVal::Str(str) => {187 IndexableVal::Str(str) => {192 let x: IStr = IStr::from_untyped(x.0, s)?;188 let x: IStr = IStr::from_untyped(x.0)?;193 Ok(!x.is_empty() && str.contains(&*x))189 Ok(!x.is_empty() && str.contains(&*x))194 }190 }195 IndexableVal::Arr(a) => {191 IndexableVal::Arr(a) => {196 for item in a.iter(s.clone()) {192 for item in a.iter() {197 let item = item?;193 let item = item?;198 if equals(s.clone(), &item, &x.0)? {194 if equals(&item, &x.0)? {199 return Ok(true);195 return Ok(true);200 }196 }201 }197 }205}201}206202207#[builtin]203#[builtin]208pub fn builtin_count(s: State, arr: Vec<Any>, v: Any) -> Result<usize> {204pub fn builtin_count(arr: Vec<Any>, v: Any) -> Result<usize> {209 let mut count = 0;205 let mut count = 0;210 for item in &arr {206 for item in &arr {211 if equals(s.clone(), &item.0, &v.0)? {207 if equals(&item.0, &v.0)? {212 count += 1;208 count += 1;213 }209 }214 }210 }crates/jrsonnet-stdlib/src/lib.rsdiffbeforeafterboth12 trace::PathResolver,12 trace::PathResolver,13 Context, ContextBuilder, IStr, ObjValue, ObjValueBuilder, State, Thunk, Val,13 Context, ContextBuilder, IStr, ObjValue, ObjValueBuilder, State, Thunk, Val,14};14};15use jrsonnet_gcmodule::Cc;15use jrsonnet_gcmodule::{Cc, Trace};16use jrsonnet_parser::Source;16use jrsonnet_parser::Source;171718mod expr;18mod expr;41mod misc;41mod misc;42pub use misc::*;42pub use misc::*;434344pub fn stdlib_uncached(s: State, settings: Rc<RefCell<Settings>>) -> ObjValue {44pub fn stdlib_uncached(settings: Rc<RefCell<Settings>>) -> ObjValue {45 let mut builder = ObjValueBuilder::new();45 let mut builder = ObjValueBuilder::new();464647 let expr = expr::stdlib_expr();47 let expr = expr::stdlib_expr();48 let eval = jrsonnet_evaluator::evaluate(s.clone(), Context::default(), &expr)48 let eval = jrsonnet_evaluator::evaluate(ContextBuilder::dangerous_empty_state().build(), &expr)49 .expect("stdlib.jsonnet should have no errors")49 .expect("stdlib.jsonnet should have no errors")50 .as_obj()50 .as_obj()51 .expect("stdlib.jsonnet should evaluate to object");51 .expect("stdlib.jsonnet should evaluate to object");173}173}174174175pub trait TracePrinter {175pub trait TracePrinter {176 fn print_trace(&self, s: State, loc: CallLocation, value: IStr);176 fn print_trace(&self, loc: CallLocation, value: IStr);177}177}178178179pub struct StdTracePrinter {179pub struct StdTracePrinter {185 }185 }186}186}187impl TracePrinter for StdTracePrinter {187impl TracePrinter for StdTracePrinter {188 fn print_trace(&self, _s: State, loc: CallLocation, value: IStr) {188 fn print_trace(&self, loc: CallLocation, value: IStr) {189 eprint!("TRACE:");189 eprint!("TRACE:");190 if let Some(loc) = loc.0 {190 if let Some(loc) = loc.0 {191 let locs = loc.0.map_source_locations(&[loc.1]);191 let locs = loc.0.map_source_locations(&[loc.1]);220 Source::new_virtual(source_name.into(), code.into())220 Source::new_virtual(source_name.into(), code.into())221}221}222222223#[derive(Trace)]223pub struct ContextInitializer {224pub struct ContextInitializer {224 // When we don't need to support legacy-this-file, we can reuse same context for all files225 // When we don't need to support legacy-this-file, we can reuse same context for all files225 #[cfg(not(feature = "legacy-this-file"))]226 #[cfg(not(feature = "legacy-this-file"))]242 Self {243 Self {243 #[cfg(not(feature = "legacy-this-file"))]244 #[cfg(not(feature = "legacy-this-file"))]244 context: {245 context: {245 let mut context = ContextBuilder::with_capacity(1);246 let mut context = ContextBuilder::with_capacity(s, 1);246 context.bind(247 context.bind(247 "std".into(),248 "std".into(),248 Thunk::evaluated(Val::Obj(stdlib_uncached(s, settings.clone()))),249 Thunk::evaluated(Val::Obj(stdlib_uncached(settings.clone()))),249 );250 );250 context.build()251 context.build()251 },252 },349impl StateExt for State {350impl StateExt for State {350 fn with_stdlib(&self) {351 fn with_stdlib(&self) {351 let initializer = ContextInitializer::new(self.clone(), PathResolver::new_cwd_fallback());352 let initializer = ContextInitializer::new(self.clone(), PathResolver::new_cwd_fallback());352 self.settings_mut().context_initializer = Box::new(initializer)353 self.settings_mut().context_initializer = tb!(initializer)353 }354 }354 fn add_global(&self, name: IStr, value: Thunk<Val>) {355 fn add_global(&self, name: IStr, value: Thunk<Val>) {355 self.settings()356 self.settings()crates/jrsonnet-stdlib/src/manifest.rsdiffbeforeafterboth6 ManifestYamlOptions,6 ManifestYamlOptions,7 },7 },8 typed::Any,8 typed::Any,9 IStr, State,9 IStr,10};10};111112#[builtin]12#[builtin]161617#[builtin]17#[builtin]18pub fn builtin_manifest_json_ex(18pub fn builtin_manifest_json_ex(19 s: State,20 value: Any,19 value: Any,21 indent: IStr,20 indent: IStr,22 newline: Option<IStr>,21 newline: Option<IStr>,26 let newline = newline.as_deref().unwrap_or("\n");25 let newline = newline.as_deref().unwrap_or("\n");27 let key_val_sep = key_val_sep.as_deref().unwrap_or(": ");26 let key_val_sep = key_val_sep.as_deref().unwrap_or(": ");28 manifest_json_ex(27 manifest_json_ex(29 s,30 &value.0,28 &value.0,31 &ManifestJsonOptions {29 &ManifestJsonOptions {32 padding: &indent,30 padding: &indent,413942#[builtin]40#[builtin]43pub fn builtin_manifest_yaml_doc(41pub fn builtin_manifest_yaml_doc(44 s: State,45 value: Any,42 value: Any,46 indent_array_in_object: Option<bool>,43 indent_array_in_object: Option<bool>,47 quote_keys: Option<bool>,44 quote_keys: Option<bool>,48 #[cfg(feature = "exp-preserve-order")] preserve_order: Option<bool>,45 #[cfg(feature = "exp-preserve-order")] preserve_order: Option<bool>,49) -> Result<String> {46) -> Result<String> {50 manifest_yaml_ex(47 manifest_yaml_ex(51 s,52 &value.0,48 &value.0,53 &ManifestYamlOptions {49 &ManifestYamlOptions {54 padding: " ",50 padding: " ",crates/jrsonnet-stdlib/src/misc.rsdiffbeforeafterboth6 throw,6 throw,7 typed::{Any, Either2, Either4},7 typed::{Any, Either2, Either4},8 val::{equals, ArrValue},8 val::{equals, ArrValue},9 Either, IStr, ObjValue, State, Thunk, Val,9 Context, Either, IStr, ObjValue, Thunk, Val,10};10};111112use crate::{extvar_source, Settings};12use crate::{extvar_source, Settings};25#[builtin(fields(25#[builtin(fields(26 settings: Rc<RefCell<Settings>>,26 settings: Rc<RefCell<Settings>>,27))]27))]28pub fn builtin_ext_var(this: &builtin_ext_var, s: State, x: IStr) -> Result<Any> {28pub fn builtin_ext_var(this: &builtin_ext_var, ctx: Context, x: IStr) -> Result<Any> {29 let ctx = s.create_default_context(extvar_source(&x, ""));29 let ctx = ctx.state().create_default_context(extvar_source(&x, ""));30 Ok(Any(this30 Ok(Any(this31 .settings31 .settings32 .borrow()32 .borrow()33 .ext_vars33 .ext_vars34 .get(&x)34 .get(&x)35 .cloned()35 .cloned()36 .ok_or_else(|| UndefinedExternalVariable(x))?36 .ok_or_else(|| UndefinedExternalVariable(x))?37 .evaluate_arg(s.clone(), ctx, true)?37 .evaluate_arg(ctx, true)?38 .evaluate(s)?))38 .evaluate()?))39}39}404041#[builtin(fields(41#[builtin(fields(58))]58))]59pub fn builtin_trace(59pub fn builtin_trace(60 this: &builtin_trace,60 this: &builtin_trace,61 s: State,62 loc: CallLocation,61 loc: CallLocation,63 str: IStr,62 str: IStr,64 rest: Thunk<Val>,63 rest: Thunk<Val>,65) -> Result<Any> {64) -> Result<Any> {66 this.settings65 this.settings.borrow().trace_printer.print_trace(loc, str);67 .borrow()68 .trace_printer69 .print_trace(s.clone(), loc, str);70 Ok(Any(rest.evaluate(s)?))66 Ok(Any(rest.evaluate()?))71}67}726873#[allow(clippy::comparison_chain)]69#[allow(clippy::comparison_chain)]74#[builtin]70#[builtin]75pub fn builtin_starts_with(71pub fn builtin_starts_with(a: Either![IStr, ArrValue], b: Either![IStr, ArrValue]) -> Result<bool> {76 s: State,77 a: Either![IStr, ArrValue],78 b: Either![IStr, ArrValue],79) -> Result<bool> {80 Ok(match (a, b) {72 Ok(match (a, b) {81 (Either2::A(a), Either2::A(b)) => a.starts_with(b.as_str()),73 (Either2::A(a), Either2::A(b)) => a.starts_with(b.as_str()),82 (Either2::B(a), Either2::B(b)) => {74 (Either2::B(a), Either2::B(b)) => {83 if b.len() > a.len() {75 if b.len() > a.len() {84 return Ok(false);76 return Ok(false);85 } else if b.len() == a.len() {77 } else if b.len() == a.len() {86 return equals(s, &Val::Arr(a), &Val::Arr(b));78 return equals(&Val::Arr(a), &Val::Arr(b));87 } else {79 } else {88 for (a, b) in a80 for (a, b) in a.slice(None, Some(b.len()), None).iter().zip(b.iter()) {89 .slice(None, Some(b.len()), None)90 .iter(s.clone())91 .zip(b.iter(s.clone()))92 {93 let a = a?;81 let a = a?;94 let b = b?;82 let b = b?;95 if !equals(s.clone(), &a, &b)? {83 if !equals(&a, &b)? {96 return Ok(false);84 return Ok(false);97 }85 }98 }86 }106#[allow(clippy::comparison_chain)]94#[allow(clippy::comparison_chain)]107#[builtin]95#[builtin]108pub fn builtin_ends_with(96pub fn builtin_ends_with(a: Either![IStr, ArrValue], b: Either![IStr, ArrValue]) -> Result<bool> {109 s: State,110 a: Either![IStr, ArrValue],111 b: Either![IStr, ArrValue],112) -> Result<bool> {113 Ok(match (a, b) {97 Ok(match (a, b) {114 (Either2::A(a), Either2::A(b)) => a.ends_with(b.as_str()),98 (Either2::A(a), Either2::A(b)) => a.ends_with(b.as_str()),115 (Either2::B(a), Either2::B(b)) => {99 (Either2::B(a), Either2::B(b)) => {116 if b.len() > a.len() {100 if b.len() > a.len() {117 return Ok(false);101 return Ok(false);118 } else if b.len() == a.len() {102 } else if b.len() == a.len() {119 return equals(s, &Val::Arr(a), &Val::Arr(b));103 return equals(&Val::Arr(a), &Val::Arr(b));120 } else {104 } else {121 let a_len = a.len();105 let a_len = a.len();122 for (a, b) in a106 for (a, b) in a123 .slice(Some(a_len - b.len()), None, None)107 .slice(Some(a_len - b.len()), None, None)124 .iter(s.clone())108 .iter()125 .zip(b.iter(s.clone()))109 .zip(b.iter())126 {110 {127 let a = a?;111 let a = a?;128 let b = b?;112 let b = b?;129 if !equals(s.clone(), &a, &b)? {113 if !equals(&a, &b)? {130 return Ok(false);114 return Ok(false);131 }115 }132 }116 }crates/jrsonnet-stdlib/src/operator.rsdiffbeforeafterboth8 stdlib::std_format,8 stdlib::std_format,9 typed::{Any, Either, Either2},9 typed::{Any, Either, Either2},10 val::{equals, primitive_equals},10 val::{equals, primitive_equals},11 IStr, State, Val,11 IStr, Val,12};12};131314#[builtin]14#[builtin]15pub fn builtin_mod(s: State, a: Either![f64, IStr], b: Any) -> Result<Any> {15pub fn builtin_mod(a: Either![f64, IStr], b: Any) -> Result<Any> {16 use Either2::*;16 use Either2::*;17 Ok(Any(evaluate_mod_op(17 Ok(Any(evaluate_mod_op(18 s,19 &match a {18 &match a {20 A(v) => Val::Num(v),19 A(v) => Val::Num(v),21 B(s) => Val::Str(s),20 B(s) => Val::Str(s),30}29}313032#[builtin]31#[builtin]33pub fn builtin_equals(s: State, a: Any, b: Any) -> Result<bool> {32pub fn builtin_equals(a: Any, b: Any) -> Result<bool> {34 equals(s, &a.0, &b.0)33 equals(&a.0, &b.0)35}34}363537#[builtin]36#[builtin]38pub fn builtin_format(s: State, str: IStr, vals: Any) -> Result<String> {37pub fn builtin_format(str: IStr, vals: Any) -> Result<String> {39 std_format(s, str, vals.0)38 std_format(str, vals.0)40}39}4140crates/jrsonnet-stdlib/src/sort.rsdiffbeforeafterboth1use jrsonnet_evaluator::{1use jrsonnet_evaluator::{2 error::Result,2 error::Result,3 function::{builtin, FuncVal},3 function::{builtin, CallLocation, FuncVal},4 throw,4 throw,5 typed::Any,5 typed::Any,6 val::ArrValue,6 val::ArrValue,7 State, Val,7 Context, Val,8};8};9use jrsonnet_gcmodule::Cc;9use jrsonnet_gcmodule::Cc;101050}50}515152/// * `key_getter` - None, if identity sort required52/// * `key_getter` - None, if identity sort required53pub fn sort(s: State, values: Cc<Vec<Val>>, key_getter: FuncVal) -> Result<Cc<Vec<Val>>> {53pub fn sort(ctx: Context, values: Cc<Vec<Val>>, key_getter: FuncVal) -> Result<Cc<Vec<Val>>> {54 if values.len() <= 1 {54 if values.len() <= 1 {55 return Ok(values);55 return Ok(values);56 }56 }76 for value in values.iter() {76 for value in values.iter() {77 vk.push((77 vk.push((78 value.clone(),78 value.clone(),79 key_getter.evaluate_simple(s.clone(), &(Any(value.clone()),))?,79 key_getter.evaluate(80 ctx.clone(),81 CallLocation::native(),82 &(Any(value.clone()),),83 true,84 )?,80 ));85 ));81 }86 }9710298#[builtin]103#[builtin]99#[allow(non_snake_case)]104#[allow(non_snake_case)]100pub fn builtin_sort(s: State, arr: ArrValue, keyF: Option<FuncVal>) -> Result<ArrValue> {105pub fn builtin_sort(ctx: Context, arr: ArrValue, keyF: Option<FuncVal>) -> Result<ArrValue> {101 if arr.len() <= 1 {106 if arr.len() <= 1 {102 return Ok(arr);107 return Ok(arr);103 }108 }104 Ok(ArrValue::Eager(super::sort::sort(109 Ok(ArrValue::Eager(super::sort::sort(105 s.clone(),110 ctx,106 arr.evaluated(s)?,111 arr.evaluated()?,107 keyF.unwrap_or_else(FuncVal::identity),112 keyF.unwrap_or_else(FuncVal::identity),108 )?))113 )?))109}114}flake.lockdiffbeforeafterbothno changes
flake.nixdiffbeforeafterboth21 nativeBuildInputs = with pkgs;[21 nativeBuildInputs = with pkgs;[22 rust22 rust23 cargo-edit23 cargo-edit24 hyperfine25 go-jsonnet24 ];26 ];25 };27 };26 }28 }tests/tests/as_native.rsdiffbeforeafterboth131314 let native = func.into_native::<((u32, u32), u32)>();14 let native = func.into_native::<((u32, u32), u32)>();151516 ensure_eq!(native(s.clone(), 1, 2)?, 3);16 ensure_eq!(native(1, 2)?, 3);17 ensure_eq!(native(s, 3, 4)?, 7);17 ensure_eq!(native(3, 4)?, 7);181819 Ok(())19 Ok(())20}20}tests/tests/builtin.rsdiffbeforeafterboth5 function::{builtin, builtin::Builtin, CallLocation, FuncVal},5 function::{builtin, builtin::Builtin, CallLocation, FuncVal},6 tb,6 tb,7 typed::Typed,7 typed::Typed,8 Context, State, Thunk, Val,8 ContextBuilder, State, Thunk, Val,9};9};10use jrsonnet_gcmodule::Cc;10use jrsonnet_gcmodule::Cc;11use jrsonnet_stdlib::StateExt;11use jrsonnet_stdlib::StateExt;171718#[test]18#[test]19fn basic_function() -> Result<()> {19fn basic_function() -> Result<()> {20 let s = State::default();21 let a: a = a {};20 let a: a = a {};22 let v = u32::from_untyped(21 let v = u32::from_untyped(a.call(23 a.call(s.clone(), Context::new(), CallLocation::native(), &())?,22 ContextBuilder::dangerous_empty_state().build(),24 s,23 CallLocation::native(),24 &(),25 )?;25 )?)?;262650 null50 null51 ",51 ",52 )?;52 )?;53 ensure_val_eq!(s, v, Val::Null);53 ensure_val_eq!(v, Val::Null);54 Ok(())54 Ok(())55}55}565689 null89 null90 ",90 ",91 )?;91 )?;92 ensure_val_eq!(s, v, Val::Null);92 ensure_val_eq!(v, Val::Null);93 Ok(())93 Ok(())94}94}9595tests/tests/common.rsdiffbeforeafterboth272728#[macro_export]28#[macro_export]29macro_rules! ensure_val_eq {29macro_rules! ensure_val_eq {30 ($s:expr, $a:expr, $b:expr) => {{30 ($a:expr, $b:expr) => {{31 if !::jrsonnet_evaluator::val::equals($s.clone(), &$a.clone(), &$b.clone())? {31 if !::jrsonnet_evaluator::val::equals(&$a.clone(), &$b.clone())? {32 ::jrsonnet_evaluator::throw!(32 ::jrsonnet_evaluator::throw!(33 "assertion failed: a != b\na={:#?}\nb={:#?}",33 "assertion failed: a != b\na={:#?}\nb={:#?}",34 $a.to_json(34 $a.to_json(35 $s.clone(),36 2,35 2,37 #[cfg(feature = "exp-preserve-order")]36 #[cfg(feature = "exp-preserve-order")]38 false37 false39 )?,38 )?,40 $b.to_json(39 $b.to_json(41 $s.clone(),42 2,40 2,43 #[cfg(feature = "exp-preserve-order")]41 #[cfg(feature = "exp-preserve-order")]44 false42 false49}47}504851#[builtin]49#[builtin]52fn assert_throw(s: State, lazy: Thunk<Val>, message: String) -> Result<bool> {50fn assert_throw(lazy: Thunk<Val>, message: String) -> Result<bool> {53 match lazy.evaluate(s) {51 match lazy.evaluate() {54 Ok(_) => {52 Ok(_) => {55 throw!("expected argument to throw on evaluation, but it returned instead")53 throw!("expected argument to throw on evaluation, but it returned instead")56 }54 }68 bobj.member("assertThrow".into())66 bobj.member("assertThrow".into())69 .hide()67 .hide()70 .value(68 .value(Val::Func(FuncVal::StaticBuiltin(assert_throw::INST)))71 s.clone(),72 Val::Func(FuncVal::StaticBuiltin(assert_throw::INST)),73 )74 .expect("no error");69 .expect("no error");7570tests/tests/golden.rsdiffbeforeafterboth131314fn run(root: &Path, file: &Path) -> String {14fn run(root: &Path, file: &Path) -> String {15 let s = State::default();15 let s = State::default();16 s.set_trace_format(Box::new(CompactFormat {16 s.set_trace_format(CompactFormat {17 resolver: PathResolver::Relative(root.to_owned()),17 resolver: PathResolver::Relative(root.to_owned()),18 padding: 3,18 padding: 3,19 }));19 });20 s.with_stdlib();20 s.with_stdlib();21 common::with_test(&s);21 common::with_test(&s);22 s.set_import_resolver(Box::new(FileImportResolver::default()));22 s.set_import_resolver(Box::new(FileImportResolver::default()));26 Err(e) => return s.stringify_err(&e),26 Err(e) => return s.stringify_err(&e),27 };27 };28 match v.to_json(28 match v.to_json(29 s.clone(),30 3,29 3,31 #[cfg(feature = "exp-preserve-order")]30 #[cfg(feature = "exp-preserve-order")]32 false,31 false,tests/tests/sanity.rsdiffbeforeafterboth9 s.with_stdlib();9 s.with_stdlib();101011 let v = s.evaluate_snippet("snip".to_owned(), "assert 1 == 1: 'fail'; null")?;11 let v = s.evaluate_snippet("snip".to_owned(), "assert 1 == 1: 'fail'; null")?;12 ensure_val_eq!(s, v, Val::Null);12 ensure_val_eq!(v, Val::Null);13 let v = s.evaluate_snippet("snip".to_owned(), "std.assertEqual(1, 1)")?;13 let v = s.evaluate_snippet("snip".to_owned(), "std.assertEqual(1, 1)")?;14 ensure_val_eq!(s, v, Val::Bool(true));14 ensure_val_eq!(v, Val::Bool(true));151516 Ok(())16 Ok(())17}17}tests/tests/suite.rsdiffbeforeafterboth131314fn run(root: &Path, file: &Path) {14fn run(root: &Path, file: &Path) {15 let s = State::default();15 let s = State::default();16 s.set_trace_format(Box::new(CompactFormat {16 s.set_trace_format(CompactFormat {17 resolver: PathResolver::Relative(root.to_owned()),17 resolver: PathResolver::Relative(root.to_owned()),18 padding: 3,18 padding: 3,19 }));19 });20 s.with_stdlib();20 s.with_stdlib();21 common::with_test(&s);21 common::with_test(&s);22 s.set_import_resolver(Box::new(FileImportResolver::default()));22 s.set_import_resolver(Box::new(FileImportResolver::default()));tests/tests/typed_obj.rsdiffbeforeafterboth11 b: u16,11 b: u16,12}12}131314fn test_roundtrip<T: Typed + PartialEq + Debug + Clone>(value: T, s: State) -> Result<()> {14fn test_roundtrip<T: Typed + PartialEq + Debug + Clone>(value: T) -> Result<()> {15 let untyped = T::into_untyped(value.clone(), s.clone())?;15 let untyped = T::into_untyped(value.clone())?;16 let value2 = T::from_untyped(untyped.clone(), s.clone())?;16 let value2 = T::from_untyped(untyped.clone())?;17 ensure_eq!(value, value2);17 ensure_eq!(value, value2);18 let untyped2 = T::into_untyped(value2, s.clone())?;18 let untyped2 = T::into_untyped(value2)?;19 ensure_val_eq!(s, untyped, untyped2);19 ensure_val_eq!(untyped, untyped2);202021 Ok(())21 Ok(())22}22}26 let s = State::default();26 let s = State::default();27 s.with_stdlib();27 s.with_stdlib();28 let a = A::from_untyped(28 let a = A::from_untyped(s.evaluate_snippet("snip".to_owned(), "{a: 1, b: 2}")?)?;29 s.evaluate_snippet("snip".to_owned(), "{a: 1, b: 2}")?,30 s.clone(),31 )?;32 ensure_eq!(a, A { a: 1, b: 2 });29 ensure_eq!(a, A { a: 1, b: 2 });33 test_roundtrip(a, s)?;30 test_roundtrip(a)?;34 Ok(())31 Ok(())35}32}363346 let s = State::default();43 let s = State::default();47 s.with_stdlib();44 s.with_stdlib();48 let b = B::from_untyped(45 let b = B::from_untyped(s.evaluate_snippet("snip".to_owned(), "{a: 1, c: 2}")?)?;49 s.evaluate_snippet("snip".to_owned(), "{a: 1, c: 2}")?,50 s.clone(),51 )?;52 ensure_eq!(b, B { a: 1, b: 2 });46 ensure_eq!(b, B { a: 1, b: 2 });53 ensure_eq!(47 ensure_eq!(54 &B::into_untyped(b.clone(), s.clone())?.to_string(s.clone())? as &str,48 &B::into_untyped(b.clone())?.to_string()? as &str,55 r#"{"a": 1, "c": 2}"#,49 r#"{"a": 1, "c": 2}"#,56 );50 );57 test_roundtrip(b, s)?;51 test_roundtrip(b)?;58 Ok(())52 Ok(())59}53}605479 s.with_stdlib();73 s.with_stdlib();80 let obj = Object::from_untyped(74 let obj = Object::from_untyped(81 s.evaluate_snippet("snip".to_owned(), "{apiVersion: 'ver', kind: 'kind', b: 2}")?,75 s.evaluate_snippet("snip".to_owned(), "{apiVersion: 'ver', kind: 'kind', b: 2}")?,82 s.clone(),83 )?;76 )?;84 ensure_eq!(77 ensure_eq!(85 obj,78 obj,92 }85 }93 );86 );94 ensure_eq!(87 ensure_eq!(95 &Object::into_untyped(obj.clone(), s.clone())?.to_string(s.clone())? as &str,88 &Object::into_untyped(obj.clone())?.to_string()? as &str,96 r#"{"apiVersion": "ver", "b": 2, "kind": "kind"}"#,89 r#"{"apiVersion": "ver", "b": 2, "kind": "kind"}"#,97 );90 );98 test_roundtrip(obj, s)?;91 test_roundtrip(obj)?;99 Ok(())92 Ok(())100}93}10194110 let s = State::default();103 let s = State::default();111 s.with_stdlib();104 s.with_stdlib();112 let c = C::from_untyped(105 let c = C::from_untyped(s.evaluate_snippet("snip".to_owned(), "{a: 1, b: 2}")?)?;113 s.evaluate_snippet("snip".to_owned(), "{a: 1, b: 2}")?,114 s.clone(),115 )?;116 ensure_eq!(c, C { a: Some(1), b: 2 });106 ensure_eq!(c, C { a: Some(1), b: 2 });117 ensure_eq!(107 ensure_eq!(118 &C::into_untyped(c.clone(), s.clone())?.to_string(s.clone())? as &str,108 &C::into_untyped(c.clone())?.to_string()? as &str,119 r#"{"a": 1, "b": 2}"#,109 r#"{"a": 1, "b": 2}"#,120 );110 );121 test_roundtrip(c, s)?;111 test_roundtrip(c)?;122 Ok(())112 Ok(())123}113}124114125#[test]115#[test]126fn optional_field_none() -> Result<()> {116fn optional_field_none() -> Result<()> {127 let s = State::default();117 let s = State::default();128 s.with_stdlib();118 s.with_stdlib();129 let c = C::from_untyped(s.evaluate_snippet("snip".to_owned(), "{b: 2}")?, s.clone())?;119 let c = C::from_untyped(s.evaluate_snippet("snip".to_owned(), "{b: 2}")?)?;130 ensure_eq!(c, C { a: None, b: 2 });120 ensure_eq!(c, C { a: None, b: 2 });131 ensure_eq!(121 ensure_eq!(132 &C::into_untyped(c.clone(), s.clone())?.to_string(s.clone())? as &str,122 &C::into_untyped(c.clone())?.to_string()? as &str,133 r#"{"b": 2}"#,123 r#"{"b": 2}"#,134 );124 );135 test_roundtrip(c, s)?;125 test_roundtrip(c)?;136 Ok(())126 Ok(())137}127}138128153 let s = State::default();143 let s = State::default();154 s.with_stdlib();144 s.with_stdlib();155 let d = D::from_untyped(145 let d = D::from_untyped(s.evaluate_snippet("snip".to_owned(), "{b: 2, v:1}")?)?;156 s.evaluate_snippet("snip".to_owned(), "{b: 2, v:1}")?,157 s.clone(),158 )?;159 ensure_eq!(146 ensure_eq!(160 d,147 d,164 }151 }165 );152 );166 ensure_eq!(153 ensure_eq!(167 &D::into_untyped(d.clone(), s.clone())?.to_string(s.clone())? as &str,154 &D::into_untyped(d.clone())?.to_string()? as &str,168 r#"{"b": 2, "v": 1}"#,155 r#"{"b": 2, "v": 1}"#,169 );156 );170 test_roundtrip(d, s)?;157 test_roundtrip(d)?;171 Ok(())158 Ok(())172}159}173160176 let s = State::default();163 let s = State::default();177 s.with_stdlib();164 s.with_stdlib();178 let d = D::from_untyped(165 let d = D::from_untyped(s.evaluate_snippet("snip".to_owned(), "{b: 2, v: '1'}")?)?;179 s.evaluate_snippet("snip".to_owned(), "{b: 2, v: '1'}")?,180 s.clone(),181 )?;182 ensure_eq!(d, D { e: None, b: 2 });166 ensure_eq!(d, D { e: None, b: 2 });183 ensure_eq!(167 ensure_eq!(184 &D::into_untyped(d.clone(), s.clone())?.to_string(s.clone())? as &str,168 &D::into_untyped(d.clone())?.to_string()? as &str,185 r#"{"b": 2}"#,169 r#"{"b": 2}"#,186 );170 );187 test_roundtrip(d, s)?;171 test_roundtrip(d)?;188 Ok(())172 Ok(())189}173}190174