git.delta.rocks / jrsonnet / refs/commits / dad6c32364f5

difftreelog

refactor error helpers

Yaroslav Bolyukin2023-08-13parent: #81c4597.patch.diff
in: master

38 files changed

modifiedbindings/jsonnet/src/import.rsdiffbeforeafterboth
13};13};
1414
15use jrsonnet_evaluator::{15use jrsonnet_evaluator::{
16 bail,
16 error::{ErrorKind::*, Result},17 error::{ErrorKind::*, Result},
17 throw, FileImportResolver, ImportResolver,18 FileImportResolver, ImportResolver,
18};19};
19use jrsonnet_gcmodule::Trace;20use jrsonnet_gcmodule::Trace;
20use jrsonnet_parser::{SourceDirectory, SourceFile, SourcePath};21use jrsonnet_parser::{SourceDirectory, SourceFile, SourcePath};
80 assert!(success == 0 || success == 1);81 assert!(success == 0 || success == 1);
81 if success == 0 {82 if success == 0 {
82 let result = String::from_utf8(buf_intern).expect("error should be valid string");83 let result = String::from_utf8(buf_intern).expect("error should be valid string");
83 throw!(ImportCallbackError(result));84 bail!(ImportCallbackError(result));
84 }85 }
8586
86 let found_here_raw = unsafe { CStr::from_ptr(found_here) };87 let found_here_raw = unsafe { CStr::from_ptr(found_here) };
modifiedbindings/jsonnet/src/lib.rsdiffbeforeafterboth
19};19};
2020
21use jrsonnet_evaluator::{21use jrsonnet_evaluator::{
22 apply_tla,22 apply_tla, bail,
23 function::TlaArg,23 function::TlaArg,
24 gc::GcHashMap,24 gc::GcHashMap,
25 manifest::{JsonFormat, ManifestFormat, ToStringFormat},25 manifest::{JsonFormat, ManifestFormat, ToStringFormat},
26 stack::set_stack_depth_limit,26 stack::set_stack_depth_limit,
27 tb, throw,27 tb,
28 trace::{CompactFormat, PathResolver, TraceFormat},28 trace::{CompactFormat, PathResolver, TraceFormat},
29 FileImportResolver, IStr, Result, State, Val,29 FileImportResolver, IStr, Result, State, Val,
30};30};
249249
250fn val_to_multi(val: Val, format: &dyn ManifestFormat) -> Result<Vec<(IStr, IStr)>> {250fn val_to_multi(val: Val, format: &dyn ManifestFormat) -> Result<Vec<(IStr, IStr)>> {
251 let Val::Obj(val) = val else {251 let Val::Obj(val) = val else {
252 throw!("expected object as multi output")252 bail!("expected object as multi output")
253 };253 };
254 let mut out = Vec::new();254 let mut out = Vec::new();
255 for (k, v) in val.iter(255 for (k, v) in val.iter(
336336
337fn val_to_stream(val: Val, format: &dyn ManifestFormat) -> Result<Vec<IStr>> {337fn val_to_stream(val: Val, format: &dyn ManifestFormat) -> Result<Vec<IStr>> {
338 let Val::Arr(val) = val else {338 let Val::Arr(val) = val else {
339 throw!("expected array as stream output")339 bail!("expected array as stream output")
340 };340 };
341 let mut out = Vec::new();341 let mut out = Vec::new();
342 for item in val.iter() {342 for item in val.iter() {
modifiedcmds/jrsonnet/src/main.rsdiffbeforeafterboth
7use clap_complete::Shell;7use clap_complete::Shell;
8use jrsonnet_cli::{GcOpts, ManifestOpts, MiscOpts, OutputOpts, StdOpts, TlaOpts, TraceOpts};8use jrsonnet_cli::{GcOpts, ManifestOpts, MiscOpts, OutputOpts, StdOpts, TlaOpts, TraceOpts};
9use jrsonnet_evaluator::{9use jrsonnet_evaluator::{
10 apply_tla,10 apply_tla, bail,
11 error::{Error as JrError, ErrorKind},11 error::{Error as JrError, ErrorKind},
12 throw, ResultExt, State, Val,12 ResultExt, State, Val,
13};13};
1414
15#[cfg(feature = "mimalloc")]15#[cfg(feature = "mimalloc")]
208 create_dir_all(dir)?;208 create_dir_all(dir)?;
209 }209 }
210 let Val::Obj(obj) = val else {210 let Val::Obj(obj) = val else {
211 throw!(211 bail!(
212 "value should be object for --multi manifest, got {}",212 "value should be object for --multi manifest, got {}",
213 val.value_type()213 val.value_type()
214 )214 )
modifiedcrates/jrsonnet-evaluator/src/ctx.rsdiffbeforeafterboth
54 pub fn binding(&self, name: IStr) -> Result<Thunk<Val>> {54 pub fn binding(&self, name: IStr) -> Result<Thunk<Val>> {
55 use std::cmp::Ordering;55 use std::cmp::Ordering;
5656
57 use crate::throw;57 use crate::bail;
5858
59 if let Some(val) = self.0.bindings.get(&name).cloned() {59 if let Some(val) = self.0.bindings.get(&name).cloned() {
60 return Ok(val);60 return Ok(val);
70 });70 });
71 heap.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap_or(Ordering::Equal));71 heap.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap_or(Ordering::Equal));
7272
73 throw!(VariableIsNotDefined(73 bail!(VariableIsNotDefined(
74 name,74 name,
75 heap.into_iter().map(|(_, k)| k).collect()75 heap.into_iter().map(|(_, k)| k).collect()
76 ))76 ))
modifiedcrates/jrsonnet-evaluator/src/dynamic.rsdiffbeforeafterboth
22
3use jrsonnet_gcmodule::{Cc, Trace};3use jrsonnet_gcmodule::{Cc, Trace};
44
5use crate::{error::ErrorKind::InfiniteRecursionDetected, throw, val::ThunkValue, Result, Thunk};5use crate::{bail, error::ErrorKind::InfiniteRecursionDetected, val::ThunkValue, Result};
66
7// TODO: Replace with OnceCell once in std7// TODO: Replace with OnceCell once in std
8#[derive(Clone, Trace)]8#[derive(Clone, Trace)]
4141
42 fn get(self: Box<Self>) -> Result<Self::Output> {42 fn get(self: Box<Self>) -> Result<Self::Output> {
43 let Some(value) = self.0.get() else {43 let Some(value) = self.0.get() else {
44 throw!(InfiniteRecursionDetected);44 bail!(InfiniteRecursionDetected);
45 };45 };
46 Ok(value.clone())46 Ok(value.clone())
47 }47 }
modifiedcrates/jrsonnet-evaluator/src/error.rsdiffbeforeafterboth
370}370}
371371
372#[macro_export]372#[macro_export]
373macro_rules! throw {373macro_rules! bail {
374 ($w:ident$(::$i:ident)*$(($($tt:tt)*))?) => {374 ($w:ident$(::$i:ident)*$(($($tt:tt)*))?) => {
375 return Err($w$(::$i)*$(($($tt)*))?.into())375 return Err($w$(::$i)*$(($($tt)*))?.into())
376 };376 };
377 ($w:ident$(::$i:ident)*$({$($tt:tt)*})?) => {377 ($w:ident$(::$i:ident)*$({$($tt:tt)*})?) => {
378 return Err($w$(::$i)*$({$($tt)*})?.into())378 return Err($w$(::$i)*$({$($tt)*})?.into())
379 };379 };
380 ($l:literal) => {
381 return Err($crate::error::ErrorKind::RuntimeError($l.into()).into())
382 };
383 ($l:literal, $($tt:tt)*) => {380 ($l:literal$(, $($tt:tt)*)?) => {
384 return Err($crate::error::ErrorKind::RuntimeError(format!($l, $($tt)*).into()).into())381 return Err($crate::error::ErrorKind::RuntimeError(format!($l$(, $($tt)*)?).into()).into())
385 };382 };
386}383}
384
385#[macro_export]
386macro_rules! runtime_error {
387 ($l:literal$(, $($tt:tt)*)?) => {
388 $crate::error::Error::from($crate::error::ErrorKind::RuntimeError(format!($l$(, $($tt)*)?).into()))
389 };
390}
387391
modifiedcrates/jrsonnet-evaluator/src/evaluate/destructure.rsdiffbeforeafterboth
3use jrsonnet_parser::{BindSpec, Destruct, LocExpr, ParamsDesc};3use jrsonnet_parser::{BindSpec, Destruct, LocExpr, ParamsDesc};
44
5use crate::{5use crate::{
6 bail,
6 error::{ErrorKind::*, Result},7 error::{ErrorKind::*, Result},
7 evaluate, evaluate_method, evaluate_named,8 evaluate, evaluate_method, evaluate_named,
8 gc::GcHashMap,9 gc::GcHashMap,
9 throw,
10 val::ThunkValue,10 val::ThunkValue,
11 Context, Pending, Thunk, Val,11 Context, Pending, Thunk, Val,
12};12};
23 Destruct::Full(v) => {23 Destruct::Full(v) => {
24 let old = new_bindings.insert(v.clone(), parent);24 let old = new_bindings.insert(v.clone(), parent);
25 if old.is_some() {25 if old.is_some() {
26 throw!(DuplicateLocalVar(v.clone()))26 bail!(DuplicateLocalVar(v.clone()))
27 }27 }
28 }28 }
29 #[cfg(feature = "exp-destruct")]29 #[cfg(feature = "exp-destruct")]
46 fn get(self: Box<Self>) -> Result<Self::Output> {46 fn get(self: Box<Self>) -> Result<Self::Output> {
47 let v = self.parent.evaluate()?;47 let v = self.parent.evaluate()?;
48 let Val::Arr(arr) = v else {48 let Val::Arr(arr) = v else {
49 throw!("expected array");49 bail!("expected array");
50 };50 };
51 if !self.has_rest {51 if !self.has_rest {
52 if arr.len() != self.min_len {52 if arr.len() != self.min_len {
53 throw!("expected {} elements, got {}", self.min_len, arr.len())53 bail!("expected {} elements, got {}", self.min_len, arr.len())
54 }54 }
55 } else if arr.len() < self.min_len {55 } else if arr.len() < self.min_len {
56 throw!(56 bail!(
57 "expected at least {} elements, but array was only {}",57 "expected at least {} elements, but array was only {}",
58 self.min_len,58 self.min_len,
59 arr.len()59 arr.len()
178 fn get(self: Box<Self>) -> Result<Self::Output> {178 fn get(self: Box<Self>) -> Result<Self::Output> {
179 let v = self.parent.evaluate()?;179 let v = self.parent.evaluate()?;
180 let Val::Obj(obj) = v else {180 let Val::Obj(obj) = v else {
181 throw!("expected object");181 bail!("expected object");
182 };182 };
183 for field in &self.field_names {183 for field in &self.field_names {
184 if !obj.has_field_ex(field.clone(), true) {184 if !obj.has_field_ex(field.clone(), true) {
185 throw!("missing field: {}", field);185 bail!("missing field: {field}");
186 }186 }
187 }187 }
188 if !self.has_rest {188 if !self.has_rest {
189 let len = obj.len();189 let len = obj.len();
190 if len != self.field_names.len() {190 if len != self.field_names.len() {
191 throw!("too many fields, and rest not found");191 bail!("too many fields, and rest not found");
192 }192 }
193 }193 }
194 Ok(obj)194 Ok(obj)
310 }),310 }),
311 );311 );
312 if old.is_some() {312 if old.is_some() {
313 throw!(DuplicateLocalVar(name.clone()))313 bail!(DuplicateLocalVar(name.clone()))
314 }314 }
315 }315 }
316 }316 }
modifiedcrates/jrsonnet-evaluator/src/evaluate/mod.rsdiffbeforeafterboth
11use self::destructure::destruct;11use self::destructure::destruct;
12use crate::{12use crate::{
13 arr::ArrValue,13 arr::ArrValue,
14 bail,
14 destructure::evaluate_dest,15 destructure::evaluate_dest,
15 error::{suggest_object_fields, ErrorKind::*},16 error::{suggest_object_fields, ErrorKind::*},
16 evaluate::operator::{evaluate_add_op, evaluate_binary_op_special, evaluate_unary_op},17 evaluate::operator::{evaluate_add_op, evaluate_binary_op_special, evaluate_unary_op},
17 function::{CallLocation, FuncDesc, FuncVal},18 function::{CallLocation, FuncDesc, FuncVal},
18 throw,
19 typed::Typed,19 typed::Typed,
20 val::{CachedUnbound, IndexableVal, StrValue, Thunk, ThunkValue},20 val::{CachedUnbound, IndexableVal, StrValue, Thunk, ThunkValue},
21 Context, GcHashMap, ObjValue, ObjValueBuilder, ObjectAssertion, Pending, Result, ResultExt,21 Context, GcHashMap, ObjValue, ObjValueBuilder, ObjectAssertion, Pending, Result, ResultExt,
150 evaluate_comp(ctx, &specs[1..], callback)?;150 evaluate_comp(ctx, &specs[1..], callback)?;
151 }151 }
152 }152 }
153 _ => throw!(InComprehensionCanOnlyIterateOverArray),153 _ => bail!(InComprehensionCanOnlyIterateOverArray),
154 },154 },
155 }155 }
156 Ok(())156 Ok(())
375 State::push(loc, || format!("function <{}> call", f.name()), body)?375 State::push(loc, || format!("function <{}> call", f.name()), body)?
376 }376 }
377 }377 }
378 v => throw!(OnlyFunctionsCanBeCalledGot(v.value_type())),378 v => bail!(OnlyFunctionsCanBeCalledGot(v.value_type())),
379 })379 })
380}380}
381381
393 || "assertion failure".to_owned(),393 || "assertion failure".to_owned(),
394 || {394 || {
395 if let Some(msg) = msg {395 if let Some(msg) = msg {
396 throw!(AssertionFailed(evaluate(ctx, msg)?.to_string()?));396 bail!(AssertionFailed(evaluate(ctx, msg)?.to_string()?));
397 }397 }
398 throw!(AssertionFailed(Val::Null.to_string()?));398 bail!(AssertionFailed(Val::Null.to_string()?));
399 },399 },
400 )?;400 )?;
401 }401 }
457 if part.null_coaelse {457 if part.null_coaelse {
458 return Ok(Val::Null);458 return Ok(Val::Null);
459 }459 }
460 throw!(NoSuperFound)460 bail!(NoSuperFound)
461 };461 };
462 let name = evaluate(ctx.clone(), &part.value)?;462 let name = evaluate(ctx.clone(), &part.value)?;
463463
464 let Val::Str(name) = name else {464 let Val::Str(name) = name else {
465 throw!(ValueIndexMustBeTypeGot(465 bail!(ValueIndexMustBeTypeGot(
466 ValType::Obj,466 ValType::Obj,
467 ValType::Str,467 ValType::Str,
468 name.value_type(),468 name.value_type(),
483 None => {483 None => {
484 let suggestions = suggest_object_fields(super_obj, name.clone());484 let suggestions = suggest_object_fields(super_obj, name.clone());
485485
486 throw!(NoSuchField(name, suggestions))486 bail!(NoSuchField(name, suggestions))
487 }487 }
488 }488 }
489 }489 }
502 None => {502 None => {
503 let suggestions = suggest_object_fields(&v, key.clone().into_flat());503 let suggestions = suggest_object_fields(&v, key.clone().into_flat());
504504
505 throw!(NoSuchField(key.clone().into_flat(), suggestions))505 bail!(NoSuchField(key.clone().into_flat(), suggestions))
506 }506 }
507 },507 },
508 (Val::Obj(_), n) => throw!(ValueIndexMustBeTypeGot(508 (Val::Obj(_), n) => bail!(ValueIndexMustBeTypeGot(
509 ValType::Obj,509 ValType::Obj,
510 ValType::Str,510 ValType::Str,
511 n.value_type(),511 n.value_type(),
512 )),512 )),
513 (Val::Arr(v), Val::Num(n)) => {513 (Val::Arr(v), Val::Num(n)) => {
514 if n.fract() > f64::EPSILON {514 if n.fract() > f64::EPSILON {
515 throw!(FractionalIndex)515 bail!(FractionalIndex)
516 }516 }
517 v.get(n as usize)?517 v.get(n as usize)?
518 .ok_or_else(|| ArrayBoundsError(n as usize, v.len()))?518 .ok_or_else(|| ArrayBoundsError(n as usize, v.len()))?
519 }519 }
520 (Val::Arr(_), Val::Str(n)) => {520 (Val::Arr(_), Val::Str(n)) => {
521 throw!(AttemptedIndexAnArrayWithString(n.into_flat()))521 bail!(AttemptedIndexAnArrayWithString(n.into_flat()))
522 }522 }
523 (Val::Arr(_), n) => throw!(ValueIndexMustBeTypeGot(523 (Val::Arr(_), n) => bail!(ValueIndexMustBeTypeGot(
524 ValType::Arr,524 ValType::Arr,
525 ValType::Num,525 ValType::Num,
526 n.value_type(),526 n.value_type(),
537 .into();537 .into();
538 if v.is_empty() {538 if v.is_empty() {
539 let size = s.into_flat().chars().count();539 let size = s.into_flat().chars().count();
540 throw!(StringBoundsError(n as usize, size))540 bail!(StringBoundsError(n as usize, size))
541 }541 }
542 StrValue::Flat(v)542 StrValue::Flat(v)
543 }),543 }),
544 (Val::Str(_), n) => throw!(ValueIndexMustBeTypeGot(544 (Val::Str(_), n) => bail!(ValueIndexMustBeTypeGot(
545 ValType::Str,545 ValType::Str,
546 ValType::Num,546 ValType::Num,
547 n.value_type(),547 n.value_type(),
548 )),548 )),
549 #[cfg(feature = "exp-null-coaelse")]549 #[cfg(feature = "exp-null-coaelse")]
550 (Val::Null, _) if part.null_coaelse => return Ok(Val::Null),550 (Val::Null, _) if part.null_coaelse => return Ok(Val::Null),
551 (v, _) => throw!(CantIndexInto(v.value_type())),551 (v, _) => bail!(CantIndexInto(v.value_type())),
552 };552 };
553 }553 }
554 indexable554 indexable
612 ErrorStmt(e) => State::push(612 ErrorStmt(e) => State::push(
613 CallLocation::new(loc),613 CallLocation::new(loc),
614 || "error statement".to_owned(),614 || "error statement".to_owned(),
615 || throw!(RuntimeError(evaluate(ctx, e)?.to_string()?,)),615 || bail!(RuntimeError(evaluate(ctx, e)?.to_string()?,)),
616 )?,616 )?,
617 IfElse {617 IfElse {
618 cond,618 cond,
661 }661 }
662 i @ (Import(path) | ImportStr(path) | ImportBin(path)) => {662 i @ (Import(path) | ImportStr(path) | ImportBin(path)) => {
663 let Expr::Str(path) = &*path.0 else {663 let Expr::Str(path) = &*path.0 else {
664 throw!("computed imports are not supported")664 bail!("computed imports are not supported")
665 };665 };
666 let tmp = loc.clone().0;666 let tmp = loc.clone().0;
667 let s = ctx.state();667 let s = ctx.state();
modifiedcrates/jrsonnet-evaluator/src/evaluate/operator.rsdiffbeforeafterboth
44
5use crate::{5use crate::{
6 arr::ArrValue,6 arr::ArrValue,
7 bail,
7 error::ErrorKind::*,8 error::ErrorKind::*,
8 evaluate,9 evaluate,
9 stdlib::std_format,10 stdlib::std_format,
10 throw,
11 typed::Typed,11 typed::Typed,
12 val::{equals, StrValue},12 val::{equals, StrValue},
13 Context, Result, Val,13 Context, Result, Val,
21 (Minus, Num(n)) => Num(-*n),21 (Minus, Num(n)) => Num(-*n),
22 (Not, Bool(v)) => Bool(!v),22 (Not, Bool(v)) => Bool(!v),
23 (BitNot, Num(n)) => Num(!(*n as i64) as f64),23 (BitNot, Num(n)) => Num(!(*n as i64) as f64),
24 (op, o) => throw!(UnaryOperatorDoesNotOperateOnType(op, o.value_type())),24 (op, o) => bail!(UnaryOperatorDoesNotOperateOnType(op, o.value_type())),
25 })25 })
26}26}
2727
49 (Num(v1), Num(v2)) => Val::new_checked_num(v1 + v2)?,49 (Num(v1), Num(v2)) => Val::new_checked_num(v1 + v2)?,
50 #[cfg(feature = "exp-bigint")]50 #[cfg(feature = "exp-bigint")]
51 (BigInt(a), BigInt(b)) => BigInt(Box::new((&**a).clone() + (&**b).clone())),51 (BigInt(a), BigInt(b)) => BigInt(Box::new((&**a).clone() + (&**b).clone())),
52 _ => throw!(BinaryOperatorDoesNotOperateOnValues(52 _ => bail!(BinaryOperatorDoesNotOperateOnValues(
53 BinaryOpType::Add,53 BinaryOpType::Add,
54 a.value_type(),54 a.value_type(),
55 b.value_type(),55 b.value_type(),
62 match (a, b) {62 match (a, b) {
63 (Num(a), Num(b)) => {63 (Num(a), Num(b)) => {
64 if *b == 0.0 {64 if *b == 0.0 {
65 throw!(DivisionByZero)65 bail!(DivisionByZero)
66 }66 }
67 Ok(Num(a % b))67 Ok(Num(a % b))
68 }68 }
69 (Str(str), vals) => {69 (Str(str), vals) => {
70 String::into_untyped(std_format(&str.clone().into_flat(), vals.clone())?)70 String::into_untyped(std_format(&str.clone().into_flat(), vals.clone())?)
71 }71 }
72 (a, b) => throw!(BinaryOperatorDoesNotOperateOnValues(72 (a, b) => bail!(BinaryOperatorDoesNotOperateOnValues(
73 BinaryOpType::Mod,73 BinaryOpType::Mod,
74 a.value_type(),74 a.value_type(),
75 b.value_type()75 b.value_type()
124 }124 }
125 a.len().cmp(&b.len())125 a.len().cmp(&b.len())
126 }126 }
127 (_, _) => throw!(BinaryOperatorDoesNotOperateOnValues(127 (_, _) => bail!(BinaryOperatorDoesNotOperateOnValues(
128 op,128 op,
129 a.value_type(),129 a.value_type(),
130 b.value_type()130 b.value_type()
159 (Num(v1), Mul, Num(v2)) => Val::new_checked_num(v1 * v2)?,159 (Num(v1), Mul, Num(v2)) => Val::new_checked_num(v1 * v2)?,
160 (Num(v1), Div, Num(v2)) => {160 (Num(v1), Div, Num(v2)) => {
161 if *v2 == 0.0 {161 if *v2 == 0.0 {
162 throw!(DivisionByZero)162 bail!(DivisionByZero)
163 }163 }
164 Val::new_checked_num(v1 / v2)?164 Val::new_checked_num(v1 / v2)?
165 }165 }
171 (Num(v1), BitXor, Num(v2)) => Num((*v1 as i64 ^ *v2 as i64) as f64),171 (Num(v1), BitXor, Num(v2)) => Num((*v1 as i64 ^ *v2 as i64) as f64),
172 (Num(v1), Lhs, Num(v2)) => {172 (Num(v1), Lhs, Num(v2)) => {
173 if *v2 < 0.0 {173 if *v2 < 0.0 {
174 throw!("shift by negative exponent")174 bail!("shift by negative exponent")
175 }175 }
176 let exp = ((*v2 as i64) & 63) as u32;176 let exp = ((*v2 as i64) & 63) as u32;
177 Num((*v1 as i64).wrapping_shl(exp) as f64)177 Num((*v1 as i64).wrapping_shl(exp) as f64)
178 }178 }
179 (Num(v1), Rhs, Num(v2)) => {179 (Num(v1), Rhs, Num(v2)) => {
180 if *v2 < 0.0 {180 if *v2 < 0.0 {
181 throw!("shift by negative exponent")181 bail!("shift by negative exponent")
182 }182 }
183 let exp = ((*v2 as i64) & 63) as u32;183 let exp = ((*v2 as i64) & 63) as u32;
184 Num((*v1 as i64).wrapping_shr(exp) as f64)184 Num((*v1 as i64).wrapping_shr(exp) as f64)
190 #[cfg(feature = "exp-bigint")]190 #[cfg(feature = "exp-bigint")]
191 (BigInt(a), Sub, BigInt(b)) => BigInt(Box::new((&**a).clone() - (&**b).clone())),191 (BigInt(a), Sub, BigInt(b)) => BigInt(Box::new((&**a).clone() - (&**b).clone())),
192192
193 _ => throw!(BinaryOperatorDoesNotOperateOnValues(193 _ => bail!(BinaryOperatorDoesNotOperateOnValues(
194 op,194 op,
195 a.value_type(),195 a.value_type(),
196 b.value_type(),196 b.value_type(),
modifiedcrates/jrsonnet-evaluator/src/function/arglike.rsdiffbeforeafterboth
3use jrsonnet_interner::IStr;3use jrsonnet_interner::IStr;
4use jrsonnet_parser::{ArgsDesc, LocExpr};4use jrsonnet_parser::{ArgsDesc, LocExpr};
55
6use crate::{6use crate::{evaluate, gc::GcHashMap, typed::Typed, val::ThunkValue, Context, Result, Thunk, Val};
7 error::Result,
8 evaluate,
9 gc::GcHashMap,
10 typed::Typed,
11 val::{StrValue, ThunkValue},
12 Context, Thunk, Val,
13};
147
15/// Marker for arguments, which can be evaluated with context set to None8/// Marker for arguments, which can be evaluated with context set to None
modifiedcrates/jrsonnet-evaluator/src/function/builtin.rsdiffbeforeafterboth
4use jrsonnet_interner::IStr;4use jrsonnet_interner::IStr;
55
6use super::{arglike::ArgsLike, parse::parse_builtin_call, CallLocation};6use super::{arglike::ArgsLike, parse::parse_builtin_call, CallLocation};
7use crate::{error::Result, gc::TraceBox, tb, Context, Val};7use crate::{gc::TraceBox, tb, Context, Result, Val};
88
9/// Can't have str | IStr, because constant BuiltinParam causes9/// Can't have str | IStr, because constant BuiltinParam causes
10/// E0492: constant functions cannot refer to interior mutable data10/// E0492: constant functions cannot refer to interior mutable data
modifiedcrates/jrsonnet-evaluator/src/function/parse.rsdiffbeforeafterboth
66
7use super::{arglike::ArgsLike, builtin::BuiltinParam};7use super::{arglike::ArgsLike, builtin::BuiltinParam};
8use crate::{8use crate::{
9 bail,
9 destructure::destruct,10 destructure::destruct,
10 error::{ErrorKind::*, Result},11 error::{ErrorKind::*, Result},
11 evaluate_named,12 evaluate_named,
12 gc::GcHashMap,13 gc::GcHashMap,
13 throw,
14 val::ThunkValue,14 val::ThunkValue,
15 Context, Pending, Thunk, Val,15 Context, Pending, Thunk, Val,
16};16};
47 let mut passed_args =47 let mut passed_args =
48 GcHashMap::with_capacity(params.iter().map(|p| p.0.capacity_hint()).sum());48 GcHashMap::with_capacity(params.iter().map(|p| p.0.capacity_hint()).sum());
49 if args.unnamed_len() > params.len() {49 if args.unnamed_len() > params.len() {
50 throw!(TooManyArgsFunctionHas(50 bail!(TooManyArgsFunctionHas(
51 params.len(),51 params.len(),
52 params.iter().map(|p| (p.0.name(), p.1.is_some())).collect()52 params.iter().map(|p| (p.0.name(), p.1.is_some())).collect()
53 ))53 ))
71 args.named_iter(ctx, tailstrict, &mut |name, value| {71 args.named_iter(ctx, tailstrict, &mut |name, value| {
72 // FIXME: O(n) for arg existence check72 // FIXME: O(n) for arg existence check
73 if !params.iter().any(|p| p.0.name().as_ref() == Some(name)) {73 if !params.iter().any(|p| p.0.name().as_ref() == Some(name)) {
74 throw!(UnknownFunctionParameter((name as &str).to_owned()));74 bail!(UnknownFunctionParameter((name as &str).to_owned()));
75 }75 }
76 if passed_args.insert(name.clone(), value).is_some() {76 if passed_args.insert(name.clone(), value).is_some() {
77 throw!(BindingParameterASecondTime(name.clone()));77 bail!(BindingParameterASecondTime(name.clone()));
78 }78 }
79 filled_named += 1;79 filled_named += 1;
80 Ok(())80 Ok(())
125 }125 }
126 });126 });
127 if !found {127 if !found {
128 throw!(FunctionParameterNotBoundInCall(128 bail!(FunctionParameterNotBoundInCall(
129 param.0.clone().name(),129 param.0.clone().name(),
130 params.iter().map(|p| (p.0.name(), p.1.is_some())).collect()130 params.iter().map(|p| (p.0.name(), p.1.is_some())).collect()
131 ));131 ));
159) -> Result<Vec<Option<Thunk<Val>>>> {159) -> Result<Vec<Option<Thunk<Val>>>> {
160 let mut passed_args: Vec<Option<Thunk<Val>>> = vec![None; params.len()];160 let mut passed_args: Vec<Option<Thunk<Val>>> = vec![None; params.len()];
161 if args.unnamed_len() > params.len() {161 if args.unnamed_len() > params.len() {
162 throw!(TooManyArgsFunctionHas(162 bail!(TooManyArgsFunctionHas(
163 params.len(),163 params.len(),
164 params164 params
165 .iter()165 .iter()
183 .position(|p| p.name() == name)183 .position(|p| p.name() == name)
184 .ok_or_else(|| UnknownFunctionParameter((name as &str).to_owned()))?;184 .ok_or_else(|| UnknownFunctionParameter((name as &str).to_owned()))?;
185 if replace(&mut passed_args[id], Some(arg)).is_some() {185 if replace(&mut passed_args[id], Some(arg)).is_some() {
186 throw!(BindingParameterASecondTime(name.clone()));186 bail!(BindingParameterASecondTime(name.clone()));
187 }187 }
188 filled_args += 1;188 filled_args += 1;
189 Ok(())189 Ok(())
207 }207 }
208 });208 });
209 if !found {209 if !found {
210 throw!(FunctionParameterNotBoundInCall(210 bail!(FunctionParameterNotBoundInCall(
211 param.name().as_str().map(IStr::from),211 param.name().as_str().map(IStr::from),
212 params212 params
213 .iter()213 .iter()
modifiedcrates/jrsonnet-evaluator/src/import.rsdiffbeforeafterboth
12use jrsonnet_parser::{SourceDirectory, SourceFile, SourcePath};12use jrsonnet_parser::{SourceDirectory, SourceFile, SourcePath};
1313
14use crate::{14use crate::{
15 bail,
15 error::{ErrorKind::*, Result},16 error::{ErrorKind::*, Result},
16 throw,
17};17};
1818
19/// Implements file resolution logic for `import` and `importStr`19/// Implements file resolution logic for `import` and `importStr`
25 /// `from` should only be returned from [`ImportResolver::resolve`], or from other defined file, any other value25 /// `from` should only be returned from [`ImportResolver::resolve`], or from other defined file, any other value
26 /// may result in panic26 /// may result in panic
27 fn resolve_from(&self, from: &SourcePath, path: &str) -> Result<SourcePath> {27 fn resolve_from(&self, from: &SourcePath, path: &str) -> Result<SourcePath> {
28 throw!(ImportNotSupported(from.clone(), path.into()))28 bail!(ImportNotSupported(from.clone(), path.into()))
29 }29 }
30 fn resolve_from_default(&self, path: &str) -> Result<SourcePath> {30 fn resolve_from_default(&self, path: &str) -> Result<SourcePath> {
31 self.resolve_from(&SourcePath::default(), path)31 self.resolve_from(&SourcePath::default(), path)
32 }32 }
33 /// Resolves absolute path, doesn't supports jpath and other fancy things33 /// Resolves absolute path, doesn't supports jpath and other fancy things
34 fn resolve(&self, path: &Path) -> Result<SourcePath> {34 fn resolve(&self, path: &Path) -> Result<SourcePath> {
35 throw!(AbsoluteImportNotSupported(path.to_owned()))35 bail!(AbsoluteImportNotSupported(path.to_owned()))
36 }36 }
3737
38 /// Load resolved file38 /// Load resolved file
110 )));110 )));
111 }111 }
112 }112 }
113 throw!(ImportFileNotFound(from.clone(), path.to_owned()))113 bail!(ImportFileNotFound(from.clone(), path.to_owned()))
114 }114 }
115 }115 }
116 fn resolve(&self, path: &Path) -> Result<SourcePath> {116 fn resolve(&self, path: &Path) -> Result<SourcePath> {
117 let meta = match fs::metadata(path) {117 let meta = match fs::metadata(path) {
118 Ok(v) => v,118 Ok(v) => v,
119 Err(e) if e.kind() == ErrorKind::NotFound => {119 Err(e) if e.kind() == ErrorKind::NotFound => {
120 throw!(AbsoluteImportFileNotFound(path.to_owned()))120 bail!(AbsoluteImportFileNotFound(path.to_owned()))
121 }121 }
122 Err(e) => throw!(ImportIo(e.to_string())),122 Err(e) => bail!(ImportIo(e.to_string())),
123 };123 };
124 if meta.is_file() {124 if meta.is_file() {
125 Ok(SourcePath::new(SourceFile::new(125 Ok(SourcePath::new(SourceFile::new(
138 let path = if let Some(f) = id.downcast_ref::<SourceFile>() {138 let path = if let Some(f) = id.downcast_ref::<SourceFile>() {
139 f.path()139 f.path()
140 } else if id.downcast_ref::<SourceDirectory>().is_some() || id.is_default() {140 } else if id.downcast_ref::<SourceDirectory>().is_some() || id.is_default() {
141 throw!(ImportIsADirectory(id.clone()))141 bail!(ImportIsADirectory(id.clone()))
142 } else {142 } else {
143 unreachable!("other types are not supported in resolve");143 unreachable!("other types are not supported in resolve");
144 };144 };
modifiedcrates/jrsonnet-evaluator/src/integrations/serde.rsdiffbeforeafterboth
620 where620 where
621 T: std::fmt::Display,621 T: std::fmt::Display,
622 {622 {
623 JrError::new(ErrorKind::RuntimeError(format!("serde: {msg}").into()))623 runtime_error!("serde: {msg}")
624 }624 }
625}625}
626626
modifiedcrates/jrsonnet-evaluator/src/lib.rsdiffbeforeafterboth
354 }354 }
355 let parsed = file.parsed.as_ref().expect("just set").clone();355 let parsed = file.parsed.as_ref().expect("just set").clone();
356 if file.evaluating {356 if file.evaluating {
357 throw!(InfiniteRecursionDetected)357 bail!(InfiniteRecursionDetected)
358 }358 }
359 file.evaluating = true;359 file.evaluating = true;
360 // Dropping file cache guard here, as evaluation may use this map too360 // Dropping file cache guard here, as evaluation may use this map too
modifiedcrates/jrsonnet-evaluator/src/manifest.rsdiffbeforeafterboth
1use std::{borrow::Cow, fmt::Write};1use std::{borrow::Cow, fmt::Write};
22
3use crate::{3use crate::{bail, Result, State, Val};
4 error::{ErrorKind::*, Result},
5 throw, State, Val,
6};
74
8pub trait ManifestFormat {5pub trait ManifestFormat {
268 }265 }
269 buf.push('}');266 buf.push('}');
270 }267 }
271 Val::Func(_) => throw!(RuntimeError("tried to manifest function".into())),268 Val::Func(_) => bail!("tried to manifest function"),
272 };269 };
273 Ok(())270 Ok(())
274}271}
292impl ManifestFormat for StringFormat {289impl ManifestFormat for StringFormat {
293 fn manifest_buf(&self, val: Val, out: &mut String) -> Result<()> {290 fn manifest_buf(&self, val: Val, out: &mut String) -> Result<()> {
294 let Val::Str(s) = val else {291 let Val::Str(s) = val else {
295 throw!(292 bail!(
296 "output should be string for string manifest format, got {}",293 "output should be string for string manifest format, got {}",
297 val.value_type()294 val.value_type()
298 )295 )
309impl<I: ManifestFormat> ManifestFormat for YamlStreamFormat<I> {306impl<I: ManifestFormat> ManifestFormat for YamlStreamFormat<I> {
310 fn manifest_buf(&self, val: Val, out: &mut String) -> Result<()> {307 fn manifest_buf(&self, val: Val, out: &mut String) -> Result<()> {
311 let Val::Arr(arr) = val else {308 let Val::Arr(arr) = val else {
312 throw!(309 bail!(
313 "output should be array for yaml stream format, got {}",310 "output should be array for yaml stream format, got {}",
314 val.value_type()311 val.value_type()
315 )312 )
modifiedcrates/jrsonnet-evaluator/src/obj.rsdiffbeforeafterboth
1313
14use crate::{14use crate::{
15 arr::{PickObjectKeyValues, PickObjectValues},15 arr::{PickObjectKeyValues, PickObjectValues},
16 bail,
16 error::{suggest_object_fields, Error, ErrorKind::*},17 error::{suggest_object_fields, Error, ErrorKind::*},
17 function::CallLocation,18 function::CallLocation,
18 gc::{GcHashMap, GcHashSet, TraceBox},19 gc::{GcHashMap, GcHashSet, TraceBox},
19 operator::evaluate_add_op,20 operator::evaluate_add_op,
20 tb, throw,21 tb,
21 val::{ArrValue, ThunkValue},22 val::{ArrValue, ThunkValue},
22 MaybeUnbound, Result, State, Thunk, Unbound, Val,23 MaybeUnbound, Result, State, Thunk, Unbound, Val,
23};24};
404 pub fn get_or_bail(&self, key: IStr) -> Result<Val> {405 pub fn get_or_bail(&self, key: IStr) -> Result<Val> {
405 let Some(value) = self.get(key.clone())? else {406 let Some(value) = self.get(key.clone())? else {
406 let suggestions = suggest_object_fields(self, key.clone());407 let suggestions = suggest_object_fields(self, key.clone());
407 throw!(NoSuchField(key, suggestions))408 bail!(NoSuchField(key, suggestions))
408 };409 };
409 Ok(value)410 Ok(value)
410 }411 }
723 return Ok(match v {724 return Ok(match v {
724 CacheValue::Cached(v) => Some(v.clone()),725 CacheValue::Cached(v) => Some(v.clone()),
725 CacheValue::NotFound => None,726 CacheValue::NotFound => None,
726 CacheValue::Pending => throw!(InfiniteRecursionDetected),727 CacheValue::Pending => bail!(InfiniteRecursionDetected),
727 CacheValue::Errored(e) => return Err(e.clone()),728 CacheValue::Errored(e) => return Err(e.clone()),
728 });729 });
729 }730 }
953 State::push(954 State::push(
954 CallLocation(location.as_ref()),955 CallLocation(location.as_ref()),
955 || format!("field <{}> initializtion", name.clone()),956 || format!("field <{}> initializtion", name.clone()),
956 || throw!(DuplicateFieldName(name.clone())),957 || bail!(DuplicateFieldName(name.clone())),
957 )?;958 )?;
958 }959 }
959 Ok(())960 Ok(())
modifiedcrates/jrsonnet-evaluator/src/stdlib/format.rsdiffbeforeafterboth
7use thiserror::Error;7use thiserror::Error;
88
9use crate::{9use crate::{
10 bail,
10 error::{format_found, suggest_object_fields, ErrorKind::*},11 error::{format_found, suggest_object_fields, ErrorKind::*},
11 throw,
12 typed::Typed,12 typed::Typed,
13 Error, ObjValue, Result, Val,13 Error, ObjValue, Result, Val,
14};14};
611 Val::Str(s) => {611 Val::Str(s) => {
612 let s = s.into_flat();612 let s = s.into_flat();
613 if s.chars().count() != 1 {613 if s.chars().count() != 1 {
614 throw!("%c expected 1 char string, got {}", s.chars().count(),);614 bail!("%c expected 1 char string, got {}", s.chars().count());
615 }615 }
616 tmp_out.push_str(&s);616 tmp_out.push_str(&s);
617 }617 }
618 _ => {618 _ => {
619 throw!(TypeMismatch(619 bail!(TypeMismatch(
620 "%c requires number/string",620 "%c requires number/string",
621 vec![ValType::Num, ValType::Str],621 vec![ValType::Num, ValType::Str],
622 value.value_type(),622 value.value_type(),
657 let width = match c.width {657 let width = match c.width {
658 Width::Star => {658 Width::Star => {
659 if values.is_empty() {659 if values.is_empty() {
660 throw!(NotEnoughValues);660 bail!(NotEnoughValues);
661 }661 }
662 let value = &values[0];662 let value = &values[0];
663 values = &values[1..];663 values = &values[1..];
668 let precision = match c.precision {668 let precision = match c.precision {
669 Some(Width::Star) => {669 Some(Width::Star) => {
670 if values.is_empty() {670 if values.is_empty() {
671 throw!(NotEnoughValues);671 bail!(NotEnoughValues);
672 }672 }
673 let value = &values[0];673 let value = &values[0];
674 values = &values[1..];674 values = &values[1..];
683 &Val::Null683 &Val::Null
684 } else {684 } else {
685 if values.is_empty() {685 if values.is_empty() {
686 throw!(NotEnoughValues);686 bail!(NotEnoughValues);
687 }687 }
688 let value = &values[0];688 let value = &values[0];
689 values = &values[1..];689 values = &values[1..];
696 }696 }
697697
698 if !values.is_empty() {698 if !values.is_empty() {
699 throw!(699 bail!(
700 "too many values to format, expected {value_count}, got {}",700 "too many values to format, expected {value_count}, got {}",
701 value_count + values.len()701 value_count + values.len()
702 )702 )
717 let current = &field[name_offset..end_offset];717 let current = &field[name_offset..end_offset];
718 let full = &field[..name_offset];718 let full = &field[..name_offset];
719 let found = Box::new(suggest_object_fields(&obj, current.into()));719 let found = Box::new(suggest_object_fields(&obj, current.into()));
720 throw!(SubfieldNotFound {720 bail!(SubfieldNotFound {
721 current: current.into(),721 current: current.into(),
722 full: full.into(),722 full: full.into(),
723 found,723 found,
726 } else {726 } else {
727 // No underflow may happen, initially we always start with an object727 // No underflow may happen, initially we always start with an object
728 let subfield = &field[..name_offset - 1];728 let subfield = &field[..name_offset - 1];
729 throw!(SubfieldDidntYieldAnObject(729 bail!(SubfieldDidntYieldAnObject(
730 subfield.into(),730 subfield.into(),
731 current.value_type()731 current.value_type()
732 ));732 ));
750 let f: IStr = c.mkey.into();750 let f: IStr = c.mkey.into();
751 let width = match c.width {751 let width = match c.width {
752 Width::Star => {752 Width::Star => {
753 throw!(CannotUseStarWidthWithObject);753 bail!(CannotUseStarWidthWithObject);
754 }754 }
755 Width::Fixed(n) => n,755 Width::Fixed(n) => n,
756 };756 };
757 let precision = match c.precision {757 let precision = match c.precision {
758 Some(Width::Star) => {758 Some(Width::Star) => {
759 throw!(CannotUseStarWidthWithObject);759 bail!(CannotUseStarWidthWithObject);
760 }760 }
761 Some(Width::Fixed(n)) => Some(n),761 Some(Width::Fixed(n)) => Some(n),
762 None => None,762 None => None,
766 Val::Null766 Val::Null
767 } else {767 } else {
768 if f.is_empty() {768 if f.is_empty() {
769 throw!(MappingKeysRequired);769 bail!(MappingKeysRequired);
770 }770 }
771 if let Some(v) = values.get(f.clone())? {771 if let Some(v) = values.get(f.clone())? {
772 v772 v
modifiedcrates/jrsonnet-evaluator/src/stdlib/mod.rsdiffbeforeafterboth
33
4use format::{format_arr, format_obj};4use format::{format_arr, format_obj};
55
6use crate::{error::Result, function::CallLocation, State, Val};6use crate::{function::CallLocation, Result, State, Val};
77
8pub mod format;8pub mod format;
99
modifiedcrates/jrsonnet-evaluator/src/typed/conversions.rsdiffbeforeafterboth
77
8use crate::{8use crate::{
9 arr::{ArrValue, BytesArray},9 arr::{ArrValue, BytesArray},
10 error::Result,10 bail,
11 function::{native::NativeDesc, FuncDesc, FuncVal},11 function::{native::NativeDesc, FuncDesc, FuncVal},
12 throw,
13 typed::CheckType,12 typed::CheckType,
14 val::{IndexableVal, StrValue, ThunkMapper},13 val::{IndexableVal, ThunkMapper},
15 ObjValue, ObjValueBuilder, Thunk, Val,14 ObjValue, ObjValueBuilder, Result, Thunk, Val,
16};15};
1716
18#[derive(Trace)]17#[derive(Trace)]
134 Val::Num(n) => {133 Val::Num(n) => {
135 #[allow(clippy::float_cmp)]134 #[allow(clippy::float_cmp)]
136 if n.trunc() != n {135 if n.trunc() != n {
137 throw!(136 bail!(
138 "cannot convert number with fractional part to {}",137 "cannot convert number with fractional part to {}",
139 stringify!($ty)138 stringify!($ty)
140 )139 )
189 Val::Num(n) => {188 Val::Num(n) => {
190 #[allow(clippy::float_cmp)]189 #[allow(clippy::float_cmp)]
191 if n.trunc() != n {190 if n.trunc() != n {
192 throw!(191 bail!(
193 "cannot convert number with fractional part to {}",192 "cannot convert number with fractional part to {}",
194 stringify!($ty)193 stringify!($ty)
195 )194 )
253252
254 fn into_untyped(value: Self) -> Result<Val> {253 fn into_untyped(value: Self) -> Result<Val> {
255 if value > MAX_SAFE_INTEGER as Self {254 if value > MAX_SAFE_INTEGER as Self {
256 throw!("number is too large")255 bail!("number is too large")
257 }256 }
258 Ok(Val::Num(value as f64))257 Ok(Val::Num(value as f64))
259 }258 }
264 Val::Num(n) => {263 Val::Num(n) => {
265 #[allow(clippy::float_cmp)]264 #[allow(clippy::float_cmp)]
266 if n.trunc() != n {265 if n.trunc() != n {
267 throw!("cannot convert number with fractional part to usize")266 bail!("cannot convert number with fractional part to usize")
268 }267 }
269 Ok(n as Self)268 Ok(n as Self)
270 }269 }
354 let mut out = ObjValueBuilder::with_capacity(typed.len());353 let mut out = ObjValueBuilder::with_capacity(typed.len());
355 for (k, v) in typed {354 for (k, v) in typed {
356 let Some(key) = K::into_untyped(k)?.as_str() else {355 let Some(key) = K::into_untyped(k)?.as_str() else {
357 throw!("map key should serialize to string");356 bail!("map key should serialize to string");
358 };357 };
359 let value = V::into_untyped(v)?;358 let value = V::into_untyped(v)?;
360 out.member(key).value_unchecked(value);359 out.member(key).value_unchecked(value);
567 <Self as Typed>::TYPE.check(&value)?;566 <Self as Typed>::TYPE.check(&value)?;
568 match value {567 match value {
569 Val::Func(FuncVal::Normal(desc)) => Ok(desc),568 Val::Func(FuncVal::Normal(desc)) => Ok(desc),
570 Val::Func(_) => throw!("expected normal function, not builtin"),569 Val::Func(_) => bail!("expected normal function, not builtin"),
571 _ => unreachable!(),570 _ => unreachable!(),
572 }571 }
573 }572 }
649 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Func);648 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Func);
650649
651 fn into_untyped(_typed: Self) -> Result<Val> {650 fn into_untyped(_typed: Self) -> Result<Val> {
652 throw!("can only convert functions from jsonnet to native")651 bail!("can only convert functions from jsonnet to native")
653 }652 }
654653
655 fn from_untyped(untyped: Val) -> Result<Self> {654 fn from_untyped(untyped: Val) -> Result<Self> {
modifiedcrates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth
1111
12pub use crate::arr::{ArrValue, ArrayLike};12pub use crate::arr::{ArrValue, ArrayLike};
13use crate::{13use crate::{
14 bail,
14 error::{Error, ErrorKind::*},15 error::{Error, ErrorKind::*},
15 function::FuncVal,16 function::FuncVal,
16 gc::{GcHashMap, TraceBox},17 gc::{GcHashMap, TraceBox},
17 manifest::{ManifestFormat, ToStringFormat},18 manifest::{ManifestFormat, ToStringFormat},
18 tb, throw,19 tb,
19 typed::BoundedUsize,20 typed::BoundedUsize,
20 ObjValue, Result, Unbound, WeakObjValue,21 ObjValue, Result, Unbound, WeakObjValue,
21};22};
456 if num.is_finite() {457 if num.is_finite() {
457 Ok(Self::Num(num))458 Ok(Self::Num(num))
458 } else {459 } else {
459 throw!("overflow")460 bail!("overflow")
460 }461 }
461 }462 }
462463
495 Ok(match self {496 Ok(match self {
496 Val::Str(s) => IndexableVal::Str(s.into_flat()),497 Val::Str(s) => IndexableVal::Str(s.into_flat()),
497 Val::Arr(arr) => IndexableVal::Arr(arr),498 Val::Arr(arr) => IndexableVal::Arr(arr),
498 _ => throw!(ValueIsNotIndexable(self.value_type())),499 _ => bail!(ValueIsNotIndexable(self.value_type())),
499 })500 })
500 }501 }
501}502}
514 #[cfg(feature = "exp-bigint")]515 #[cfg(feature = "exp-bigint")]
515 (Val::BigInt(a), Val::BigInt(b)) => a == b,516 (Val::BigInt(a), Val::BigInt(b)) => a == b,
516 (Val::Arr(_), Val::Arr(_)) => {517 (Val::Arr(_), Val::Arr(_)) => {
517 throw!("primitiveEquals operates on primitive types, got array")518 bail!("primitiveEquals operates on primitive types, got array")
518 }519 }
519 (Val::Obj(_), Val::Obj(_)) => {520 (Val::Obj(_), Val::Obj(_)) => {
520 throw!("primitiveEquals operates on primitive types, got object")521 bail!("primitiveEquals operates on primitive types, got object")
521 }522 }
522 (a, b) if is_function_like(a) && is_function_like(b) => {523 (a, b) if is_function_like(a) && is_function_like(b) => {
523 throw!("cannot test equality of functions")524 bail!("cannot test equality of functions")
524 }525 }
525 (_, _) => false,526 (_, _) => false,
526 })527 })
modifiedcrates/jrsonnet-macros/src/lib.rsdiffbeforeafterboth
356 use ::jrsonnet_evaluator::{356 use ::jrsonnet_evaluator::{
357 State, Val,357 State, Val,
358 function::{builtin::{Builtin, StaticBuiltin, BuiltinParam, ParamName}, CallLocation, ArgsLike, parse::parse_builtin_call},358 function::{builtin::{Builtin, StaticBuiltin, BuiltinParam, ParamName}, CallLocation, ArgsLike, parse::parse_builtin_call},
359 error::Result, Context, typed::Typed,359 Result, Context, typed::Typed,
360 parser::ExprLocation,360 parser::ExprLocation,
361 };361 };
362 const PARAMS: &'static [BuiltinParam] = &[362 const PARAMS: &'static [BuiltinParam] = &[
modifiedcrates/jrsonnet-stdlib/src/arrays.rsdiffbeforeafterboth
1#![allow(non_snake_case)]1#![allow(non_snake_case)]
22
3use jrsonnet_evaluator::{3use jrsonnet_evaluator::{
4 error::{ErrorKind::RuntimeError, Result},4 bail,
5 function::{builtin, FuncVal},5 function::{builtin, FuncVal},
6 throw,6 runtime_error,
7 typed::{BoundedI32, BoundedUsize, Either2, NativeFn, Typed},7 typed::{BoundedI32, BoundedUsize, Either2, NativeFn, Typed},
8 val::{equals, ArrValue, IndexableVal, StrValue},8 val::{equals, ArrValue, IndexableVal},
9 Either, IStr, Thunk, Val,9 Either, IStr, Result, Thunk, Val,
10};10};
1111
12pub(crate) fn eval_on_empty(on_empty: Option<Thunk<Val>>) -> Result<Val> {12pub(crate) fn eval_on_empty(on_empty: Option<Thunk<Val>>) -> Result<Val> {
13 if let Some(on_empty) = on_empty {13 if let Some(on_empty) = on_empty {
14 on_empty.evaluate()14 on_empty.evaluate()
15 } else {15 } else {
16 throw!("expected non-empty array")16 bail!("expected non-empty array")
17 }17 }
18}18}
1919
39 Either2::A(s) => Val::Str(StrValue::Flat(s.repeat(count).into())),39 Either2::A(s) => Val::Str(StrValue::Flat(s.repeat(count).into())),
40 Either2::B(arr) => Val::Arr(40 Either2::B(arr) => Val::Arr(
41 ArrValue::repeated(arr, count)41 ArrValue::repeated(arr, count)
42 .ok_or_else(|| RuntimeError("repeated length overflow".into()))?,42 .ok_or_else(|| runtime_error!("repeated length overflow"))?,
43 ),43 ),
44 })44 })
45}45}
73 match func(Either2::A(c.to_string()))? {73 match func(Either2::A(c.to_string()))? {
74 Val::Str(o) => write!(out, "{o}").unwrap(),74 Val::Str(o) => write!(out, "{o}").unwrap(),
75 Val::Null => continue,75 Val::Null => continue,
76 _ => throw!("in std.join all items should be strings"),76 _ => bail!("in std.join all items should be strings"),
77 };77 };
78 }78 }
79 Ok(IndexableVal::Str(out.into()))79 Ok(IndexableVal::Str(out.into()))
89 }89 }
90 }90 }
91 Val::Null => continue,91 Val::Null => continue,
92 _ => throw!("in std.join all items should be arrays"),92 _ => bail!("in std.join all items should be arrays"),
93 };93 };
94 }94 }
95 Ok(IndexableVal::Arr(out.into()))95 Ok(IndexableVal::Arr(out.into()))
154 } else if matches!(item, Val::Null) {154 } else if matches!(item, Val::Null) {
155 continue;155 continue;
156 } else {156 } else {
157 throw!("in std.join all items should be arrays");157 bail!("in std.join all items should be arrays");
158 }158 }
159 }159 }
160160
175 } else if matches!(item, Val::Null) {175 } else if matches!(item, Val::Null) {
176 continue;176 continue;
177 } else {177 } else {
178 throw!("in std.join all items should be strings");178 bail!("in std.join all items should be strings");
179 }179 }
180 }180 }
181181
modifiedcrates/jrsonnet-stdlib/src/compat.rsdiffbeforeafterboth
1use std::cmp::Ordering;1use std::cmp::Ordering;
22
3use jrsonnet_evaluator::{error::Result, function::builtin, operator::evaluate_compare_op, Val};3use jrsonnet_evaluator::{function::builtin, operator::evaluate_compare_op, Result, Val};
44
5#[builtin]5#[builtin]
6#[allow(non_snake_case)]6#[allow(non_snake_case)]
modifiedcrates/jrsonnet-stdlib/src/encoding.rsdiffbeforeafterboth
1use base64::{engine::general_purpose::STANDARD, Engine};1use base64::{engine::general_purpose::STANDARD, Engine};
2use jrsonnet_evaluator::{2use jrsonnet_evaluator::{
3 error::{ErrorKind::RuntimeError, Result},
4 function::builtin,3 function::builtin,
4 runtime_error,
5 typed::{Either, Either2},5 typed::{Either, Either2},
6 IBytes, IStr,6 IBytes, IStr, Result,
7};7};
88
9#[builtin]9#[builtin]
1313
14#[builtin]14#[builtin]
15pub fn builtin_decode_utf8(arr: IBytes) -> Result<IStr> {15pub fn builtin_decode_utf8(arr: IBytes) -> Result<IStr> {
16 Ok(arr16 arr.cast_str().ok_or_else(|| runtime_error!("bad utf8"))
17 .cast_str()
18 .ok_or_else(|| RuntimeError("bad utf8".into()))?)
19}17}
2018
21#[builtin]19#[builtin]
31pub fn builtin_base64_decode_bytes(str: IStr) -> Result<IBytes> {29pub fn builtin_base64_decode_bytes(str: IStr) -> Result<IBytes> {
32 Ok(STANDARD30 Ok(STANDARD
33 .decode(str.as_bytes())31 .decode(str.as_bytes())
34 .map_err(|e| RuntimeError(format!("invalid base64: {e}").into()))?32 .map_err(|e| runtime_error!("invalid base64: {e}"))?
35 .as_slice()33 .as_slice()
36 .into())34 .into())
37}35}
40pub fn builtin_base64_decode(str: IStr) -> Result<String> {38pub fn builtin_base64_decode(str: IStr) -> Result<String> {
41 let bytes = STANDARD39 let bytes = STANDARD
42 .decode(str.as_bytes())40 .decode(str.as_bytes())
43 .map_err(|e| RuntimeError(format!("invalid base64: {e}").into()))?;41 .map_err(|e| runtime_error!("invalid base64: {e}"))?;
44 Ok(String::from_utf8(bytes).map_err(|_| RuntimeError("bad utf8".into()))?)42 Ok(String::from_utf8(bytes).map_err(|_| runtime_error!("bad utf8"))?)
45}43}
4644
modifiedcrates/jrsonnet-stdlib/src/manifest/mod.rsdiffbeforeafterboth
2mod yaml;2mod yaml;
33
4use jrsonnet_evaluator::{4use jrsonnet_evaluator::{
5 error::Result,
6 function::builtin,5 function::builtin,
7 manifest::{escape_string_json, JsonFormat},6 manifest::{escape_string_json, JsonFormat},
8 IStr, ObjValue, Val,7 IStr, ObjValue, Result, Val,
9};8};
10pub use toml::TomlFormat;9pub use toml::TomlFormat;
11pub use yaml::YamlFormat;10pub use yaml::YamlFormat;
modifiedcrates/jrsonnet-stdlib/src/manifest/toml.rsdiffbeforeafterboth
1use std::borrow::Cow;1use std::borrow::Cow;
22
3use jrsonnet_evaluator::{3use jrsonnet_evaluator::{
4 bail,
4 manifest::{escape_string_json_buf, ManifestFormat},5 manifest::{escape_string_json_buf, ManifestFormat},
5 throw,
6 val::ArrValue,6 val::ArrValue,
7 IStr, ObjValue, Result, Val,7 IStr, ObjValue, Result, Val,
8};8};
157 buf.push_str(" }");157 buf.push_str(" }");
158 }158 }
159 Val::Null => {159 Val::Null => {
160 throw!("tried to manifest null")160 bail!("tried to manifest null")
161 }161 }
162 Val::Func(_) => {162 Val::Func(_) => {
163 throw!("tried to manifest function")163 bail!("tried to manifest function")
164 }164 }
165 }165 }
166 Ok(())166 Ok(())
290 Val::Obj(obj) => {290 Val::Obj(obj) => {
291 manifest_table_internal(&obj, &mut Vec::new(), buf, &mut String::new(), self)291 manifest_table_internal(&obj, &mut Vec::new(), buf, &mut String::new(), self)
292 }292 }
293 _ => throw!("toml body should be object"),293 _ => bail!("toml body should be object"),
294 }294 }
295 }295 }
296}296}
modifiedcrates/jrsonnet-stdlib/src/manifest/yaml.rsdiffbeforeafterboth
1use std::{borrow::Cow, fmt::Write};1use std::{borrow::Cow, fmt::Write};
22
3use jrsonnet_evaluator::{3use jrsonnet_evaluator::{
4 bail,
4 manifest::{escape_string_json_buf, ManifestFormat},5 manifest::{escape_string_json_buf, ManifestFormat},
5 throw, Result, Val,6 Result, Val,
6};7};
78
8pub struct YamlFormat<'s> {9pub struct YamlFormat<'s> {
219 }220 }
220 }221 }
221 }222 }
222 Val::Func(_) => throw!("tried to manifest function"),223 Val::Func(_) => bail!("tried to manifest function"),
223 }224 }
224 Ok(())225 Ok(())
225}226}
modifiedcrates/jrsonnet-stdlib/src/misc.rsdiffbeforeafterboth
1use std::{cell::RefCell, rc::Rc};1use std::{cell::RefCell, rc::Rc};
22
3use jrsonnet_evaluator::{3use jrsonnet_evaluator::{
4 bail,
4 error::{ErrorKind::*, Result},5 error::{ErrorKind::*, Result},
5 function::{builtin, ArgLike, CallLocation, FuncVal},6 function::{builtin, ArgLike, CallLocation, FuncVal},
6 manifest::JsonFormat,7 manifest::JsonFormat,
7 throw,
8 typed::{Either2, Either4},8 typed::{Either2, Either4},
9 val::{equals, ArrValue},9 val::{equals, ArrValue},
10 Context, Either, IStr, ObjValue, Thunk, Val,10 Context, Either, IStr, ObjValue, Thunk, Val,
103 true103 true
104 }104 }
105 }105 }
106 _ => throw!("both arguments should be of the same type"),106 _ => bail!("both arguments should be of the same type"),
107 })107 })
108}108}
109109
129 true129 true
130 }130 }
131 }131 }
132 _ => throw!("both arguments should be of the same type"),132 _ => bail!("both arguments should be of the same type"),
133 })133 })
134}134}
135135
modifiedcrates/jrsonnet-stdlib/src/operator.rsdiffbeforeafterboth
2//! However, in our case we instead implement them in native, and implement native functions on top of core for backwards compatibility2//! However, in our case we instead implement them in native, and implement native functions on top of core for backwards compatibility
33
4use jrsonnet_evaluator::{4use jrsonnet_evaluator::{
5 error::Result,
6 function::builtin,5 function::builtin,
7 operator::evaluate_mod_op,6 operator::evaluate_mod_op,
8 stdlib::std_format,7 stdlib::std_format,
9 typed::{Either, Either2},8 typed::{Either, Either2},
10 val::{equals, primitive_equals, StrValue},9 val::{equals, primitive_equals},
11 IStr, Val,10 IStr, Result, Val,
12};11};
1312
14#[builtin]13#[builtin]
modifiedcrates/jrsonnet-stdlib/src/parse.rsdiffbeforeafterboth
1use jrsonnet_evaluator::{1use jrsonnet_evaluator::{function::builtin, runtime_error, IStr, Result, Val};
2 error::{ErrorKind::RuntimeError, Result},
3 function::builtin,
4 IStr, Val,
5};
6use serde::Deserialize;2use serde::Deserialize;
73
8#[builtin]4#[builtin]
9pub fn builtin_parse_json(str: IStr) -> Result<Val> {5pub fn builtin_parse_json(str: IStr) -> Result<Val> {
10 let value: Val = serde_json::from_str(&str)6 let value: Val =
11 .map_err(|e| RuntimeError(format!("failed to parse json: {e}").into()))?;7 serde_json::from_str(&str).map_err(|e| runtime_error!("failed to parse json: {e}"))?;
12 Ok(value)8 Ok(value)
13}9}
1410
22 let mut out = vec![];18 let mut out = vec![];
23 for item in value {19 for item in value {
24 let val = Val::deserialize(item)20 let val =
25 .map_err(|e| RuntimeError(format!("failed to parse yaml: {e}").into()))?;21 Val::deserialize(item).map_err(|e| runtime_error!("failed to parse yaml: {e}"))?;
26 out.push(val);22 out.push(val);
27 }23 }
28 Ok(if out.is_empty() {24 Ok(if out.is_empty() {
modifiedcrates/jrsonnet-stdlib/src/sort.rsdiffbeforeafterboth
3use std::cmp::Ordering;3use std::cmp::Ordering;
44
5use jrsonnet_evaluator::{5use jrsonnet_evaluator::{
6 error::Result,6 bail,
7 function::{builtin, FuncVal},7 function::{builtin, FuncVal},
8 operator::evaluate_compare_op,8 operator::evaluate_compare_op,
9 throw,
10 val::{equals, ArrValue},9 val::{equals, ArrValue},
11 Thunk, Val,10 Result, Thunk, Val,
12};11};
13use jrsonnet_parser::BinaryOpType;12use jrsonnet_parser::BinaryOpType;
1413
44 (Val::Num(_), SortKeyType::Unknown) => sort_type = SortKeyType::Number,43 (Val::Num(_), SortKeyType::Unknown) => sort_type = SortKeyType::Number,
45 (Val::Str(_), SortKeyType::String) | (Val::Num(_), SortKeyType::Number) => {}44 (Val::Str(_), SortKeyType::String) | (Val::Num(_), SortKeyType::Number) => {}
46 (Val::Str(_) | Val::Num(_), _) => {45 (Val::Str(_) | Val::Num(_), _) => {
47 throw!("sort elements should have the same types")46 bail!("sort elements should have the same types")
48 }47 }
49 _ => {}48 _ => {}
50 }49 }
modifiedcrates/jrsonnet-stdlib/src/strings.rsdiffbeforeafterboth
1use jrsonnet_evaluator::{1use jrsonnet_evaluator::{
2 bail,
2 error::{ErrorKind::*, Result},3 error::{ErrorKind::*, Result},
3 function::builtin,4 function::builtin,
4 throw,
5 typed::{Either2, M1},5 typed::{Either2, M1},
6 val::{ArrValue, StrValue},6 val::{ArrValue, StrValue},
7 Either, IStr, Val,7 Either, IStr, Val,
91pub fn builtin_parse_int(str: IStr) -> Result<f64> {91pub fn builtin_parse_int(str: IStr) -> Result<f64> {
92 if let Some(raw) = str.strip_prefix('-') {92 if let Some(raw) = str.strip_prefix('-') {
93 if raw.is_empty() {93 if raw.is_empty() {
94 throw!("integer only consists of a minus")94 bail!("integer only consists of a minus")
95 }95 }
9696
97 parse_nat::<10>(raw).map(|value| -value)97 parse_nat::<10>(raw).map(|value| -value)
98 } else {98 } else {
99 if str.is_empty() {99 if str.is_empty() {
100 throw!("empty integer")100 bail!("empty integer")
101 }101 }
102102
103 parse_nat::<10>(str.as_str())103 parse_nat::<10>(str.as_str())
107#[builtin]107#[builtin]
108pub fn builtin_parse_octal(str: IStr) -> Result<f64> {108pub fn builtin_parse_octal(str: IStr) -> Result<f64> {
109 if str.is_empty() {109 if str.is_empty() {
110 throw!("empty octal integer");110 bail!("empty octal integer");
111 }111 }
112112
113 parse_nat::<8>(str.as_str())113 parse_nat::<8>(str.as_str())
116#[builtin]116#[builtin]
117pub fn builtin_parse_hex(str: IStr) -> Result<f64> {117pub fn builtin_parse_hex(str: IStr) -> Result<f64> {
118 if str.is_empty() {118 if str.is_empty() {
119 throw!("empty hexadecimal integer");119 bail!("empty hexadecimal integer");
120 }120 }
121121
122 parse_nat::<16>(str.as_str())122 parse_nat::<16>(str.as_str())
156 if digit < BASE {156 if digit < BASE {
157 Ok(base * aggregate + digit as f64)157 Ok(base * aggregate + digit as f64)
158 } else {158 } else {
159 throw!("{raw:?} is not a base {BASE} integer",);159 bail!("{raw:?} is not a base {BASE} integer");
160 }160 }
161 })161 })
162}162}
163163
164#[cfg(feature = "exp-bigint")]164#[cfg(feature = "exp-bigint")]
165#[builtin]165#[builtin]
166pub fn builtin_bigint(v: Either![f64, IStr]) -> Result<Val> {166pub fn builtin_bigint(v: Either![f64, IStr]) -> Result<Val> {
167 use jrsonnet_evaluator::runtime_error;
167 use Either2::*;168 use Either2::*;
168 Ok(match v {169 Ok(match v {
169 A(a) => Val::BigInt(Box::new((a as i64).into())),170 A(a) => Val::BigInt(Box::new((a as i64).into())),
170 B(b) => Val::BigInt(Box::new(171 B(b) => Val::BigInt(Box::new(
171 b.as_str()172 b.as_str()
172 .parse()173 .parse()
173 .map_err(|e| RuntimeError(format!("bad bigint: {e}").into()))?,174 .map_err(|e| runtime_error!("bad bigint: {e}"))?,
174 )),175 )),
175 })176 })
176}177}
modifiedtests/tests/as_native.rsdiffbeforeafterboth
1use jrsonnet_evaluator::{error::Result, State};1use jrsonnet_evaluator::{Result, State};
2use jrsonnet_stdlib::StateExt;2use jrsonnet_stdlib::StateExt;
33
4mod common;4mod common;
modifiedtests/tests/builtin.rsdiffbeforeafterboth
1mod common;1mod common;
22
3use jrsonnet_evaluator::{3use jrsonnet_evaluator::{
4 error::Result,
5 function::{builtin, builtin::Builtin, CallLocation, FuncVal},4 function::{builtin, builtin::Builtin, CallLocation, FuncVal},
6 typed::Typed,5 typed::Typed,
7 ContextBuilder, State, Thunk, Val,6 ContextBuilder, Result, State, Thunk, Val,
8};7};
9use jrsonnet_stdlib::StateExt;8use jrsonnet_stdlib::StateExt;
109
modifiedtests/tests/common.rsdiffbeforeafterboth
1use jrsonnet_evaluator::{1use jrsonnet_evaluator::{
2 error::Result,2 bail,
3 function::{builtin, FuncVal},3 function::{builtin, FuncVal},
4 throw, ObjValueBuilder, State, Thunk, Val,4 ObjValueBuilder, Result, State, Thunk, Val,
5};5};
66
7#[macro_export]7#[macro_export]
10 let a = &$a;10 let a = &$a;
11 let b = &$b;11 let b = &$b;
12 if a != b {12 if a != b {
13 ::jrsonnet_evaluator::throw!("assertion failed: a != b\na={:#?}\nb={:#?}", a, b)13 ::jrsonnet_evaluator::bail!("assertion failed: a != b\na={a:#?}\nb={b:#?}")
14 }14 }
15 }};15 }};
16}16}
19macro_rules! ensure {19macro_rules! ensure {
20 ($v:expr $(,)?) => {20 ($v:expr $(,)?) => {
21 if !$v {21 if !$v {
22 ::jrsonnet_evaluator::throw!("assertion failed: {}", stringify!($v))22 ::jrsonnet_evaluator::bail!("assertion failed: {}", stringify!($v))
23 }23 }
24 };24 };
25}25}
29 ($a:expr, $b:expr) => {{29 ($a:expr, $b:expr) => {{
30 if !::jrsonnet_evaluator::val::equals(&$a.clone(), &$b.clone())? {30 if !::jrsonnet_evaluator::val::equals(&$a.clone(), &$b.clone())? {
31 use ::jrsonnet_evaluator::manifest::JsonFormat;31 use ::jrsonnet_evaluator::manifest::JsonFormat;
32 ::jrsonnet_evaluator::throw!(32 ::jrsonnet_evaluator::bail!(
33 "assertion failed: a != b\na={:#?}\nb={:#?}",33 "assertion failed: a != b\na={:#?}\nb={:#?}",
34 $a.manifest(JsonFormat::default())?,34 $a.manifest(JsonFormat::default())?,
35 $b.manifest(JsonFormat::default())?,35 $b.manifest(JsonFormat::default())?,
42fn assert_throw(lazy: Thunk<Val>, message: String) -> Result<bool> {42fn assert_throw(lazy: Thunk<Val>, message: String) -> Result<bool> {
43 match lazy.evaluate() {43 match lazy.evaluate() {
44 Ok(_) => {44 Ok(_) => {
45 throw!("expected argument to throw on evaluation, but it returned instead")45 bail!("expected argument to throw on evaluation, but it returned instead")
46 }46 }
47 Err(e) => {47 Err(e) => {
48 let error = format!("{}", e.error());48 let error = format!("{}", e.error());
modifiedtests/tests/sanity.rsdiffbeforeafterboth
1use jrsonnet_evaluator::{1use jrsonnet_evaluator::{
2 error::Result,2 bail,
3 throw,
4 trace::{CompactFormat, TraceFormat},3 trace::{CompactFormat, TraceFormat},
5 State, Val,4 Result, State, Val,
6};5};
7use jrsonnet_stdlib::StateExt;6use jrsonnet_stdlib::StateExt;
87
2928
30 {29 {
31 let Err(e) = s.evaluate_snippet("snip".to_owned(), "assert 1 == 2: 'fail'; null") else {30 let Err(e) = s.evaluate_snippet("snip".to_owned(), "assert 1 == 2: 'fail'; null") else {
32 throw!("assertion should fail");31 bail!("assertion should fail");
33 };32 };
34 let e = trace_format.format(&e).unwrap();33 let e = trace_format.format(&e).unwrap();
35 ensure!(e.starts_with("assert failed: fail\n"));34 ensure!(e.starts_with("assert failed: fail\n"));
36 }35 }
37 {36 {
38 let Err(e) = s.evaluate_snippet("snip".to_owned(), "std.assertEqual(1, 2)") else {37 let Err(e) = s.evaluate_snippet("snip".to_owned(), "std.assertEqual(1, 2)") else {
39 throw!("assertion should fail")38 bail!("assertion should fail")
40 };39 };
41 let e = trace_format.format(&e).unwrap();40 let e = trace_format.format(&e).unwrap();
42 ensure!(e.starts_with("runtime error: Assertion failed. 1 != 2"))41 ensure!(e.starts_with("runtime error: Assertion failed. 1 != 2"))
modifiedtests/tests/typed_obj.rsdiffbeforeafterboth
22
3use std::fmt::Debug;3use std::fmt::Debug;
44
5use jrsonnet_evaluator::{error::Result, typed::Typed, State};5use jrsonnet_evaluator::{typed::Typed, Result, State};
6use jrsonnet_stdlib::StateExt;6use jrsonnet_stdlib::StateExt;
77
8#[derive(Clone, Typed, PartialEq, Debug)]8#[derive(Clone, Typed, PartialEq, Debug)]