difftreelog
refactor error helpers
in: master
38 files changed
bindings/jsonnet/src/import.rsdiffbeforeafterboth13};13};141415use 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 }858686 let found_here_raw = unsafe { CStr::from_ptr(found_here) };87 let found_here_raw = unsafe { CStr::from_ptr(found_here) };bindings/jsonnet/src/lib.rsdiffbeforeafterboth19};19};202021use 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};249249250fn 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(336336337fn 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() {cmds/jrsonnet/src/main.rsdiffbeforeafterboth7use 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};141415#[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 )crates/jrsonnet-evaluator/src/ctx.rsdiffbeforeafterboth54 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;565657 use crate::throw;57 use crate::bail;585859 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));727273 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 ))crates/jrsonnet-evaluator/src/dynamic.rsdiffbeforeafterboth223use jrsonnet_gcmodule::{Cc, Trace};3use jrsonnet_gcmodule::{Cc, Trace};445use crate::{error::ErrorKind::InfiniteRecursionDetected, throw, val::ThunkValue, Result, Thunk};5use crate::{bail, error::ErrorKind::InfiniteRecursionDetected, val::ThunkValue, Result};667// TODO: Replace with OnceCell once in std7// TODO: Replace with OnceCell once in std8#[derive(Clone, Trace)]8#[derive(Clone, Trace)]414142 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 }crates/jrsonnet-evaluator/src/error.rsdiffbeforeafterboth370}370}371371372#[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}384385#[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}387391crates/jrsonnet-evaluator/src/evaluate/destructure.rsdiffbeforeafterboth3use jrsonnet_parser::{BindSpec, Destruct, LocExpr, ParamsDesc};3use jrsonnet_parser::{BindSpec, Destruct, LocExpr, ParamsDesc};445use 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 }crates/jrsonnet-evaluator/src/evaluate/mod.rsdiffbeforeafterboth11use 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}381381393 || "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)?;463463464 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());485485486 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());504504505 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 indexable612 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();crates/jrsonnet-evaluator/src/evaluate/operator.rsdiffbeforeafterboth445use 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}272749 (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())),192192193 _ => 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(),crates/jrsonnet-evaluator/src/function/arglike.rsdiffbeforeafterboth3use jrsonnet_interner::IStr;3use jrsonnet_interner::IStr;4use jrsonnet_parser::{ArgsDesc, LocExpr};4use jrsonnet_parser::{ArgsDesc, LocExpr};556use 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};14715/// Marker for arguments, which can be evaluated with context set to None8/// Marker for arguments, which can be evaluated with context set to Nonecrates/jrsonnet-evaluator/src/function/builtin.rsdiffbeforeafterboth4use jrsonnet_interner::IStr;4use jrsonnet_interner::IStr;556use 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};889/// Can't have str | IStr, because constant BuiltinParam causes9/// Can't have str | IStr, because constant BuiltinParam causes10/// E0492: constant functions cannot refer to interior mutable data10/// E0492: constant functions cannot refer to interior mutable datacrates/jrsonnet-evaluator/src/function/parse.rsdiffbeforeafterboth667use 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 check73 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 params165 .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 params213 .iter()213 .iter()crates/jrsonnet-evaluator/src/import.rsdiffbeforeafterboth12use jrsonnet_parser::{SourceDirectory, SourceFile, SourcePath};12use jrsonnet_parser::{SourceDirectory, SourceFile, SourcePath};131314use crate::{14use crate::{15 bail,15 error::{ErrorKind::*, Result},16 error::{ErrorKind::*, Result},16 throw,17};17};181819/// 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 value26 /// may result in panic26 /// may result in panic27 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 things34 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 }373738 /// Load resolved file38 /// Load resolved file110 )));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 };crates/jrsonnet-evaluator/src/integrations/serde.rsdiffbeforeafterboth620 where620 where621 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}626626crates/jrsonnet-evaluator/src/lib.rsdiffbeforeafterboth354 }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 toocrates/jrsonnet-evaluator/src/manifest.rsdiffbeforeafterboth1use std::{borrow::Cow, fmt::Write};1use std::{borrow::Cow, fmt::Write};223use crate::{3use crate::{bail, Result, State, Val};4 error::{ErrorKind::*, Result},5 throw, State, Val,6};748pub 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 )crates/jrsonnet-evaluator/src/obj.rsdiffbeforeafterboth131314use 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(())crates/jrsonnet-evaluator/src/stdlib/format.rsdiffbeforeafterboth7use thiserror::Error;7use thiserror::Error;889use 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::Null684 } 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 }697697698 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 object728 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::Null767 } 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 vcrates/jrsonnet-evaluator/src/stdlib/mod.rsdiffbeforeafterboth334use format::{format_arr, format_obj};4use format::{format_arr, format_obj};556use crate::{error::Result, function::CallLocation, State, Val};6use crate::{function::CallLocation, Result, State, Val};778pub mod format;8pub mod format;99crates/jrsonnet-evaluator/src/typed/conversions.rsdiffbeforeafterboth778use 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};171618#[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 )253252254 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);650649651 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 }654653655 fn from_untyped(untyped: Val) -> Result<Self> {654 fn from_untyped(untyped: Val) -> Result<Self> {crates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth111112pub 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 }462463495 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 })crates/jrsonnet-macros/src/lib.rsdiffbeforeafterboth356 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] = &[crates/jrsonnet-stdlib/src/arrays.rsdiffbeforeafterboth1#![allow(non_snake_case)]1#![allow(non_snake_case)]223use 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};111112pub(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}191939 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 }160160175 } 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 }181181crates/jrsonnet-stdlib/src/compat.rsdiffbeforeafterboth1use std::cmp::Ordering;1use std::cmp::Ordering;223use jrsonnet_evaluator::{error::Result, function::builtin, operator::evaluate_compare_op, Val};3use jrsonnet_evaluator::{function::builtin, operator::evaluate_compare_op, Result, Val};445#[builtin]5#[builtin]6#[allow(non_snake_case)]6#[allow(non_snake_case)]crates/jrsonnet-stdlib/src/encoding.rsdiffbeforeafterboth1use 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};889#[builtin]9#[builtin]131314#[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}201821#[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(STANDARD33 .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 = STANDARD42 .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}4644crates/jrsonnet-stdlib/src/manifest/mod.rsdiffbeforeafterboth2mod yaml;2mod yaml;334use 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;crates/jrsonnet-stdlib/src/manifest/toml.rsdiffbeforeafterboth1use std::borrow::Cow;1use std::borrow::Cow;223use 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}crates/jrsonnet-stdlib/src/manifest/yaml.rsdiffbeforeafterboth1use std::{borrow::Cow, fmt::Write};1use std::{borrow::Cow, fmt::Write};223use 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};788pub 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}crates/jrsonnet-stdlib/src/misc.rsdiffbeforeafterboth1use std::{cell::RefCell, rc::Rc};1use std::{cell::RefCell, rc::Rc};223use 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 true104 }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}109109129 true129 true130 }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}135135crates/jrsonnet-stdlib/src/operator.rsdiffbeforeafterboth2//! 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 compatibility334use 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};131214#[builtin]13#[builtin]crates/jrsonnet-stdlib/src/parse.rsdiffbeforeafterboth1use 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;738#[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}141022 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() {crates/jrsonnet-stdlib/src/sort.rsdiffbeforeafterboth3use std::cmp::Ordering;3use std::cmp::Ordering;445use 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;141344 (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 }crates/jrsonnet-stdlib/src/strings.rsdiffbeforeafterboth1use 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 }969697 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 }102102103 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 }112112113 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 }121121122 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}163163164#[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}tests/tests/as_native.rsdiffbeforeafterboth1use jrsonnet_evaluator::{error::Result, State};1use jrsonnet_evaluator::{Result, State};2use jrsonnet_stdlib::StateExt;2use jrsonnet_stdlib::StateExt;334mod common;4mod common;tests/tests/builtin.rsdiffbeforeafterboth1mod common;1mod common;223use 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;109tests/tests/common.rsdiffbeforeafterboth1use 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};667#[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());tests/tests/sanity.rsdiffbeforeafterboth1use 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;87292830 {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"))tests/tests/typed_obj.rsdiffbeforeafterboth223use std::fmt::Debug;3use std::fmt::Debug;445use jrsonnet_evaluator::{error::Result, typed::Typed, State};5use jrsonnet_evaluator::{typed::Typed, Result, State};6use jrsonnet_stdlib::StateExt;6use jrsonnet_stdlib::StateExt;778#[derive(Clone, Typed, PartialEq, Debug)]8#[derive(Clone, Typed, PartialEq, Debug)]