difftreelog
refactor do not expose direct operator evaluator access
in: master
7 files changed
crates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth1use std::{2 cell::RefCell,3 cmp::Ordering,4 fmt::{self, Debug, Display},5 marker::PhantomData,6 mem::replace,7 num::NonZeroU32,8 rc::Rc,9};1011use jrsonnet_gcmodule::{Acyclic, Cc, Trace, cc_dyn};12use jrsonnet_interner::IStr;13pub use jrsonnet_macros::Thunk;14use jrsonnet_types::ValType;15use rustc_hash::FxHashMap;1617pub use crate::arr::{ArrValue, ArrayLike};18use crate::{19 NumValue, ObjValue, Result, SupThis, Unbound, WeakSupThis, bail,20 error::{Error, ErrorKind::*},21 function::FuncVal,22 gc::WithCapacityExt as _,23 manifest::{ManifestFormat, ToStringFormat},24 typed::BoundedUsize,25};2627pub trait ThunkValue: Trace {28 type Output;29 fn get(&self) -> Result<Self::Output>;30}3132#[derive(Trace)]33enum MemoizedClusureThunkInner<D: Trace, T: Trace> {34 Computed(T),35 Errored(Error),36 Waiting {37 env: D,38 // Carries no data, as it is not a real closure, all the39 // captured environment is stored in `env` field.40 #[trace(skip)]41 closure: fn(D) -> Result<T>,42 },43 Pending,44}45#[derive(Trace)]46pub struct MemoizedClosureThunk<D: Trace, T: Trace>(RefCell<MemoizedClusureThunkInner<D, T>>);47impl<D: Trace, T: Trace> MemoizedClosureThunk<D, T> {48 pub fn new(env: D, closure: fn(D) -> Result<T>) -> Self {49 Self(RefCell::new(MemoizedClusureThunkInner::Waiting {50 env,51 closure,52 }))53 }54}5556impl<D: Trace, T: Trace + Clone> ThunkValue for MemoizedClosureThunk<D, T> {57 type Output = T;5859 fn get(&self) -> Result<Self::Output> {60 match &*self.0.borrow() {61 MemoizedClusureThunkInner::Computed(v) => return Ok(v.clone()),62 MemoizedClusureThunkInner::Errored(e) => return Err(e.clone()),63 MemoizedClusureThunkInner::Pending => return Err(InfiniteRecursionDetected.into()),64 MemoizedClusureThunkInner::Waiting { .. } => (),65 }66 let MemoizedClusureThunkInner::Waiting { env, closure } = replace(67 &mut *self.0.borrow_mut(),68 MemoizedClusureThunkInner::Pending,69 ) else {70 unreachable!();71 };72 let new_value = match closure(env) {73 Ok(v) => v,74 Err(e) => {75 *self.0.borrow_mut() = MemoizedClusureThunkInner::Errored(e.clone());76 return Err(e);77 }78 };79 *self.0.borrow_mut() = MemoizedClusureThunkInner::Computed(new_value.clone());80 Ok(new_value)81 }82}8384cc_dyn!(85 /// Lazily evaluated value86 #[derive(Clone)] Thunk<V: Trace>,87 ThunkValue<Output = V>,88 pub fn new() {...}89);9091impl<T: Trace> Thunk<T> {92 pub fn evaluated(val: T) -> Self93 where94 T: Clone,95 {96 #[derive(Trace)]97 struct EvaluatedThunk<T: Trace>(T);98 impl<T> ThunkValue for EvaluatedThunk<T>99 where100 T: Clone + Trace,101 {102 type Output = T;103104 fn get(&self) -> Result<Self::Output> {105 Ok(self.0.clone())106 }107 }108 Self::new(EvaluatedThunk(val))109 }110 pub fn errored(e: Error) -> Self {111 #[derive(Trace)]112 struct ErroredThunk<T: Trace>(Error, PhantomData<T>);113 impl<T> ThunkValue for ErroredThunk<T>114 where115 T: Trace,116 {117 type Output = T;118119 fn get(&self) -> Result<Self::Output> {120 Err(self.0.clone())121 }122 }123 Self::new(ErroredThunk(e, PhantomData))124 }125 pub fn result(res: Result<T, Error>) -> Self126 where127 T: Clone,128 {129 match res {130 Ok(o) => Self::evaluated(o),131 Err(e) => Self::errored(e),132 }133 }134}135136impl<T> Thunk<T>137where138 T: Trace,139{140 pub fn force(&self) -> Result<()> {141 self.evaluate()?;142 Ok(())143 }144145 /// Evaluate thunk, or return cached value146 ///147 /// # Errors148 ///149 /// - Lazy value evaluation returned error150 /// - This method was called during inner value evaluation151 pub fn evaluate(&self) -> Result<T> {152 self.0.get()153 }154}155156pub trait ThunkMapper<Input>: Trace {157 type Output;158 fn map(self, from: Input) -> Result<Self::Output>;159}160impl<Input> Thunk<Input>161where162 Input: Trace,163{164 pub fn map<M>(self, mapper: M) -> Thunk<M::Output>165 where166 M: ThunkMapper<Input>,167 M::Output: Trace + Clone,168 {169 let inner = self;170 Thunk!(move || {171 let value = inner.evaluate()?;172 let mapped = mapper.map(value)?;173 Ok(mapped)174 })175 }176}177178impl<T: Trace + Clone> From<Result<T>> for Thunk<T> {179 fn from(value: Result<T>) -> Self {180 match value {181 Ok(o) => Self::evaluated(o),182 Err(e) => Self::errored(e),183 }184 }185}186impl<T, V: Trace> From<T> for Thunk<V>187where188 T: ThunkValue<Output = V>,189{190 fn from(value: T) -> Self {191 Self::new(value)192 }193}194195impl<T: Trace + Default + Clone> Default for Thunk<T> {196 fn default() -> Self {197 Self::evaluated(T::default())198 }199}200201#[derive(Trace, Clone)]202pub struct CachedUnbound<I, T>203where204 I: Unbound<Bound = T>,205 T: Trace,206{207 cache: Cc<RefCell<FxHashMap<WeakSupThis, T>>>,208 value: I,209}210impl<I: Unbound<Bound = T>, T: Trace> CachedUnbound<I, T> {211 pub fn new(value: I) -> Self {212 Self {213 cache: Cc::new(RefCell::new(FxHashMap::new())),214 value,215 }216 }217}218impl<I: Unbound<Bound = T>, T: Clone + Trace> Unbound for CachedUnbound<I, T> {219 type Bound = T;220 fn bind(&self, sup_this: SupThis) -> Result<T> {221 let cache_key = sup_this.clone().downgrade();222 {223 if let Some(t) = self.cache.borrow().get(&cache_key) {224 return Ok(t.clone());225 }226 }227 let bound = self.value.bind(sup_this)?;228229 {230 let mut cache = self.cache.borrow_mut();231 cache.insert(cache_key, bound.clone());232 }233234 Ok(bound)235 }236}237238impl<T: Debug + Trace> Debug for Thunk<T> {239 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {240 write!(f, "Lazy")241 }242}243impl<T: Trace> PartialEq for Thunk<T> {244 fn eq(&self, other: &Self) -> bool {245 Cc::ptr_eq(&self.0, &other.0)246 }247}248249/// Represents a Jsonnet value, which can be sliced or indexed (string or array).250#[allow(clippy::module_name_repetitions)]251pub enum IndexableVal {252 /// String.253 Str(IStr),254 /// Array.255 Arr(ArrValue),256}257impl IndexableVal {258 pub fn is_empty(&self) -> bool {259 match self {260 Self::Str(s) => s.is_empty(),261 Self::Arr(s) => s.is_empty(),262 }263 }264265 pub fn to_array(self) -> ArrValue {266 match self {267 Self::Str(s) => s.chars().collect(),268 Self::Arr(arr) => arr,269 }270 }271 /// Slice the value.272 ///273 /// # Implementation274 ///275 /// For strings, will create a copy of specified interval.276 ///277 /// For arrays, nothing will be copied on this call, instead [`ArrValue::Slice`] view will be returned.278 pub fn slice(279 self,280 index: Option<i32>,281 end: Option<i32>,282 step: Option<BoundedUsize<1, { i32::MAX as usize }>>,283 ) -> Result<Self> {284 match &self {285 Self::Str(s) => {286 let mut computed_len = None;287 let mut get_len = || {288 computed_len.unwrap_or_else(|| {289 let len = s.chars().count();290 let _ = computed_len.insert(len);291 len292 })293 };294 let mut get_idx = |pos: Option<i32>, default| {295 match pos {296 #[expect(clippy::cast_sign_loss, reason = "abs value is used")]297 Some(v) if v < 0 => get_len().saturating_sub((-v as isize) as usize),298 // No need to clamp, as iterator interface is used299 #[expect(clippy::cast_sign_loss, reason = "abs value is used")]300 Some(v) => v as usize,301 None => default,302 }303 };304305 let index = get_idx(index, 0);306 let end = get_idx(end, usize::MAX);307 let step = step.as_deref().copied().unwrap_or(1);308309 if index >= end {310 return Ok(Self::Str("".into()));311 }312313 Ok(Self::Str(314 (s.chars()315 .skip(index)316 .take(end - index)317 .step_by(step)318 .collect::<String>())319 .into(),320 ))321 }322 Self::Arr(arr) => Ok(Self::Arr(arr.clone().slice(323 index,324 end,325 #[expect(326 clippy::cast_possible_truncation,327 reason = "overflow will result with skip too large which would be equivalent"328 )]329 step.map(|v| NonZeroU32::new(v.value() as u32).expect("bounded != 0")),330 ))),331 }332 }333}334335#[derive(Debug, Clone, Acyclic)]336pub enum StrValue {337 Flat(IStr),338 Tree(Rc<(StrValue, StrValue, usize)>),339}340impl StrValue {341 pub fn concat(a: Self, b: Self) -> Self {342 // TODO: benchmark for an optimal value, currently just a arbitrary choice343 const STRING_EXTEND_THRESHOLD: usize = 100;344345 if a.is_empty() {346 b347 } else if b.is_empty() {348 a349 } else if a.len() + b.len() < STRING_EXTEND_THRESHOLD {350 Self::Flat(format!("{a}{b}").into())351 } else {352 let len = a.len() + b.len();353 Self::Tree(Rc::new((a, b, len)))354 }355 }356 pub fn chunks(&self, c: &mut impl FnMut(&IStr)) {357 fn write_buf(s: &StrValue, c: &mut impl FnMut(&IStr)) {358 match s {359 StrValue::Flat(f) => c(f),360 StrValue::Tree(t) => {361 write_buf(&t.0, c);362 write_buf(&t.1, c);363 }364 }365 }366 write_buf(self, c);367 }368 pub fn into_flat(&self) -> IStr {369 fn write_buf(s: &StrValue, out: &mut String) {370 match s {371 StrValue::Flat(f) => out.push_str(f),372 StrValue::Tree(t) => {373 write_buf(&t.0, out);374 write_buf(&t.1, out);375 }376 }377 }378 match self {379 Self::Flat(f) => f.clone(),380 Self::Tree(_) => {381 let mut buf = String::with_capacity(self.len());382 write_buf(self, &mut buf);383 buf.into()384 }385 }386 }387 pub fn len(&self) -> usize {388 match self {389 Self::Flat(v) => v.len(),390 Self::Tree(t) => t.2,391 }392 }393 pub fn is_empty(&self) -> bool {394 match self {395 Self::Flat(v) => v.is_empty(),396 // Can't create non-flat empty string397 Self::Tree(_) => false,398 }399 }400}401impl<T> From<T> for StrValue402where403 IStr: From<T>,404{405 fn from(value: T) -> Self {406 Self::Flat(IStr::from(value))407 }408}409impl Display for StrValue {410 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {411 match self {412 Self::Flat(v) => write!(f, "{v}"),413 Self::Tree(t) => {414 write!(f, "{}", t.0)?;415 write!(f, "{}", t.1)416 }417 }418 }419}420impl PartialEq for StrValue {421 // False positive, into_flat returns not StrValue, but IStr, thus no infinite recursion here.422 #[allow(clippy::unconditional_recursion)]423 fn eq(&self, other: &Self) -> bool {424 let a = self.clone().into_flat();425 let b = other.clone().into_flat();426 a == b427 }428}429impl Eq for StrValue {}430impl PartialOrd for StrValue {431 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {432 Some(self.cmp(other))433 }434}435impl Ord for StrValue {436 fn cmp(&self, other: &Self) -> Ordering {437 let a = self.clone().into_flat();438 let b = other.clone().into_flat();439 a.cmp(&b)440 }441}442443/// Represents any valid Jsonnet value.444#[derive(Debug, Clone, Trace, Default)]445pub enum Val {446 /// Represents a Jsonnet boolean.447 Bool(bool),448 /// Represents a Jsonnet null value.449 #[default]450 Null,451 /// Represents a Jsonnet string.452 Str(StrValue),453 /// Represents a Jsonnet number.454 /// Should be finite, and not NaN455 /// This restriction isn't enforced by enum, as enum field can't be marked as private456 Num(NumValue),457 /// Experimental bigint458 #[cfg(feature = "exp-bigint")]459 BigInt(#[trace(skip)] Box<num_bigint::BigInt>),460 /// Represents a Jsonnet array.461 Arr(ArrValue),462 /// Represents a Jsonnet object.463 Obj(ObjValue),464 /// Represents a Jsonnet function.465 Func(FuncVal),466}467468#[cfg(target_pointer_width = "64")]469static_assertions::assert_eq_size!(Val, [u8; 24]);470471impl From<IndexableVal> for Val {472 fn from(v: IndexableVal) -> Self {473 match v {474 IndexableVal::Str(s) => Self::string(s),475 IndexableVal::Arr(a) => Self::Arr(a),476 }477 }478}479480impl Val {481 pub const fn as_bool(&self) -> Option<bool> {482 match self {483 Self::Bool(v) => Some(*v),484 _ => None,485 }486 }487 pub const fn as_null(&self) -> Option<()> {488 match self {489 Self::Null => Some(()),490 _ => None,491 }492 }493 pub fn as_str(&self) -> Option<IStr> {494 match self {495 Self::Str(s) => Some(s.clone().into_flat()),496 _ => None,497 }498 }499 pub const fn as_num(&self) -> Option<f64> {500 match self {501 Self::Num(n) => Some(n.get()),502 _ => None,503 }504 }505 #[cfg(feature = "exp-bigint")]506 pub fn as_bigint(&self) -> Option<num_bigint::BigInt> {507 match self {508 Self::BigInt(n) => Some(*n.clone()),509 _ => None,510 }511 }512 pub fn as_arr(&self) -> Option<ArrValue> {513 match self {514 Self::Arr(a) => Some(a.clone()),515 _ => None,516 }517 }518 pub fn as_obj(&self) -> Option<ObjValue> {519 match self {520 Self::Obj(o) => Some(o.clone()),521 _ => None,522 }523 }524 pub fn as_func(&self) -> Option<FuncVal> {525 match self {526 Self::Func(f) => Some(f.clone()),527 _ => None,528 }529 }530531 pub const fn value_type(&self) -> ValType {532 match self {533 Self::Str(..) => ValType::Str,534 Self::Num(..) => ValType::Num,535 #[cfg(feature = "exp-bigint")]536 Self::BigInt(..) => ValType::BigInt,537 Self::Arr(..) => ValType::Arr,538 Self::Obj(..) => ValType::Obj,539 Self::Bool(_) => ValType::Bool,540 Self::Null => ValType::Null,541 Self::Func(..) => ValType::Func,542 }543 }544545 pub fn manifest(&self, format: impl ManifestFormat) -> Result<String> {546 fn manifest_dyn(val: &Val, manifest: &dyn ManifestFormat) -> Result<String> {547 manifest.manifest(val.clone())548 }549 manifest_dyn(self, &format)550 }551552 pub fn to_string(&self) -> Result<IStr> {553 Ok(match self {554 Self::Bool(true) => "true".into(),555 Self::Bool(false) => "false".into(),556 Self::Null => "null".into(),557 Self::Str(s) => s.clone().into_flat(),558 _ => self.manifest(ToStringFormat).map(IStr::from)?,559 })560 }561562 pub fn into_indexable(self) -> Result<IndexableVal> {563 Ok(match self {564 Self::Str(s) => IndexableVal::Str(s.into_flat()),565 Self::Arr(arr) => IndexableVal::Arr(arr),566 _ => bail!(ValueIsNotIndexable(self.value_type())),567 })568 }569570 pub fn function(function: impl Into<FuncVal>) -> Self {571 Self::Func(function.into())572 }573 pub fn string(string: impl Into<StrValue>) -> Self {574 Self::Str(string.into())575 }576 pub fn num(num: impl Into<NumValue>) -> Self {577 Self::Num(num.into())578 }579 pub fn try_num<V, E>(num: V) -> Result<Self, E>580 where581 NumValue: TryFrom<V, Error = E>,582 {583 Ok(Self::Num(num.try_into()?))584 }585 pub fn arr(a: impl ArrayLike) -> Self {586 Self::Arr(ArrValue::new(a))587 }588}589590impl From<IStr> for Val {591 fn from(value: IStr) -> Self {592 Self::string(value)593 }594}595impl From<String> for Val {596 fn from(value: String) -> Self {597 Self::string(value)598 }599}600impl From<&str> for Val {601 fn from(value: &str) -> Self {602 Self::string(value)603 }604}605impl From<ObjValue> for Val {606 fn from(value: ObjValue) -> Self {607 Self::Obj(value)608 }609}610611const fn is_function_like(val: &Val) -> bool {612 matches!(val, Val::Func(_))613}614615/// Native implementation of `std.primitiveEquals`616pub fn primitive_equals(val_a: &Val, val_b: &Val) -> Result<bool> {617 Ok(match (val_a, val_b) {618 (Val::Bool(a), Val::Bool(b)) => a == b,619 (Val::Null, Val::Null) => true,620 (Val::Str(a), Val::Str(b)) => a == b,621 (Val::Num(a), Val::Num(b)) => (a.get() - b.get()).abs() <= f64::EPSILON,622 #[cfg(feature = "exp-bigint")]623 (Val::BigInt(a), Val::BigInt(b)) => a == b,624 (Val::Arr(_), Val::Arr(_)) => {625 bail!("primitiveEquals operates on primitive types, got array")626 }627 (Val::Obj(_), Val::Obj(_)) => {628 bail!("primitiveEquals operates on primitive types, got object")629 }630 (a, b) if is_function_like(a) && is_function_like(b) => {631 bail!("cannot test equality of functions")632 }633 (_, _) => false,634 })635}636637/// Native implementation of `std.equals`638pub fn equals(val_a: &Val, val_b: &Val) -> Result<bool> {639 if val_a.value_type() != val_b.value_type() {640 return Ok(false);641 }642 match (val_a, val_b) {643 (Val::Arr(a), Val::Arr(b)) => {644 if ArrValue::ptr_eq(a, b) {645 return Ok(true);646 }647 if a.len() != b.len() {648 return Ok(false);649 }650 for (a, b) in a.iter().zip(b.iter()) {651 if !equals(&a?, &b?)? {652 return Ok(false);653 }654 }655 Ok(true)656 }657 (Val::Obj(a), Val::Obj(b)) => {658 if ObjValue::ptr_eq(a, b) {659 return Ok(true);660 }661 let fields = a.fields(662 #[cfg(feature = "exp-preserve-order")]663 false,664 );665 if fields666 != b.fields(667 #[cfg(feature = "exp-preserve-order")]668 false,669 ) {670 return Ok(false);671 }672 for field in fields {673 if !equals(674 &a.get(field.clone())?.expect("field exists"),675 &b.get(field)?.expect("field exists"),676 )? {677 return Ok(false);678 }679 }680 Ok(true)681 }682 (a, b) => Ok(primitive_equals(a, b)?),683 }684}crates/jrsonnet-stdlib/Cargo.tomldiffbeforeafterboth--- a/crates/jrsonnet-stdlib/Cargo.toml
+++ b/crates/jrsonnet-stdlib/Cargo.toml
@@ -16,17 +16,13 @@
# Bigint type
exp-bigint = ["dep:num-bigint", "jrsonnet-evaluator/exp-bigint"]
-exp-null-coaelse = [
- "jrsonnet-ir/exp-null-coaelse",
- "jrsonnet-evaluator/exp-null-coaelse",
-]
+exp-null-coaelse = ["jrsonnet-evaluator/exp-null-coaelse"]
# std.regexMatch and other helpers
exp-regex = ["dep:regex", "dep:lru", "dep:rustc-hash"]
[dependencies]
jrsonnet-evaluator.workspace = true
jrsonnet-macros.workspace = true
-jrsonnet-ir.workspace = true
jrsonnet-gcmodule.workspace = true
# Used for std.parseJson/std.parseYaml
crates/jrsonnet-stdlib/src/compat.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/compat.rs
+++ b/crates/jrsonnet-stdlib/src/compat.rs
@@ -1,19 +1,15 @@
use std::cmp::Ordering;
-use jrsonnet_evaluator::{
- Result, Val, function::builtin, operator::evaluate_compare_op, val::ArrValue,
-};
+use jrsonnet_evaluator::{function::builtin, val::ArrValue, Result, Val};
#[builtin]
#[allow(non_snake_case)]
pub fn builtin___compare(v1: Val, v2: Val) -> Result<i32> {
- Ok(
- match evaluate_compare_op(&v1, &v2, jrsonnet_ir::BinaryOpType::Lt)? {
- Ordering::Less => -1,
- Ordering::Equal => 0,
- Ordering::Greater => 1,
- },
- )
+ Ok(match Val::try_cmp(&v1, &v2)? {
+ Ordering::Less => -1,
+ Ordering::Equal => 0,
+ Ordering::Greater => 1,
+ })
}
#[builtin]
@@ -27,11 +23,7 @@
#[builtin]
#[allow(non_snake_case)]
pub fn $name(arr1: ArrValue, arr2: ArrValue) -> Result<bool> {
- let ordering = evaluate_compare_op(
- &Val::Arr(arr1),
- &Val::Arr(arr2),
- jrsonnet_ir::BinaryOpType::Lt,
- )?;
+ let ordering = Val::try_cmp(&Val::Arr(arr1), &Val::Arr(arr2))?;
Ok($operator.contains(&ordering))
}
};
crates/jrsonnet-stdlib/src/operator.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/operator.rs
+++ b/crates/jrsonnet-stdlib/src/operator.rs
@@ -2,18 +2,17 @@
//! However, in our case we instead implement them in native, and implement native functions on top of core for backwards compatibility
use jrsonnet_evaluator::{
- IStr, NumValue, Result, Val,
function::builtin,
- operator::evaluate_mod_op,
stdlib::std_format,
typed::{Either, Either2},
val::{equals, primitive_equals},
+ IStr, NumValue, Result, Val,
};
#[builtin]
pub fn builtin_mod(a: Either![NumValue, IStr], b: Val) -> Result<Val> {
use Either2::*;
- evaluate_mod_op(
+ Val::try_mod(
&match a {
A(v) => Val::Num(v),
B(s) => Val::string(s),
crates/jrsonnet-stdlib/src/sets.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/sets.rs
+++ b/crates/jrsonnet-stdlib/src/sets.rs
@@ -1,9 +1,6 @@
use std::cmp::Ordering;
-use jrsonnet_evaluator::{
- Result, Thunk, Val, function::builtin, operator::evaluate_compare_op, val::ArrValue,
-};
-use jrsonnet_ir::BinaryOpType;
+use jrsonnet_evaluator::{function::builtin, val::ArrValue, Result, Thunk, Val};
use crate::keyf::KeyF;
@@ -16,9 +13,9 @@
let x = keyF.eval(x)?;
while low < high {
- let middle = usize::midpoint(high, low);
+ let middle = u32::midpoint(high, low);
let comp = keyF.eval(arr.get_lazy(middle).expect("in bounds"))?;
- match evaluate_compare_op(&comp, &x, BinaryOpType::Lt)? {
+ match Val::try_cmp(&comp, &x)? {
Ordering::Less => low = middle + 1,
Ordering::Equal => return Ok(true),
Ordering::Greater => high = middle,
@@ -46,7 +43,7 @@
let mut out = Vec::new();
while let (Some(ac), Some(bc)) = (&ak, &bk) {
- match evaluate_compare_op(ac, bc, BinaryOpType::Lt)? {
+ match Val::try_cmp(ac, bc)? {
Ordering::Less => {
av = a.next();
ak = av.clone().map(keyF).transpose()?;
@@ -86,7 +83,7 @@
let mut out = Vec::new();
while let (Some(ac), Some(bc)) = (&ak, &bk) {
- match evaluate_compare_op(ac, bc, BinaryOpType::Lt)? {
+ match Val::try_cmp(ac, bc)? {
Ordering::Less => {
// In a, but not in b
out.push(av.clone().expect("ak != None"));
@@ -133,7 +130,7 @@
let mut out = Vec::new();
while let (Some(ac), Some(bc)) = (&ak, &bk) {
- match evaluate_compare_op(ac, bc, BinaryOpType::Lt)? {
+ match Val::try_cmp(ac, bc)? {
Ordering::Less => {
out.push(av.clone().expect("ak != None"));
av = a.next();
crates/jrsonnet-stdlib/src/sort.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/sort.rs
+++ b/crates/jrsonnet-stdlib/src/sort.rs
@@ -3,12 +3,11 @@
use std::cmp::Ordering;
use jrsonnet_evaluator::{
- Result, Thunk, Val, bail,
+ bail,
function::builtin,
- operator::evaluate_compare_op,
- val::{ArrValue, equals},
+ val::{equals, ArrValue},
+ Result, Thunk, Val,
};
-use jrsonnet_ir::BinaryOpType;
use crate::{eval_on_empty, keyf::KeyF};
@@ -53,7 +52,7 @@
let mut err = None;
// evaluate_compare_op will never return equal on types, which are different from
// jsonnet perspective
- values.sort_unstable_by(|a, b| match evaluate_compare_op(a, b, BinaryOpType::Lt) {
+ values.sort_unstable_by(|a, b| match Val::try_cmp(a, b) {
Ok(ord) => ord,
Err(e) if err.is_none() => {
let _ = err.insert(e);
@@ -71,7 +70,7 @@
fn sort_keyf(values: ArrValue, keyf: KeyF) -> Result<Vec<Thunk<Val>>> {
// Slow path, user provided key getter
- let mut vk = Vec::with_capacity(values.len());
+ let mut vk = Vec::with_capacity(values.len() as usize);
for value in values.iter_lazy() {
vk.push((value.clone(), keyf.eval(value)?));
}
@@ -89,16 +88,14 @@
let mut err = None;
// evaluate_compare_op will never return equal on types, which are different from
// jsonnet perspective
- vk.sort_by(
- |(_a, ak), (_b, bk)| match evaluate_compare_op(ak, bk, BinaryOpType::Lt) {
- Ok(ord) => ord,
- Err(e) if err.is_none() => {
- let _ = err.insert(e);
- Ordering::Equal
- }
- Err(_) => Ordering::Equal,
- },
- );
+ vk.sort_by(|(_a, ak), (_b, bk)| match Val::try_cmp(ak, bk) {
+ Ok(ord) => ord,
+ Err(e) if err.is_none() => {
+ let _ = err.insert(e);
+ Ordering::Equal
+ }
+ Err(_) => Ordering::Equal,
+ });
if let Some(err) = err {
return Err(err);
}
@@ -195,7 +192,7 @@
for item in iter {
let cur = item?;
let cur_key = keyf.eval(Thunk::evaluated(cur.clone()))?;
- if evaluate_compare_op(&cur_key, &min_key, BinaryOpType::Lt)? == ordering {
+ if Val::try_cmp(&cur_key, &min_key)? == ordering {
min = cur;
min_key = cur_key;
}
crates/jrsonnet-stdlib/src/strings.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/strings.rs
+++ b/crates/jrsonnet-stdlib/src/strings.rs
@@ -26,7 +26,7 @@
#[builtin]
pub fn builtin_str_replace(str: String, from: IStr, to: IStr) -> Result<String> {
if from.is_empty() {
- bail!("'from' string must not be zero length");
+ bail!("`from` string must not be zero length");
}
Ok(str.replace(&from as &str, &to as &str))
}