difftreelog
style fix clippy warnings
in: master
16 files changed
cmds/jrsonnet/src/main.rsdiffbeforeafterboth--- a/cmds/jrsonnet/src/main.rs
+++ b/cmds/jrsonnet/src/main.rs
@@ -5,7 +5,7 @@
use clap::{CommandFactory, Parser};
use clap_complete::Shell;
-use jrsonnet_cli::{ManifestOpts, OutputOpts, TraceOpts, MiscOpts, TlaOpts, StdOpts, GcOpts};
+use jrsonnet_cli::{GcOpts, ManifestOpts, MiscOpts, OutputOpts, StdOpts, TlaOpts, TraceOpts};
use jrsonnet_evaluator::{
apply_tla,
error::{Error as JrError, ErrorKind},
@@ -133,16 +133,14 @@
fn main_catch(opts: Opts) -> bool {
let s = State::default();
- let trace = opts
- .trace
- .trace_format();
+ let trace = opts.trace.trace_format();
if let Err(e) = main_real(&s, opts) {
if let Error::Evaluation(e) = e {
let mut out = String::new();
trace.write_trace(&mut out, &e).expect("format error");
eprintln!("{out}")
} else {
- eprintln!("{}", e);
+ eprintln!("{e}");
}
return false;
}
@@ -150,7 +148,7 @@
}
fn main_real(s: &State, opts: Opts) -> Result<(), Error> {
- let _gc_leak_guard= opts.gc.leak_on_exit();
+ let _gc_leak_guard = opts.gc.leak_on_exit();
let _gc_print_stats = opts.gc.stats_printer();
let _stack_depth_override = opts.misc.stack_size_override();
@@ -220,7 +218,7 @@
} else {
let output = val.manifest(manifest_format)?;
if !output.is_empty() {
- println!("{}", output);
+ println!("{output}");
}
}
crates/jrsonnet-cli/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-cli/src/lib.rs
+++ b/crates/jrsonnet-cli/src/lib.rs
@@ -6,7 +6,10 @@
use std::{env, marker::PhantomData, path::PathBuf};
use clap::Parser;
-use jrsonnet_evaluator::{error::Result, stack::{set_stack_depth_limit, StackDepthLimitOverrideGuard, limit_stack_depth}, FileImportResolver, State, ImportResolver};
+use jrsonnet_evaluator::{
+ stack::{limit_stack_depth, StackDepthLimitOverrideGuard},
+ FileImportResolver,
+};
use jrsonnet_gcmodule::with_thread_object_space;
pub use manifest::*;
pub use stdlib::*;
@@ -71,6 +74,7 @@
}
impl GcOpts {
pub fn stats_printer(&self) -> Option<GcStatsPrinter> {
+ #[allow(clippy::unnecessary_lazy_evaluations/*, reason = "GcStatsPrinter has side-effect on Drop"*/)]
self.gc_print_stats.then(|| GcStatsPrinter {
collect_before_printing_stats: self.gc_collect_before_printing_stats,
})
@@ -96,7 +100,7 @@
eprintln!("=== GC STATS ===");
if self.collect_before_printing_stats {
let collected = jrsonnet_gcmodule::collect_thread_cycles();
- eprintln!("Collected: {}", collected);
+ eprintln!("Collected: {collected}");
}
eprintln!("Tracked: {}", jrsonnet_gcmodule::count_thread_tracked())
}
crates/jrsonnet-cli/src/manifest.rsdiffbeforeafterboth--- a/crates/jrsonnet-cli/src/manifest.rs
+++ b/crates/jrsonnet-cli/src/manifest.rs
@@ -1,10 +1,8 @@
use std::path::PathBuf;
use clap::{Parser, ValueEnum};
-use jrsonnet_evaluator::{
- error::Result,
- manifest::{JsonFormat, ManifestFormat, StringFormat, ToStringFormat, YamlStreamFormat},
- State,
+use jrsonnet_evaluator::manifest::{
+ JsonFormat, ManifestFormat, StringFormat, ToStringFormat, YamlStreamFormat,
};
use jrsonnet_stdlib::{TomlFormat, YamlFormat};
crates/jrsonnet-cli/src/stdlib.rsdiffbeforeafterboth--- a/crates/jrsonnet-cli/src/stdlib.rs
+++ b/crates/jrsonnet-cli/src/stdlib.rs
@@ -1,7 +1,7 @@
use std::{fs::read_to_string, str::FromStr};
use clap::Parser;
-use jrsonnet_evaluator::{error::Result, tb, trace::PathResolver, State};
+use jrsonnet_evaluator::{error::Result, trace::PathResolver, State};
use jrsonnet_stdlib::ContextInitializer;
#[derive(Clone)]
@@ -49,7 +49,7 @@
name: out[0].into(),
value: content,
}),
- Err(e) => Err(format!("{}", e)),
+ Err(e) => Err(format!("{e}")),
}
}
}
@@ -86,8 +86,7 @@
if self.no_stdlib {
return Ok(None);
}
- let ctx =
- ContextInitializer::new(s.clone(), PathResolver::new_cwd_fallback());
+ let ctx = ContextInitializer::new(s.clone(), PathResolver::new_cwd_fallback());
for ext in self.ext_str.iter() {
ctx.add_ext_str((&ext.name as &str).into(), (&ext.value as &str).into());
}
crates/jrsonnet-cli/src/tla.rsdiffbeforeafterboth--- a/crates/jrsonnet-cli/src/tla.rs
+++ b/crates/jrsonnet-cli/src/tla.rs
@@ -3,7 +3,7 @@
error::{ErrorKind, Result},
function::TlaArg,
gc::GcHashMap,
- IStr, State,
+ IStr,
};
use jrsonnet_parser::{ParserSettings, Source};
crates/jrsonnet-cli/src/trace.rsdiffbeforeafterboth--- a/crates/jrsonnet-cli/src/trace.rs
+++ b/crates/jrsonnet-cli/src/trace.rs
@@ -1,9 +1,5 @@
use clap::{Parser, ValueEnum};
-use jrsonnet_evaluator::{
- error::Result,
- trace::{CompactFormat, ExplainingFormat, PathResolver, TraceFormat},
- State,
-};
+use jrsonnet_evaluator::trace::{CompactFormat, ExplainingFormat, PathResolver, TraceFormat};
#[derive(PartialEq, Eq, ValueEnum, Clone)]
pub enum TraceFormatName {
crates/jrsonnet-evaluator/src/error.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/error.rs
+++ b/crates/jrsonnet-evaluator/src/error.rs
@@ -274,6 +274,7 @@
f.debug_tuple("LocError").field(&self.0).finish()
}
}
+impl std::error::Error for Error {}
pub trait ErrorSource {
fn to_location(self) -> Option<ExprLocation>;
crates/jrsonnet-evaluator/src/evaluate/operator.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/evaluate/operator.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate/operator.rs
@@ -104,28 +104,15 @@
}
}
} else {
- {
- let ai = a.iter();
- let bi = b.iter();
+ let ai = a.iter();
+ let bi = b.iter();
- for (a, b) in ai.zip(bi) {
- let ord = evaluate_compare_op(&a?, &b?, op)?;
- if !ord.is_eq() {
- return Ok(ord);
- }
+ for (a, b) in ai.zip(bi) {
+ let ord = evaluate_compare_op(&a?, &b?, op)?;
+ if !ord.is_eq() {
+ return Ok(ord);
}
}
- // {
- // let ai = a.iter_expl();
- // let bi = b.iter_expl();
-
- // for (a, b) in ai.zip(bi) {
- // let ord = evaluate_compare_op(&a?, &b?, op)?;
- // if !ord.is_eq() {
- // return Ok(ord);
- // }
- // }
- // }
}
a.len().cmp(&b.len())
}
crates/jrsonnet-evaluator/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/lib.rs
+++ b/crates/jrsonnet-evaluator/src/lib.rs
@@ -36,6 +36,7 @@
clippy::use_self,
// https://github.com/rust-lang/rust-clippy/issues/8539
clippy::iter_with_drain,
+ clippy::type_repetition_in_bounds,
// ci is being run with nightly, but library should work on stable
clippy::missing_const_for_fn,
)]
crates/jrsonnet-evaluator/src/obj.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/obj.rs
+++ b/crates/jrsonnet-evaluator/src/obj.rs
@@ -54,7 +54,7 @@
#[cfg(feature = "exp-preserve-order")]
mod ordering {
- use std::cmp::Reverse;
+ use std::cmp::{Ordering, Reverse};
use jrsonnet_gcmodule::Trace;
@@ -81,12 +81,10 @@
Self(Reverse(depth), index)
}
pub fn collide(self, other: Self) -> Self {
- if self.0 .0 > other.0 .0 {
- self
- } else if self.0 .0 < other.0 .0 {
- other
- } else {
- unreachable!("object can't have two fields with same name")
+ match self.0 .0.cmp(&other.0 .0) {
+ Ordering::Greater => self,
+ Ordering::Less => other,
+ Ordering::Equal => unreachable!("object can't have two fields with the same name"),
}
}
}
@@ -188,6 +186,12 @@
pub fn new_empty() -> Self {
Self::new(None, Cc::new(GcHashMap::new()), Cc::new(Vec::new()))
}
+ pub fn builder() -> ObjValueBuilder {
+ ObjValueBuilder::new()
+ }
+ pub fn builder_with_capacity(capacity: usize) -> ObjValueBuilder {
+ ObjValueBuilder::with_capacity(capacity)
+ }
#[must_use]
pub fn extend_from(&self, sup: Self) -> Self {
match &self.0.sup {
@@ -304,7 +308,7 @@
break;
}
fields[j] = fields[k].clone();
- j = k
+ j = k;
}
fields[j] = x;
}
crates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth1use std::{2 cell::RefCell,3 fmt::{self, Debug, Display},4 mem::replace,5 rc::Rc,6};78use jrsonnet_gcmodule::{Cc, Trace};9use jrsonnet_interner::IStr;10use jrsonnet_types::ValType;1112pub use crate::arr::ArrValue;13use crate::{14 error::{Error, ErrorKind::*},15 function::FuncVal,16 gc::{GcHashMap, TraceBox},17 manifest::{ManifestFormat, ToStringFormat},18 tb, throw,19 typed::BoundedUsize,20 ObjValue, Result, Unbound, WeakObjValue,21};2223pub trait ThunkValue: Trace {24 type Output;25 fn get(self: Box<Self>) -> Result<Self::Output>;26}2728#[derive(Trace)]29enum ThunkInner<T: Trace> {30 Computed(T),31 Errored(Error),32 Waiting(TraceBox<dyn ThunkValue<Output = T>>),33 Pending,34}3536#[allow(clippy::module_name_repetitions)]37#[derive(Clone, Trace)]38pub struct Thunk<T: Trace>(Cc<RefCell<ThunkInner<T>>>);3940impl<T: Trace> Thunk<T> {41 pub fn evaluated(val: T) -> Self {42 Self(Cc::new(RefCell::new(ThunkInner::Computed(val))))43 }44 pub fn new(f: impl ThunkValue<Output = T> + 'static) -> Self {45 Self(Cc::new(RefCell::new(ThunkInner::Waiting(tb!(f)))))46 }47 pub fn errored(e: Error) -> Self {48 Self(Cc::new(RefCell::new(ThunkInner::Errored(e))))49 }50}5152impl<T> Thunk<T>53where54 T: Clone + Trace,55{56 pub fn force(&self) -> Result<()> {57 self.evaluate()?;58 Ok(())59 }60 pub fn evaluate(&self) -> Result<T> {61 match &*self.0.borrow() {62 ThunkInner::Computed(v) => return Ok(v.clone()),63 ThunkInner::Errored(e) => return Err(e.clone()),64 ThunkInner::Pending => return Err(InfiniteRecursionDetected.into()),65 ThunkInner::Waiting(..) => (),66 };67 let ThunkInner::Waiting(value) = replace(&mut *self.0.borrow_mut(), ThunkInner::Pending) else {68 unreachable!();69 };70 let new_value = match value.0.get() {71 Ok(v) => v,72 Err(e) => {73 *self.0.borrow_mut() = ThunkInner::Errored(e.clone());74 return Err(e);75 }76 };77 *self.0.borrow_mut() = ThunkInner::Computed(new_value.clone());78 Ok(new_value)79 }80}8182type CacheKey = (Option<WeakObjValue>, Option<WeakObjValue>);8384#[derive(Trace, Clone)]85pub struct CachedUnbound<I, T>86where87 I: Unbound<Bound = T>,88 T: Trace,89{90 cache: Cc<RefCell<GcHashMap<CacheKey, T>>>,91 value: I,92}93impl<I: Unbound<Bound = T>, T: Trace> CachedUnbound<I, T> {94 pub fn new(value: I) -> Self {95 Self {96 cache: Cc::new(RefCell::new(GcHashMap::new())),97 value,98 }99 }100}101impl<I: Unbound<Bound = T>, T: Clone + Trace> Unbound for CachedUnbound<I, T> {102 type Bound = T;103 fn bind(&self, sup: Option<ObjValue>, this: Option<ObjValue>) -> Result<T> {104 let cache_key = (105 sup.as_ref().map(|s| s.clone().downgrade()),106 this.as_ref().map(|t| t.clone().downgrade()),107 );108 {109 if let Some(t) = self.cache.borrow().get(&cache_key) {110 return Ok(t.clone());111 }112 }113 let bound = self.value.bind(sup, this)?;114115 {116 let mut cache = self.cache.borrow_mut();117 cache.insert(cache_key, bound.clone());118 }119120 Ok(bound)121 }122}123124impl<T: Debug + Trace> Debug for Thunk<T> {125 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {126 write!(f, "Lazy")127 }128}129impl<T: Trace> PartialEq for Thunk<T> {130 fn eq(&self, other: &Self) -> bool {131 Cc::ptr_eq(&self.0, &other.0)132 }133}134135/// Represents a Jsonnet value, which can be spliced or indexed (string or array).136#[allow(clippy::module_name_repetitions)]137pub enum IndexableVal {138 /// String.139 Str(IStr),140 /// Array.141 Arr(ArrValue),142}143impl IndexableVal {144 /// Slice the value.145 ///146 /// # Implementation147 ///148 /// For strings, will create a copy of specified interval.149 ///150 /// For arrays, nothing will be copied on this call, instead [`ArrValue::Slice`] view will be returned.151 pub fn slice(152 self,153 index: Option<BoundedUsize<0, { i32::MAX as usize }>>,154 end: Option<BoundedUsize<0, { i32::MAX as usize }>>,155 step: Option<BoundedUsize<1, { i32::MAX as usize }>>,156 ) -> Result<Self> {157 match &self {158 IndexableVal::Str(s) => {159 let index = index.as_deref().copied().unwrap_or(0);160 let end = end.as_deref().copied().unwrap_or(usize::MAX);161 let step = step.as_deref().copied().unwrap_or(1);162163 if index >= end {164 return Ok(Self::Str("".into()));165 }166167 Ok(Self::Str(168 (s.chars()169 .skip(index)170 .take(end - index)171 .step_by(step)172 .collect::<String>())173 .into(),174 ))175 }176 IndexableVal::Arr(arr) => {177 let index = index.as_deref().copied().unwrap_or(0);178 let end = end.as_deref().copied().unwrap_or(usize::MAX).min(arr.len());179 let step = step.as_deref().copied().unwrap_or(1);180181 if index >= end {182 return Ok(Self::Arr(ArrValue::empty()));183 }184185 Ok(Self::Arr(186 arr.clone()187 .slice(Some(index), Some(end), Some(step))188 .expect("arguments checked"),189 ))190 }191 }192 }193}194195#[derive(Debug, Clone, Trace)]196pub enum StrValue {197 Flat(IStr),198 Tree(Rc<(StrValue, StrValue, usize)>),199}200impl StrValue {201 pub fn concat(a: StrValue, b: StrValue) -> Self {202 // TODO: benchmark for an optimal value, currently just a arbitrary choice203 const STRING_EXTEND_THRESHOLD: usize = 100;204205 if a.is_empty() {206 b207 } else if b.is_empty() {208 a209 } else if a.len() + b.len() < STRING_EXTEND_THRESHOLD {210 Self::Flat(format!("{a}{b}").into())211 } else {212 let len = a.len() + b.len();213 Self::Tree(Rc::new((a, b, len)))214 }215 }216 pub fn into_flat(self) -> IStr {217 #[cold]218 fn write_buf(s: &StrValue, out: &mut String) {219 match s {220 StrValue::Flat(f) => out.push_str(f),221 StrValue::Tree(t) => {222 write_buf(&t.0, out);223 write_buf(&t.1, out);224 }225 }226 }227 match self {228 StrValue::Flat(f) => f,229 StrValue::Tree(_) => {230 let mut buf = String::with_capacity(self.len());231 write_buf(&self, &mut buf);232 buf.into()233 }234 }235 }236 pub fn len(&self) -> usize {237 match self {238 StrValue::Flat(v) => v.len(),239 StrValue::Tree(t) => t.2,240 }241 }242 pub fn is_empty(&self) -> bool {243 match self {244 Self::Flat(v) => v.is_empty(),245 // Can't create non-flat empty string246 Self::Tree(_) => false,247 }248 }249}250impl Display for StrValue {251 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {252 match self {253 StrValue::Flat(v) => write!(f, "{v}"),254 StrValue::Tree(t) => {255 write!(f, "{}", t.0)?;256 write!(f, "{}", t.1)257 }258 }259 }260}261impl PartialEq for StrValue {262 fn eq(&self, other: &Self) -> bool {263 let a = self.clone().into_flat();264 let b = other.clone().into_flat();265 a == b266 }267}268impl Eq for StrValue {}269impl PartialOrd for StrValue {270 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {271 let a = self.clone().into_flat();272 let b = other.clone().into_flat();273 Some(a.cmp(&b))274 }275}276impl Ord for StrValue {277 fn cmp(&self, other: &Self) -> std::cmp::Ordering {278 self.partial_cmp(other)279 .expect("partial_cmp always returns Some")280 }281}282283/// Represents any valid Jsonnet value.284#[derive(Debug, Clone, Trace)]285pub enum Val {286 /// Represents a Jsonnet boolean.287 Bool(bool),288 /// Represents a Jsonnet null value.289 Null,290 /// Represents a Jsonnet string.291 Str(StrValue),292 /// Represents a Jsonnet number.293 /// Should be finite, and not NaN294 /// This restriction isn't enforced by enum, as enum field can't be marked as private295 Num(f64),296 /// Represents a Jsonnet array.297 Arr(ArrValue),298 /// Represents a Jsonnet object.299 Obj(ObjValue),300 /// Represents a Jsonnet function.301 Func(FuncVal),302}303304#[cfg(target_pointer_width = "64")]305static_assertions::assert_eq_size!(Val, [u8; 24]);306307impl From<IndexableVal> for Val {308 fn from(v: IndexableVal) -> Self {309 match v {310 IndexableVal::Str(s) => Self::Str(StrValue::Flat(s)),311 IndexableVal::Arr(a) => Self::Arr(a),312 }313 }314}315316impl Val {317 pub const fn as_bool(&self) -> Option<bool> {318 match self {319 Self::Bool(v) => Some(*v),320 _ => None,321 }322 }323 pub const fn as_null(&self) -> Option<()> {324 match self {325 Self::Null => Some(()),326 _ => None,327 }328 }329 pub fn as_str(&self) -> Option<IStr> {330 match self {331 Self::Str(s) => Some(s.clone().into_flat()),332 _ => None,333 }334 }335 pub const fn as_num(&self) -> Option<f64> {336 match self {337 Self::Num(n) => Some(*n),338 _ => None,339 }340 }341 pub fn as_arr(&self) -> Option<ArrValue> {342 match self {343 Self::Arr(a) => Some(a.clone()),344 _ => None,345 }346 }347 pub fn as_obj(&self) -> Option<ObjValue> {348 match self {349 Self::Obj(o) => Some(o.clone()),350 _ => None,351 }352 }353 pub fn as_func(&self) -> Option<FuncVal> {354 match self {355 Self::Func(f) => Some(f.clone()),356 _ => None,357 }358 }359360 /// Creates `Val::Num` after checking for numeric overflow.361 /// As numbers are `f64`, we can just check for their finity.362 pub fn new_checked_num(num: f64) -> Result<Self> {363 if num.is_finite() {364 Ok(Self::Num(num))365 } else {366 throw!("overflow")367 }368 }369370 pub const fn value_type(&self) -> ValType {371 match self {372 Self::Str(..) => ValType::Str,373 Self::Num(..) => ValType::Num,374 Self::Arr(..) => ValType::Arr,375 Self::Obj(..) => ValType::Obj,376 Self::Bool(_) => ValType::Bool,377 Self::Null => ValType::Null,378 Self::Func(..) => ValType::Func,379 }380 }381382 pub fn manifest(&self, format: impl ManifestFormat) -> Result<String> {383 fn manifest_dyn(val: &Val, manifest: &dyn ManifestFormat) -> Result<String> {384 manifest.manifest(val.clone())385 }386 manifest_dyn(self, &format)387 }388389 pub fn to_string(&self) -> Result<IStr> {390 Ok(match self {391 Self::Bool(true) => "true".into(),392 Self::Bool(false) => "false".into(),393 Self::Null => "null".into(),394 Self::Str(s) => s.clone().into_flat(),395 _ => self.manifest(ToStringFormat).map(IStr::from)?,396 })397 }398399 pub fn into_indexable(self) -> Result<IndexableVal> {400 Ok(match self {401 Val::Str(s) => IndexableVal::Str(s.into_flat()),402 Val::Arr(arr) => IndexableVal::Arr(arr),403 _ => throw!(ValueIsNotIndexable(self.value_type())),404 })405 }406}407408const fn is_function_like(val: &Val) -> bool {409 matches!(val, Val::Func(_))410}411412/// Native implementation of `std.primitiveEquals`413pub fn primitive_equals(val_a: &Val, val_b: &Val) -> Result<bool> {414 Ok(match (val_a, val_b) {415 (Val::Bool(a), Val::Bool(b)) => a == b,416 (Val::Null, Val::Null) => true,417 (Val::Str(a), Val::Str(b)) => a == b,418 (Val::Num(a), Val::Num(b)) => (a - b).abs() <= f64::EPSILON,419 (Val::Arr(_), Val::Arr(_)) => {420 throw!("primitiveEquals operates on primitive types, got array")421 }422 (Val::Obj(_), Val::Obj(_)) => {423 throw!("primitiveEquals operates on primitive types, got object")424 }425 (a, b) if is_function_like(a) && is_function_like(b) => {426 throw!("cannot test equality of functions")427 }428 (_, _) => false,429 })430}431432/// Native implementation of `std.equals`433pub fn equals(val_a: &Val, val_b: &Val) -> Result<bool> {434 if val_a.value_type() != val_b.value_type() {435 return Ok(false);436 }437 match (val_a, val_b) {438 (Val::Arr(a), Val::Arr(b)) => {439 if ArrValue::ptr_eq(a, b) {440 return Ok(true);441 }442 if a.len() != b.len() {443 return Ok(false);444 }445 for (a, b) in a.iter().zip(b.iter()) {446 if !equals(&a?, &b?)? {447 return Ok(false);448 }449 }450 Ok(true)451 }452 (Val::Obj(a), Val::Obj(b)) => {453 if ObjValue::ptr_eq(a, b) {454 return Ok(true);455 }456 let fields = a.fields(457 #[cfg(feature = "exp-preserve-order")]458 false,459 );460 if fields461 != b.fields(462 #[cfg(feature = "exp-preserve-order")]463 false,464 ) {465 return Ok(false);466 }467 for field in fields {468 if !equals(469 &a.get(field.clone())?.expect("field exists"),470 &b.get(field)?.expect("field exists"),471 )? {472 return Ok(false);473 }474 }475 Ok(true)476 }477 (a, b) => Ok(primitive_equals(a, b)?),478 }479}1use std::{2 cell::RefCell,3 fmt::{self, Debug, Display},4 mem::replace,5 rc::Rc,6};78use jrsonnet_gcmodule::{Cc, Trace};9use jrsonnet_interner::IStr;10use jrsonnet_types::ValType;1112pub use crate::arr::ArrValue;13use crate::{14 error::{Error, ErrorKind::*},15 function::FuncVal,16 gc::{GcHashMap, TraceBox},17 manifest::{ManifestFormat, ToStringFormat},18 tb, throw,19 typed::BoundedUsize,20 ObjValue, Result, Unbound, WeakObjValue,21};2223pub trait ThunkValue: Trace {24 type Output;25 fn get(self: Box<Self>) -> Result<Self::Output>;26}2728#[derive(Trace)]29enum ThunkInner<T: Trace> {30 Computed(T),31 Errored(Error),32 Waiting(TraceBox<dyn ThunkValue<Output = T>>),33 Pending,34}3536/// Lazily evaluated value37#[allow(clippy::module_name_repetitions)]38#[derive(Clone, Trace)]39pub struct Thunk<T: Trace>(Cc<RefCell<ThunkInner<T>>>);4041impl<T: Trace> Thunk<T> {42 pub fn evaluated(val: T) -> Self {43 Self(Cc::new(RefCell::new(ThunkInner::Computed(val))))44 }45 pub fn new(f: impl ThunkValue<Output = T> + 'static) -> Self {46 Self(Cc::new(RefCell::new(ThunkInner::Waiting(tb!(f)))))47 }48 pub fn errored(e: Error) -> Self {49 Self(Cc::new(RefCell::new(ThunkInner::Errored(e))))50 }51}5253impl<T> Thunk<T>54where55 T: Clone + Trace,56{57 pub fn force(&self) -> Result<()> {58 self.evaluate()?;59 Ok(())60 }6162 /// Evaluate thunk, or return cached value63 ///64 /// # Errors65 ///66 /// - Lazy value evaluation returned error67 /// - This method was called during inner value evaluation68 pub fn evaluate(&self) -> Result<T> {69 match &*self.0.borrow() {70 ThunkInner::Computed(v) => return Ok(v.clone()),71 ThunkInner::Errored(e) => return Err(e.clone()),72 ThunkInner::Pending => return Err(InfiniteRecursionDetected.into()),73 ThunkInner::Waiting(..) => (),74 };75 let ThunkInner::Waiting(value) = replace(&mut *self.0.borrow_mut(), ThunkInner::Pending) else {76 unreachable!();77 };78 let new_value = match value.0.get() {79 Ok(v) => v,80 Err(e) => {81 *self.0.borrow_mut() = ThunkInner::Errored(e.clone());82 return Err(e);83 }84 };85 *self.0.borrow_mut() = ThunkInner::Computed(new_value.clone());86 Ok(new_value)87 }88}8990type CacheKey = (Option<WeakObjValue>, Option<WeakObjValue>);9192#[derive(Trace, Clone)]93pub struct CachedUnbound<I, T>94where95 I: Unbound<Bound = T>,96 T: Trace,97{98 cache: Cc<RefCell<GcHashMap<CacheKey, T>>>,99 value: I,100}101impl<I: Unbound<Bound = T>, T: Trace> CachedUnbound<I, T> {102 pub fn new(value: I) -> Self {103 Self {104 cache: Cc::new(RefCell::new(GcHashMap::new())),105 value,106 }107 }108}109impl<I: Unbound<Bound = T>, T: Clone + Trace> Unbound for CachedUnbound<I, T> {110 type Bound = T;111 fn bind(&self, sup: Option<ObjValue>, this: Option<ObjValue>) -> Result<T> {112 let cache_key = (113 sup.as_ref().map(|s| s.clone().downgrade()),114 this.as_ref().map(|t| t.clone().downgrade()),115 );116 {117 if let Some(t) = self.cache.borrow().get(&cache_key) {118 return Ok(t.clone());119 }120 }121 let bound = self.value.bind(sup, this)?;122123 {124 let mut cache = self.cache.borrow_mut();125 cache.insert(cache_key, bound.clone());126 }127128 Ok(bound)129 }130}131132impl<T: Debug + Trace> Debug for Thunk<T> {133 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {134 write!(f, "Lazy")135 }136}137impl<T: Trace> PartialEq for Thunk<T> {138 fn eq(&self, other: &Self) -> bool {139 Cc::ptr_eq(&self.0, &other.0)140 }141}142143/// Represents a Jsonnet value, which can be sliced or indexed (string or array).144#[allow(clippy::module_name_repetitions)]145pub enum IndexableVal {146 /// String.147 Str(IStr),148 /// Array.149 Arr(ArrValue),150}151impl IndexableVal {152 /// Slice the value.153 ///154 /// # Implementation155 ///156 /// For strings, will create a copy of specified interval.157 ///158 /// For arrays, nothing will be copied on this call, instead [`ArrValue::Slice`] view will be returned.159 pub fn slice(160 self,161 index: Option<BoundedUsize<0, { i32::MAX as usize }>>,162 end: Option<BoundedUsize<0, { i32::MAX as usize }>>,163 step: Option<BoundedUsize<1, { i32::MAX as usize }>>,164 ) -> Result<Self> {165 match &self {166 IndexableVal::Str(s) => {167 let index = index.as_deref().copied().unwrap_or(0);168 let end = end.as_deref().copied().unwrap_or(usize::MAX);169 let step = step.as_deref().copied().unwrap_or(1);170171 if index >= end {172 return Ok(Self::Str("".into()));173 }174175 Ok(Self::Str(176 (s.chars()177 .skip(index)178 .take(end - index)179 .step_by(step)180 .collect::<String>())181 .into(),182 ))183 }184 IndexableVal::Arr(arr) => {185 let index = index.as_deref().copied().unwrap_or(0);186 let end = end.as_deref().copied().unwrap_or(usize::MAX).min(arr.len());187 let step = step.as_deref().copied().unwrap_or(1);188189 if index >= end {190 return Ok(Self::Arr(ArrValue::empty()));191 }192193 Ok(Self::Arr(194 arr.clone()195 .slice(Some(index), Some(end), Some(step))196 .expect("arguments checked"),197 ))198 }199 }200 }201}202203#[derive(Debug, Clone, Trace)]204pub enum StrValue {205 Flat(IStr),206 Tree(Rc<(StrValue, StrValue, usize)>),207}208impl StrValue {209 pub fn concat(a: StrValue, b: StrValue) -> Self {210 // TODO: benchmark for an optimal value, currently just a arbitrary choice211 const STRING_EXTEND_THRESHOLD: usize = 100;212213 if a.is_empty() {214 b215 } else if b.is_empty() {216 a217 } else if a.len() + b.len() < STRING_EXTEND_THRESHOLD {218 Self::Flat(format!("{a}{b}").into())219 } else {220 let len = a.len() + b.len();221 Self::Tree(Rc::new((a, b, len)))222 }223 }224 pub fn into_flat(self) -> IStr {225 #[cold]226 fn write_buf(s: &StrValue, out: &mut String) {227 match s {228 StrValue::Flat(f) => out.push_str(f),229 StrValue::Tree(t) => {230 write_buf(&t.0, out);231 write_buf(&t.1, out);232 }233 }234 }235 match self {236 StrValue::Flat(f) => f,237 StrValue::Tree(_) => {238 let mut buf = String::with_capacity(self.len());239 write_buf(&self, &mut buf);240 buf.into()241 }242 }243 }244 pub fn len(&self) -> usize {245 match self {246 StrValue::Flat(v) => v.len(),247 StrValue::Tree(t) => t.2,248 }249 }250 pub fn is_empty(&self) -> bool {251 match self {252 Self::Flat(v) => v.is_empty(),253 // Can't create non-flat empty string254 Self::Tree(_) => false,255 }256 }257}258impl From<&str> for StrValue {259 fn from(value: &str) -> Self {260 Self::Flat(value.into())261 }262}263impl From<String> for StrValue {264 fn from(value: String) -> Self {265 Self::Flat(value.into())266 }267}268impl Display for StrValue {269 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {270 match self {271 StrValue::Flat(v) => write!(f, "{v}"),272 StrValue::Tree(t) => {273 write!(f, "{}", t.0)?;274 write!(f, "{}", t.1)275 }276 }277 }278}279impl PartialEq for StrValue {280 fn eq(&self, other: &Self) -> bool {281 let a = self.clone().into_flat();282 let b = other.clone().into_flat();283 a == b284 }285}286impl Eq for StrValue {}287impl PartialOrd for StrValue {288 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {289 let a = self.clone().into_flat();290 let b = other.clone().into_flat();291 Some(a.cmp(&b))292 }293}294impl Ord for StrValue {295 fn cmp(&self, other: &Self) -> std::cmp::Ordering {296 self.partial_cmp(other)297 .expect("partial_cmp always returns Some")298 }299}300301/// Represents any valid Jsonnet value.302#[derive(Debug, Clone, Trace)]303pub enum Val {304 /// Represents a Jsonnet boolean.305 Bool(bool),306 /// Represents a Jsonnet null value.307 Null,308 /// Represents a Jsonnet string.309 Str(StrValue),310 /// Represents a Jsonnet number.311 /// Should be finite, and not NaN312 /// This restriction isn't enforced by enum, as enum field can't be marked as private313 Num(f64),314 /// Represents a Jsonnet array.315 Arr(ArrValue),316 /// Represents a Jsonnet object.317 Obj(ObjValue),318 /// Represents a Jsonnet function.319 Func(FuncVal),320}321322#[cfg(target_pointer_width = "64")]323static_assertions::assert_eq_size!(Val, [u8; 24]);324325impl From<IndexableVal> for Val {326 fn from(v: IndexableVal) -> Self {327 match v {328 IndexableVal::Str(s) => Self::Str(StrValue::Flat(s)),329 IndexableVal::Arr(a) => Self::Arr(a),330 }331 }332}333334impl Val {335 pub const fn as_bool(&self) -> Option<bool> {336 match self {337 Self::Bool(v) => Some(*v),338 _ => None,339 }340 }341 pub const fn as_null(&self) -> Option<()> {342 match self {343 Self::Null => Some(()),344 _ => None,345 }346 }347 pub fn as_str(&self) -> Option<IStr> {348 match self {349 Self::Str(s) => Some(s.clone().into_flat()),350 _ => None,351 }352 }353 pub const fn as_num(&self) -> Option<f64> {354 match self {355 Self::Num(n) => Some(*n),356 _ => None,357 }358 }359 pub fn as_arr(&self) -> Option<ArrValue> {360 match self {361 Self::Arr(a) => Some(a.clone()),362 _ => None,363 }364 }365 pub fn as_obj(&self) -> Option<ObjValue> {366 match self {367 Self::Obj(o) => Some(o.clone()),368 _ => None,369 }370 }371 pub fn as_func(&self) -> Option<FuncVal> {372 match self {373 Self::Func(f) => Some(f.clone()),374 _ => None,375 }376 }377378 /// Creates `Val::Num` after checking for numeric overflow.379 /// As numbers are `f64`, we can just check for their finity.380 pub fn new_checked_num(num: f64) -> Result<Self> {381 if num.is_finite() {382 Ok(Self::Num(num))383 } else {384 throw!("overflow")385 }386 }387388 pub const fn value_type(&self) -> ValType {389 match self {390 Self::Str(..) => ValType::Str,391 Self::Num(..) => ValType::Num,392 Self::Arr(..) => ValType::Arr,393 Self::Obj(..) => ValType::Obj,394 Self::Bool(_) => ValType::Bool,395 Self::Null => ValType::Null,396 Self::Func(..) => ValType::Func,397 }398 }399400 pub fn manifest(&self, format: impl ManifestFormat) -> Result<String> {401 fn manifest_dyn(val: &Val, manifest: &dyn ManifestFormat) -> Result<String> {402 manifest.manifest(val.clone())403 }404 manifest_dyn(self, &format)405 }406407 pub fn to_string(&self) -> Result<IStr> {408 Ok(match self {409 Self::Bool(true) => "true".into(),410 Self::Bool(false) => "false".into(),411 Self::Null => "null".into(),412 Self::Str(s) => s.clone().into_flat(),413 _ => self.manifest(ToStringFormat).map(IStr::from)?,414 })415 }416417 pub fn into_indexable(self) -> Result<IndexableVal> {418 Ok(match self {419 Val::Str(s) => IndexableVal::Str(s.into_flat()),420 Val::Arr(arr) => IndexableVal::Arr(arr),421 _ => throw!(ValueIsNotIndexable(self.value_type())),422 })423 }424}425426const fn is_function_like(val: &Val) -> bool {427 matches!(val, Val::Func(_))428}429430/// Native implementation of `std.primitiveEquals`431pub fn primitive_equals(val_a: &Val, val_b: &Val) -> Result<bool> {432 Ok(match (val_a, val_b) {433 (Val::Bool(a), Val::Bool(b)) => a == b,434 (Val::Null, Val::Null) => true,435 (Val::Str(a), Val::Str(b)) => a == b,436 (Val::Num(a), Val::Num(b)) => (a - b).abs() <= f64::EPSILON,437 (Val::Arr(_), Val::Arr(_)) => {438 throw!("primitiveEquals operates on primitive types, got array")439 }440 (Val::Obj(_), Val::Obj(_)) => {441 throw!("primitiveEquals operates on primitive types, got object")442 }443 (a, b) if is_function_like(a) && is_function_like(b) => {444 throw!("cannot test equality of functions")445 }446 (_, _) => false,447 })448}449450/// Native implementation of `std.equals`451pub fn equals(val_a: &Val, val_b: &Val) -> Result<bool> {452 if val_a.value_type() != val_b.value_type() {453 return Ok(false);454 }455 match (val_a, val_b) {456 (Val::Arr(a), Val::Arr(b)) => {457 if ArrValue::ptr_eq(a, b) {458 return Ok(true);459 }460 if a.len() != b.len() {461 return Ok(false);462 }463 for (a, b) in a.iter().zip(b.iter()) {464 if !equals(&a?, &b?)? {465 return Ok(false);466 }467 }468 Ok(true)469 }470 (Val::Obj(a), Val::Obj(b)) => {471 if ObjValue::ptr_eq(a, b) {472 return Ok(true);473 }474 let fields = a.fields(475 #[cfg(feature = "exp-preserve-order")]476 false,477 );478 if fields479 != b.fields(480 #[cfg(feature = "exp-preserve-order")]481 false,482 ) {483 return Ok(false);484 }485 for field in fields {486 if !equals(487 &a.get(field.clone())?.expect("field exists"),488 &b.get(field)?.expect("field exists"),489 )? {490 return Ok(false);491 }492 }493 Ok(true)494 }495 (a, b) => Ok(primitive_equals(a, b)?),496 }497}crates/jrsonnet-parser/src/source.rsdiffbeforeafterboth--- a/crates/jrsonnet-parser/src/source.rs
+++ b/crates/jrsonnet-parser/src/source.rs
@@ -33,8 +33,8 @@
}
fn dyn_eq(&self, other: &dyn $T) -> bool {
let Some(other) = other.as_any().downcast_ref::<Self>() else {
- return false
- };
+ return false
+ };
let this = <Self as $T>::as_any(self)
.downcast_ref::<Self>()
.expect("restricted by impl");
crates/jrsonnet-stdlib/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/lib.rs
+++ b/crates/jrsonnet-stdlib/src/lib.rs
@@ -211,7 +211,7 @@
locs[0].line
);
}
- eprintln!(" {}", value);
+ eprintln!(" {value}");
}
}
@@ -229,7 +229,7 @@
}
fn extvar_source(name: &str, code: impl Into<IStr>) -> Source {
- let source_name = format!("<extvar:{}>", name);
+ let source_name = format!("<extvar:{name}>");
Source::new_virtual(source_name.into(), code.into())
}
crates/jrsonnet-stdlib/src/misc.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/misc.rs
+++ b/crates/jrsonnet-stdlib/src/misc.rs
@@ -46,7 +46,7 @@
.ext_natives
.get(&x)
.cloned()
- .map_or(Val::Null, |v| Val::Func(FuncVal::Builtin(v.clone())))
+ .map_or(Val::Null, |v| Val::Func(FuncVal::Builtin(v)))
}
#[builtin(fields(
crates/jrsonnet-stdlib/src/parse.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/parse.rs
+++ b/crates/jrsonnet-stdlib/src/parse.rs
@@ -8,7 +8,7 @@
#[builtin]
pub fn builtin_parse_json(str: IStr) -> Result<Val> {
let value: Val = serde_json::from_str(&str)
- .map_err(|e| RuntimeError(format!("failed to parse json: {}", e).into()))?;
+ .map_err(|e| RuntimeError(format!("failed to parse json: {e}").into()))?;
Ok(value)
}
@@ -22,7 +22,7 @@
let mut out = vec![];
for item in value {
let val = Val::deserialize(item)
- .map_err(|e| RuntimeError(format!("failed to parse yaml: {}", e).into()))?;
+ .map_err(|e| RuntimeError(format!("failed to parse yaml: {e}").into()))?;
out.push(val);
}
Ok(if out.is_empty() {
crates/jrsonnet-types/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-types/src/lib.rs
+++ b/crates/jrsonnet-types/src/lib.rs
@@ -150,7 +150,7 @@
if should_add_braces {
write!(f, "(")?;
}
- write!(f, "{}", v)?;
+ write!(f, "{v}")?;
if should_add_braces {
write!(f, ")")?;
}
@@ -162,7 +162,7 @@
if *a == ComplexValType::Any {
write!(f, "array")?
} else {
- write!(f, "Array<{}>", a)?
+ write!(f, "Array<{a}>")?
}
Ok(())
}
@@ -171,7 +171,7 @@
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ComplexValType::Any => write!(f, "any")?,
- ComplexValType::Simple(s) => write!(f, "{}", s)?,
+ ComplexValType::Simple(s) => write!(f, "{s}")?,
ComplexValType::Char => write!(f, "char")?,
ComplexValType::BoundedNumber(a, b) => write!(
f,
@@ -187,7 +187,7 @@
if i != 0 {
write!(f, ", ")?;
}
- write!(f, "{}: {}", k, v)?;
+ write!(f, "{k}: {v}")?;
}
write!(f, "}}")?;
}