difftreelog
style fix clippy warnings
in: master
20 files changed
bindings/jsonnet/src/lib.rsdiffbeforeafterboth--- a/bindings/jsonnet/src/lib.rs
+++ b/bindings/jsonnet/src/lib.rs
@@ -70,6 +70,7 @@
/// Creates a new Jsonnet virtual machine.
#[no_mangle]
+#[allow(clippy::box_default)]
pub extern "C" fn jsonnet_make() -> *mut State {
let state = State::default();
state.settings_mut().import_resolver = Box::new(FileImportResolver::default());
crates/jrsonnet-cli/src/stdlib.rsdiffbeforeafterboth--- a/crates/jrsonnet-cli/src/stdlib.rs
+++ b/crates/jrsonnet-cli/src/stdlib.rs
@@ -44,7 +44,7 @@
if out.len() != 2 {
return Err("bad ext-file syntax".to_owned());
}
- let file = read_to_string(&out[1]);
+ let file = read_to_string(out[1]);
match file {
Ok(content) => Ok(Self {
name: out[0].into(),
crates/jrsonnet-evaluator/src/error.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/error.rs
+++ b/crates/jrsonnet-evaluator/src/error.rs
@@ -100,7 +100,7 @@
#[error("duplicate local var: {0}")]
DuplicateLocalVar(IStr),
- #[error("type mismatch: expected {}, got {2} {0}", .1.iter().map(|e| format!("{}", e)).collect::<Vec<_>>().join(", "))]
+ #[error("type mismatch: expected {}, got {2} {0}", .1.iter().map(|e| format!("{e}")).collect::<Vec<_>>().join(", "))]
TypeMismatch(&'static str, Vec<ValType>, ValType),
#[error("no such field: {}{}", format_empty_str(.0), format_found(.1, "field"))]
NoSuchField(IStr, Vec<IStr>),
@@ -113,7 +113,7 @@
BindingParameterASecondTime(IStr),
#[error("too many args, function has {0}{}", format_signature(.1))]
TooManyArgsFunctionHas(usize, FunctionSignature),
- #[error("function argument is not passed: {}{}", .0.as_ref().map(|n| n.as_str()).unwrap_or("<unnamed>"), format_signature(.1))]
+ #[error("function argument is not passed: {}{}", .0.as_ref().map_or("<unnamed>", IStr::as_str), format_signature(.1))]
FunctionParameterNotBoundInCall(Option<IStr>, FunctionSignature),
#[error("external variable is not defined: {0}")]
@@ -249,7 +249,7 @@
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "{}", self.0 .0)?;
for el in &self.0 .1 .0 {
- writeln!(f, "\t{:?}", el)?;
+ writeln!(f, "\t{el:?}")?;
}
Ok(())
}
crates/jrsonnet-evaluator/src/evaluate/mod.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/evaluate/mod.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate/mod.rs
@@ -436,7 +436,7 @@
UnaryOp(o, v) => evaluate_unary_op(*o, &evaluate(s, ctx, v)?)?,
Var(name) => s.push(
CallLocation::new(loc),
- || format!("variable <{}> access", name),
+ || format!("variable <{name}> access"),
|| ctx.binding(name.clone())?.evaluate(s.clone()),
)?,
Index(value, index) => {
@@ -446,7 +446,7 @@
) {
(Val::Obj(v), Val::Str(key)) => s.push(
CallLocation::new(loc),
- || format!("field <{}> access", key),
+ || format!("field <{key}> access"),
|| match v.get(s.clone(), key.clone()) {
Ok(Some(v)) => Ok(v),
#[cfg(not(feature = "friendly-errors"))]
@@ -611,7 +611,7 @@
if let Some(value) = expr {
Ok(Some(s.push(
loc,
- || format!("slice {}", desc),
+ || format!("slice {desc}"),
|| T::from_untyped(evaluate(s.clone(), ctx.clone(), value)?, s.clone()),
)?))
} else {
crates/jrsonnet-evaluator/src/evaluate/operator.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/evaluate/operator.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate/operator.rs
@@ -30,8 +30,8 @@
(Str(a), Num(b)) => Str(format!("{a}{b}").into()),
(Str(a), o) | (o, Str(a)) if a.is_empty() => Val::Str(o.clone().to_string(s)?),
- (Str(a), o) => Str(format!("{}{}", a, o.clone().to_string(s)?).into()),
- (o, Str(a)) => Str(format!("{}{}", o.clone().to_string(s)?, a).into()),
+ (Str(a), o) => Str(format!("{a}{}", o.clone().to_string(s)?).into()),
+ (o, Str(a)) => Str(format!("{}{a}", o.clone().to_string(s)?).into()),
(Obj(v1), Obj(v2)) => Obj(v2.extend_from(v1.clone())),
(Arr(a), Arr(b)) => {
crates/jrsonnet-evaluator/src/function/arglike.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/function/arglike.rs
+++ b/crates/jrsonnet-evaluator/src/function/arglike.rs
@@ -108,7 +108,7 @@
handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,
) -> Result<()> {
for (idx, el) in self.iter().enumerate() {
- handler(idx, Thunk::evaluated(el.clone()))?
+ handler(idx, Thunk::evaluated(el.clone()))?;
}
Ok(())
}
crates/jrsonnet-evaluator/src/function/parse.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/function/parse.rs
+++ b/crates/jrsonnet-evaluator/src/function/parse.rs
@@ -179,12 +179,7 @@
// FIXME: O(n) for arg existence check
let id = params
.iter()
- .position(|p| {
- p.name
- .as_ref()
- .map(|v| &v as &str == name as &str)
- .unwrap_or(false)
- })
+ .position(|p| p.name.as_ref().map_or(false, |v| v as &str == name as &str))
.ok_or_else(|| UnknownFunctionParameter((name as &str).to_owned()))?;
if replace(&mut passed_args[id], Some(arg)).is_some() {
throw!(BindingParameterASecondTime(name.clone()));
@@ -209,8 +204,7 @@
if param
.name
.as_ref()
- .map(|v| &v as &str == name as &str)
- .unwrap_or(false)
+ .map_or(false, |v| v as &str == name as &str)
{
found = true;
}
crates/jrsonnet-evaluator/src/import.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/import.rs
+++ b/crates/jrsonnet-evaluator/src/import.rs
@@ -123,15 +123,11 @@
};
if meta.is_file() {
Ok(SourcePath::new(SourceFile::new(
- path.canonicalize()
- .map_err(|e| ImportIo(e.to_string()))?
- .to_owned(),
+ path.canonicalize().map_err(|e| ImportIo(e.to_string()))?,
)))
} else if meta.is_dir() {
Ok(SourcePath::new(SourceDirectory::new(
- path.canonicalize()
- .map_err(|e| ImportIo(e.to_string()))?
- .to_owned(),
+ path.canonicalize().map_err(|e| ImportIo(e.to_string()))?,
)))
} else {
unreachable!("this can't be a symlink")
crates/jrsonnet-evaluator/src/integrations/serde.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/integrations/serde.rs
+++ b/crates/jrsonnet-evaluator/src/integrations/serde.rs
@@ -16,7 +16,7 @@
Self::Null => Val::Null,
Self::Bool(v) => Val::Bool(v),
Self::Number(n) => Val::Num(n.as_f64().ok_or_else(|| {
- RuntimeError(format!("json number can't be represented as jsonnet: {}", n).into())
+ RuntimeError(format!("json number can't be represented as jsonnet: {n}").into())
})?),
Self::String(s) => Val::Str((&s as &str).into()),
Self::Array(a) => {
crates/jrsonnet-evaluator/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/lib.rs
+++ b/crates/jrsonnet-evaluator/src/lib.rs
@@ -594,7 +594,7 @@
.insert(name, TlaArg::String(value));
}
pub fn add_tla_code(&self, name: IStr, code: &str) -> Result<()> {
- let source_name = format!("<top-level-arg:{}>", name);
+ let source_name = format!("<top-level-arg:{name}>");
let source = Source::new_virtual(source_name.into(), code.into());
let parsed = jrsonnet_parser::parse(
code,
crates/jrsonnet-evaluator/src/obj.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/obj.rs
+++ b/crates/jrsonnet-evaluator/src/obj.rs
@@ -156,9 +156,9 @@
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(super_obj) = self.0.sup.as_ref() {
if f.alternate() {
- write!(f, "{:#?}", super_obj)?;
+ write!(f, "{super_obj:#?}")?;
} else {
- write!(f, "{:?}", super_obj)?;
+ write!(f, "{super_obj:?}")?;
}
write!(f, " + ")?;
}
@@ -395,10 +395,9 @@
})?;
self.0.value_cache.borrow_mut().insert(
key,
- match &value {
- Some(v) => CacheValue::Cached(v.clone()),
- None => CacheValue::NotFound,
- },
+ value
+ .as_ref()
+ .map_or(CacheValue::NotFound, |v| CacheValue::Cached(v.clone())),
);
Ok(value)
}
crates/jrsonnet-evaluator/src/stdlib/format.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/stdlib/format.rs
+++ b/crates/jrsonnet-evaluator/src/stdlib/format.rs
@@ -45,7 +45,7 @@
let mut i = 1;
while i < bytes.len() {
if bytes[i] == b')' {
- return Ok((&str[1..i as usize], &str[i as usize + 1..]));
+ return Ok((&str[1..i], &str[i + 1..]));
}
i += 1;
}
@@ -310,6 +310,7 @@
nums
};
let neg = iv < 0.0;
+ #[allow(clippy::bool_to_int_with_if)]
let zp = padding.saturating_sub(if neg || blank || sign { 1 } else { 0 });
let zp2 = zp
.max(precision)
@@ -406,6 +407,7 @@
ensure_pt: bool,
trailing: bool,
) {
+ #[allow(clippy::bool_to_int_with_if)]
let dot_size = if precision == 0 && !ensure_pt { 0 } else { 1 };
padding = padding.saturating_sub(dot_size + precision);
render_decimal(out, n.floor(), padding, 0, blank, sign);
@@ -478,10 +480,7 @@
precision: Option<usize>,
) -> Result<()> {
let clfags = &code.cflags;
- let (fpprec, iprec) = match precision {
- Some(v) => (v, v),
- None => (6, 0),
- };
+ let (fpprec, iprec) = precision.map_or((6, 0), |v| (v, v));
let padding = if clfags.zero && !clfags.left {
width
} else {
@@ -586,8 +585,10 @@
}
}
ConvTypeV::Char => match value.clone() {
- Val::Num(n) => tmp_out
- .push(std::char::from_u32(n as u32).ok_or(InvalidUnicodeCodepointGot(n as u32))?),
+ Val::Num(n) => tmp_out.push(
+ std::char::from_u32(n as u32)
+ .ok_or_else(|| InvalidUnicodeCodepointGot(n as u32))?,
+ ),
Val::Str(s) => {
if s.chars().count() != 1 {
throw!(RuntimeError(
crates/jrsonnet-evaluator/src/stdlib/manifest.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/stdlib/manifest.rs
+++ b/crates/jrsonnet-evaluator/src/stdlib/manifest.rs
@@ -49,7 +49,7 @@
}
Val::Null => buf.push_str("null"),
Val::Str(s) => escape_string_json_buf(s, buf),
- Val::Num(n) => write!(buf, "{}", n).unwrap(),
+ Val::Num(n) => write!(buf, "{n}").unwrap(),
Val::Arr(items) => {
buf.push('[');
if !items.is_empty() {
crates/jrsonnet-evaluator/src/stdlib/mod.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/stdlib/mod.rs
+++ b/crates/jrsonnet-evaluator/src/stdlib/mod.rs
@@ -12,7 +12,7 @@
pub fn std_format(s: State, str: IStr, vals: Val) -> Result<String> {
s.push(
CallLocation::native(),
- || format!("std.format of {}", str),
+ || format!("std.format of {str}"),
|| {
Ok(match vals {
Val::Arr(vals) => format_arr(s.clone(), &str, &vals.evaluated(s.clone())?)?,
crates/jrsonnet-evaluator/src/trace/mod.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/trace/mod.rs
+++ b/crates/jrsonnet-evaluator/src/trace/mod.rs
@@ -16,12 +16,9 @@
}
impl PathResolver {
- /// Will return Self::Relative(cwd), or Self::Absolute on cwd failure
+ /// Will return `Self::Relative(cwd)`, or `Self::Absolute` on cwd failure
pub fn new_cwd_fallback() -> Self {
- match std::env::current_dir() {
- Ok(v) => Self::Relative(v),
- Err(_) => Self::Absolute,
- }
+ std::env::current_dir().map_or(Self::Absolute, Self::Relative)
}
pub fn resolve(&self, from: &Path) -> String {
match self {
@@ -97,10 +94,10 @@
use std::fmt::Write;
writeln!(out)?;
- let mut n = match path.source_path().path() {
- Some(r) => self.resolver.resolve(r),
- None => path.source_path().to_string(),
- };
+ let mut n = path.source_path().path().map_or_else(
+ || path.source_path().to_string(),
+ |r| self.resolver.resolve(r),
+ );
let mut offset = error.location.offset;
let is_eof = if offset >= path.code().len() {
offset = path.code().len().saturating_sub(1);
@@ -119,7 +116,7 @@
write!(n, ":").unwrap();
print_code_location(&mut n, &location, &location).unwrap();
- write!(out, "{:<p$}{}", "", n, p = self.padding,)?;
+ write!(out, "{:<p$}{n}", "", p = self.padding)?;
}
let file_names = error
.trace()
@@ -185,10 +182,10 @@
let desc = &item.desc;
if let Some(source) = &item.location {
let start_end = source.0.map_source_locations(&[source.1, source.2]);
- let resolved_path = match source.0.source_path().path() {
- Some(r) => r.display().to_string(),
- None => source.0.source_path().to_string(),
- };
+ let resolved_path = source.0.source_path().path().map_or_else(
+ || source.0.source_path().to_string(),
+ |r| r.display().to_string(),
+ );
write!(
out,
@@ -196,7 +193,7 @@
desc, resolved_path, start_end[0].line, start_end[0].column,
)?;
} else {
- write!(out, " during {}", desc)?;
+ write!(out, " during {desc}")?;
}
}
Ok(())
@@ -252,7 +249,7 @@
desc,
)?;
} else {
- write!(out, "{}", desc)?;
+ write!(out, "{desc}")?;
}
}
Ok(())
@@ -280,10 +277,10 @@
.take(end.line_end_offset - end.line_start_offset)
.collect();
- let origin = match origin.source_path().path() {
- Some(r) => self.resolver.resolve(r),
- None => origin.source_path().to_string(),
- };
+ let origin = origin.source_path().path().map_or_else(
+ || origin.source_path().to_string(),
+ |r| self.resolver.resolve(r),
+ );
let snippet = Snippet {
opt: FormatOptions {
color: true,
@@ -308,7 +305,7 @@
};
let dl = DisplayList::from(snippet);
- write!(out, "{}", dl)?;
+ write!(out, "{dl}")?;
Ok(())
}
crates/jrsonnet-evaluator/src/typed/mod.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/typed/mod.rs
+++ b/crates/jrsonnet-evaluator/src/typed/mod.rs
@@ -21,8 +21,8 @@
UnionFailed(ComplexValType, TypeLocErrorList),
#[error(
"number out of bounds: {0} not in {}..{}",
- .1.map(|v|v.to_string()).unwrap_or_else(|| "".to_owned()),
- .2.map(|v|v.to_string()).unwrap_or_else(|| "".to_owned()),
+ .1.map(|v|v.to_string()).unwrap_or_default(),
+ .2.map(|v|v.to_string()).unwrap_or_default(),
)]
BoundsFailed(f64, Option<f64>, Option<f64>),
}
@@ -65,7 +65,7 @@
writeln!(f)?;
}
out.clear();
- write!(out, "{}", err)?;
+ write!(out, "{err}")?;
for (i, line) in out.lines().enumerate() {
if line.trim().is_empty() {
@@ -77,7 +77,7 @@
writeln!(f)?;
write!(f, " ")?;
}
- write!(f, "{}", line)?;
+ write!(f, "{line}")?;
}
}
Ok(())
@@ -125,8 +125,8 @@
impl Display for ValuePathItem {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
- Self::Field(name) => write!(f, ".{:?}", name)?,
- Self::Index(idx) => write!(f, "[{}]", idx)?,
+ Self::Field(name) => write!(f, ".{name:?}")?,
+ Self::Index(idx) => write!(f, "[{idx}]")?,
}
Ok(())
}
@@ -138,7 +138,7 @@
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "self")?;
for elem in self.0.iter().rev() {
- write!(f, "{}", elem)?;
+ write!(f, "{elem}")?;
}
Ok(())
}
@@ -171,7 +171,7 @@
for (i, item) in a.iter(s.clone()).enumerate() {
push_type_description(
s.clone(),
- || format!("array index {}", i),
+ || format!("array index {i}"),
|| ValuePathItem::Index(i as u64),
|| elem_type.check(s.clone(), &item.clone()?),
)?;
@@ -185,7 +185,7 @@
for (i, item) in a.iter(s.clone()).enumerate() {
push_type_description(
s.clone(),
- || format!("array index {}", i),
+ || format!("array index {i}"),
|| ValuePathItem::Index(i as u64),
|| elem_type.check(s.clone(), &item.clone()?),
)?;
@@ -200,7 +200,7 @@
if let Some(got_v) = obj.get(s.clone(), (*k).into())? {
push_type_description(
s.clone(),
- || format!("property {}", k),
+ || format!("property {k}"),
|| ValuePathItem::Field((*k).into()),
|| v.check(s.clone(), &got_v),
)?;
crates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth1use std::{cell::RefCell, fmt::Debug, rc::Rc};23use jrsonnet_gcmodule::{Cc, Trace};4use jrsonnet_interner::{IBytes, IStr};5use jrsonnet_types::ValType;67use crate::{8 error::{Error::*, LocError},9 function::FuncVal,10 gc::{GcHashMap, TraceBox},11 stdlib::manifest::{12 manifest_json_ex, manifest_yaml_ex, ManifestJsonOptions, ManifestType, ManifestYamlOptions,13 },14 throw,15 typed::BoundedUsize,16 ObjValue, Result, State, Unbound, WeakObjValue,17};1819pub trait ThunkValue: Trace {20 type Output;21 fn get(self: Box<Self>, s: State) -> Result<Self::Output>;22}2324#[derive(Trace)]25enum ThunkInner<T: Trace> {26 Computed(T),27 Errored(LocError),28 Waiting(TraceBox<dyn ThunkValue<Output = T>>),29 Pending,30}3132#[allow(clippy::module_name_repetitions)]33#[derive(Clone, Trace)]34pub struct Thunk<T: Trace>(Cc<RefCell<ThunkInner<T>>>);3536impl<T> Thunk<T>37where38 T: Clone + Trace,39{40 pub fn new(f: TraceBox<dyn ThunkValue<Output = T>>) -> Self {41 Self(Cc::new(RefCell::new(ThunkInner::Waiting(f))))42 }43 pub fn evaluated(val: T) -> Self {44 Self(Cc::new(RefCell::new(ThunkInner::Computed(val))))45 }46 pub fn force(&self, s: State) -> Result<()> {47 self.evaluate(s)?;48 Ok(())49 }50 pub fn evaluate(&self, s: State) -> Result<T> {51 match &*self.0.borrow() {52 ThunkInner::Computed(v) => return Ok(v.clone()),53 ThunkInner::Errored(e) => return Err(e.clone()),54 ThunkInner::Pending => return Err(InfiniteRecursionDetected.into()),55 ThunkInner::Waiting(..) => (),56 };57 let value = if let ThunkInner::Waiting(value) =58 std::mem::replace(&mut *self.0.borrow_mut(), ThunkInner::Pending)59 {60 value61 } else {62 unreachable!()63 };64 let new_value = match value.0.get(s) {65 Ok(v) => v,66 Err(e) => {67 *self.0.borrow_mut() = ThunkInner::Errored(e.clone());68 return Err(e);69 }70 };71 *self.0.borrow_mut() = ThunkInner::Computed(new_value.clone());72 Ok(new_value)73 }74}7576type CacheKey = (Option<WeakObjValue>, Option<WeakObjValue>);7778#[derive(Trace, Clone)]79pub struct CachedUnbound<I, T>80where81 I: Unbound<Bound = T>,82 T: Trace,83{84 cache: Cc<RefCell<GcHashMap<CacheKey, T>>>,85 value: I,86}87impl<I: Unbound<Bound = T>, T: Trace> CachedUnbound<I, T> {88 pub fn new(value: I) -> Self {89 Self {90 cache: Cc::new(RefCell::new(GcHashMap::new())),91 value,92 }93 }94}95impl<I: Unbound<Bound = T>, T: Clone + Trace> Unbound for CachedUnbound<I, T> {96 type Bound = T;97 fn bind(&self, s: State, sup: Option<ObjValue>, this: Option<ObjValue>) -> Result<T> {98 let cache_key = (99 sup.as_ref().map(|s| s.clone().downgrade()),100 this.as_ref().map(|t| t.clone().downgrade()),101 );102 {103 if let Some(t) = self.cache.borrow().get(&cache_key) {104 return Ok(t.clone());105 }106 }107 let bound = self.value.bind(s, sup, this)?;108109 {110 let mut cache = self.cache.borrow_mut();111 cache.insert(cache_key, bound.clone());112 }113114 Ok(bound)115 }116}117118impl<T: Debug + Trace> Debug for Thunk<T> {119 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {120 write!(f, "Lazy")121 }122}123impl<T: Trace> PartialEq for Thunk<T> {124 fn eq(&self, other: &Self) -> bool {125 Cc::ptr_eq(&self.0, &other.0)126 }127}128129#[derive(Clone)]130pub enum ManifestFormat {131 YamlStream(Box<ManifestFormat>),132 Yaml {133 padding: usize,134 #[cfg(feature = "exp-preserve-order")]135 preserve_order: bool,136 },137 Json {138 padding: usize,139 #[cfg(feature = "exp-preserve-order")]140 preserve_order: bool,141 },142 ToString,143 String,144}145impl ManifestFormat {146 #[cfg(feature = "exp-preserve-order")]147 fn preserve_order(&self) -> bool {148 match self {149 ManifestFormat::YamlStream(s) => s.preserve_order(),150 ManifestFormat::Yaml { preserve_order, .. } => *preserve_order,151 ManifestFormat::Json { preserve_order, .. } => *preserve_order,152 ManifestFormat::ToString => false,153 ManifestFormat::String => false,154 }155 }156}157158#[derive(Debug, Clone, Trace)]159pub struct Slice {160 pub(crate) inner: ArrValue,161 pub(crate) from: u32,162 pub(crate) to: u32,163 pub(crate) step: u32,164}165impl Slice {166 const fn from(&self) -> usize {167 self.from as usize168 }169 const fn to(&self) -> usize {170 self.to as usize171 }172 const fn step(&self) -> usize {173 self.step as usize174 }175 const fn len(&self) -> usize {176 // TODO: use div_ceil177 let diff = self.to() - self.from();178 let rem = diff % self.step();179 let div = diff / self.step();180181 if rem == 0 {182 div183 } else {184 div + 1185 }186 }187}188189#[derive(Debug, Clone, Trace)]190// may contrain other ArrValue191#[trace(tracking(force))]192pub enum ArrValue {193 Bytes(#[trace(skip)] IBytes),194 Lazy(Cc<Vec<Thunk<Val>>>),195 Eager(Cc<Vec<Val>>),196 Extended(Box<(Self, Self)>),197 Range(i32, i32),198 Slice(Box<Slice>),199 Reversed(Box<Self>),200}201202#[cfg(target_pointer_width = "64")]203static_assertions::assert_eq_size!(ArrValue, [u8; 16]);204205impl ArrValue {206 pub fn new_eager() -> Self {207 Self::Eager(Cc::new(Vec::new()))208 }209 pub fn empty() -> Self {210 Self::new_range(0, 0)211 }212213 /// # Panics214 /// If a > b215 #[inline]216 pub fn new_range(a: i32, b: i32) -> Self {217 assert!(a <= b);218 Self::Range(a, b)219 }220221 /// # Panics222 /// If passed numbers are incorrect223 #[must_use]224 pub fn slice(self, from: Option<usize>, to: Option<usize>, step: Option<usize>) -> Self {225 let len = self.len();226 let from = from.unwrap_or(0);227 let to = to.unwrap_or(len).min(len);228 let step = step.unwrap_or(1);229 assert!(from < to);230 assert!(step > 0);231232 Self::Slice(Box::new(Slice {233 inner: self,234 from: from as u32,235 to: to as u32,236 step: step as u32,237 }))238 }239240 pub fn len(&self) -> usize {241 match self {242 Self::Bytes(i) => i.len(),243 Self::Lazy(l) => l.len(),244 Self::Eager(e) => e.len(),245 Self::Extended(v) => v.0.len() + v.1.len(),246 Self::Range(a, b) => a.abs_diff(*b) as usize + 1,247 Self::Reversed(i) => i.len(),248 Self::Slice(s) => s.len(),249 }250 }251252 pub fn is_empty(&self) -> bool {253 self.len() == 0254 }255256 pub fn get(&self, s: State, index: usize) -> Result<Option<Val>> {257 match self {258 Self::Bytes(i) => i259 .get(index)260 .map_or(Ok(None), |v| Ok(Some(Val::Num(f64::from(*v))))),261 Self::Lazy(vec) => {262 if let Some(v) = vec.get(index) {263 Ok(Some(v.evaluate(s)?))264 } else {265 Ok(None)266 }267 }268 Self::Eager(vec) => Ok(vec.get(index).cloned()),269 Self::Extended(v) => {270 let a_len = v.0.len();271 if a_len > index {272 v.0.get(s, index)273 } else {274 v.1.get(s, index - a_len)275 }276 }277 Self::Range(a, _) => {278 if index >= self.len() {279 return Ok(None);280 }281 Ok(Some(Val::Num(((*a as isize) + index as isize) as f64)))282 }283 Self::Reversed(v) => {284 let len = v.len();285 if index >= len {286 return Ok(None);287 }288 v.get(s, len - index - 1)289 }290 Self::Slice(v) => {291 let index = v.from() + index * v.step();292 if index >= v.to() {293 return Ok(None);294 }295 v.inner.get(s, index as usize)296 }297 }298 }299300 pub fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {301 match self {302 Self::Bytes(i) => i303 .get(index)304 .map(|b| Thunk::evaluated(Val::Num(f64::from(*b)))),305 Self::Lazy(vec) => vec.get(index).cloned(),306 Self::Eager(vec) => vec.get(index).cloned().map(Thunk::evaluated),307 Self::Extended(v) => {308 let a_len = v.0.len();309 if a_len > index {310 v.0.get_lazy(index)311 } else {312 v.1.get_lazy(index - a_len)313 }314 }315 Self::Range(a, _) => {316 if index >= self.len() {317 return None;318 }319 Some(Thunk::evaluated(Val::Num(320 ((*a as isize) + index as isize) as f64,321 )))322 }323 Self::Reversed(v) => {324 let len = v.len();325 if index >= len {326 return None;327 }328 v.get_lazy(len - index - 1)329 }330 Self::Slice(s) => {331 let index = s.from() + index * s.step();332 if index >= s.to() {333 return None;334 }335 s.inner.get_lazy(index as usize)336 }337 }338 }339340 pub fn evaluated(&self, s: State) -> Result<Cc<Vec<Val>>> {341 Ok(match self {342 Self::Bytes(i) => {343 let mut out = Vec::with_capacity(i.len());344 for v in i.iter() {345 out.push(Val::Num(f64::from(*v)));346 }347 Cc::new(out)348 }349 Self::Lazy(vec) => {350 let mut out = Vec::with_capacity(vec.len());351 for item in vec.iter() {352 out.push(item.evaluate(s.clone())?);353 }354 Cc::new(out)355 }356 Self::Eager(vec) => vec.clone(),357 Self::Extended(_v) => {358 let mut out = Vec::with_capacity(self.len());359 for item in self.iter(s) {360 out.push(item?);361 }362 Cc::new(out)363 }364 Self::Range(a, b) => {365 let mut out = Vec::with_capacity(self.len());366 for i in *a..*b {367 out.push(Val::Num(f64::from(i)));368 }369 Cc::new(out)370 }371 Self::Reversed(r) => {372 let mut r = r.evaluated(s)?;373 Cc::update_with(&mut r, |v| v.reverse());374 r375 }376 Self::Slice(v) => {377 let mut out = Vec::with_capacity(v.inner.len());378 for v in v379 .inner380 .iter_lazy()381 .skip(v.from())382 .take(v.to() - v.from())383 .step_by(v.step())384 {385 out.push(v.evaluate(s.clone())?);386 }387 Cc::new(out)388 }389 })390 }391392 pub fn iter(&self, s: State) -> impl DoubleEndedIterator<Item = Result<Val>> + '_ {393 (0..self.len()).map(move |idx| match self {394 Self::Bytes(b) => Ok(Val::Num(f64::from(b[idx]))),395 Self::Lazy(l) => l[idx].evaluate(s.clone()),396 Self::Eager(e) => Ok(e[idx].clone()),397 Self::Extended(..) | Self::Range(..) | Self::Reversed(..) | Self::Slice(..) => {398 self.get(s.clone(), idx).map(|e| e.expect("idx < len"))399 }400 })401 }402403 pub fn iter_lazy(&self) -> impl DoubleEndedIterator<Item = Thunk<Val>> + '_ {404 (0..self.len()).map(move |idx| match self {405 Self::Bytes(b) => Thunk::evaluated(Val::Num(f64::from(b[idx]))),406 Self::Lazy(l) => l[idx].clone(),407 Self::Eager(e) => Thunk::evaluated(e[idx].clone()),408 Self::Slice(..) | Self::Extended(..) | Self::Range(..) | Self::Reversed(..) => {409 self.get_lazy(idx).expect("idx < len")410 }411 })412 }413414 #[must_use]415 pub fn reversed(self) -> Self {416 Self::Reversed(Box::new(self))417 }418419 pub fn map(self, s: State, mapper: impl Fn(Val) -> Result<Val>) -> Result<Self> {420 let mut out = Vec::with_capacity(self.len());421422 for value in self.iter(s) {423 out.push(mapper(value?)?);424 }425426 Ok(Self::Eager(Cc::new(out)))427 }428429 pub fn filter(self, s: State, filter: impl Fn(&Val) -> Result<bool>) -> Result<Self> {430 let mut out = Vec::with_capacity(self.len());431432 for value in self.iter(s) {433 let value = value?;434 if filter(&value)? {435 out.push(value);436 }437 }438439 Ok(Self::Eager(Cc::new(out)))440 }441442 pub fn ptr_eq(a: &Self, b: &Self) -> bool {443 match (a, b) {444 (Self::Lazy(a), Self::Lazy(b)) => Cc::ptr_eq(a, b),445 (Self::Eager(a), Self::Eager(b)) => Cc::ptr_eq(a, b),446 _ => false,447 }448 }449}450451impl From<Vec<Thunk<Val>>> for ArrValue {452 fn from(v: Vec<Thunk<Val>>) -> Self {453 Self::Lazy(Cc::new(v))454 }455}456457impl From<Vec<Val>> for ArrValue {458 fn from(v: Vec<Val>) -> Self {459 Self::Eager(Cc::new(v))460 }461}462463#[allow(clippy::module_name_repetitions)]464pub enum IndexableVal {465 Str(IStr),466 Arr(ArrValue),467}468impl IndexableVal {469 pub fn slice(470 self,471 index: Option<BoundedUsize<0, { i32::MAX as usize }>>,472 end: Option<BoundedUsize<0, { i32::MAX as usize }>>,473 step: Option<BoundedUsize<1, { i32::MAX as usize }>>,474 ) -> Result<Self> {475 match &self {476 IndexableVal::Str(s) => {477 let index = index.as_deref().copied().unwrap_or(0);478 let end = end.as_deref().copied().unwrap_or(usize::MAX);479 let step = step.as_deref().copied().unwrap_or(1);480481 if index >= end {482 return Ok(Self::Str("".into()));483 }484485 Ok(Self::Str(486 (s.chars()487 .skip(index)488 .take(end - index)489 .step_by(step)490 .collect::<String>())491 .into(),492 ))493 }494 IndexableVal::Arr(arr) => {495 let index = index.as_deref().copied().unwrap_or(0);496 let end = end.as_deref().copied().unwrap_or(usize::MAX).min(arr.len());497 let step = step.as_deref().copied().unwrap_or(1);498499 if index >= end {500 return Ok(Self::Arr(ArrValue::new_eager()));501 }502503 Ok(Self::Arr(ArrValue::Slice(Box::new(Slice {504 inner: arr.clone(),505 from: index as u32,506 to: end as u32,507 step: step as u32,508 }))))509 }510 }511 }512}513514#[derive(Debug, Clone, Trace)]515pub enum Val {516 Bool(bool),517 Null,518 Str(IStr),519 Num(f64),520 Arr(ArrValue),521 Obj(ObjValue),522 Func(FuncVal),523}524525impl From<IndexableVal> for Val {526 fn from(v: IndexableVal) -> Self {527 match v {528 IndexableVal::Str(s) => Self::Str(s),529 IndexableVal::Arr(a) => Self::Arr(a),530 }531 }532}533534#[cfg(target_pointer_width = "64")]535static_assertions::assert_eq_size!(Val, [u8; 32]);536537impl Val {538 pub const fn as_bool(&self) -> Option<bool> {539 match self {540 Self::Bool(v) => Some(*v),541 _ => None,542 }543 }544 pub const fn as_null(&self) -> Option<()> {545 match self {546 Self::Null => Some(()),547 _ => None,548 }549 }550 pub fn as_str(&self) -> Option<IStr> {551 match self {552 Self::Str(s) => Some(s.clone()),553 _ => None,554 }555 }556 pub const fn as_num(&self) -> Option<f64> {557 match self {558 Self::Num(n) => Some(*n),559 _ => None,560 }561 }562 pub fn as_arr(&self) -> Option<ArrValue> {563 match self {564 Self::Arr(a) => Some(a.clone()),565 _ => None,566 }567 }568 pub fn as_obj(&self) -> Option<ObjValue> {569 match self {570 Self::Obj(o) => Some(o.clone()),571 _ => None,572 }573 }574 pub fn as_func(&self) -> Option<FuncVal> {575 match self {576 Self::Func(f) => Some(f.clone()),577 _ => None,578 }579 }580581 /// Creates `Val::Num` after checking for numeric overflow.582 /// As numbers are `f64`, we can just check for their finity.583 pub fn new_checked_num(num: f64) -> Result<Self> {584 if num.is_finite() {585 Ok(Self::Num(num))586 } else {587 throw!(RuntimeError("overflow".into()))588 }589 }590591 pub const fn value_type(&self) -> ValType {592 match self {593 Self::Str(..) => ValType::Str,594 Self::Num(..) => ValType::Num,595 Self::Arr(..) => ValType::Arr,596 Self::Obj(..) => ValType::Obj,597 Self::Bool(_) => ValType::Bool,598 Self::Null => ValType::Null,599 Self::Func(..) => ValType::Func,600 }601 }602603 pub fn to_string(&self, s: State) -> Result<IStr> {604 Ok(match self {605 Self::Bool(true) => "true".into(),606 Self::Bool(false) => "false".into(),607 Self::Null => "null".into(),608 Self::Str(s) => s.clone(),609 v => manifest_json_ex(610 s,611 v,612 &ManifestJsonOptions {613 padding: "",614 mtype: ManifestType::ToString,615 newline: "\n",616 key_val_sep: ": ",617 #[cfg(feature = "exp-preserve-order")]618 preserve_order: false,619 },620 )?621 .into(),622 })623 }624625 /// Expects value to be object, outputs (key, manifested value) pairs626 pub fn manifest_multi(&self, s: State, ty: &ManifestFormat) -> Result<Vec<(IStr, IStr)>> {627 let obj = match self {628 Self::Obj(obj) => obj,629 _ => throw!(MultiManifestOutputIsNotAObject),630 };631 let keys = obj.fields(632 #[cfg(feature = "exp-preserve-order")]633 ty.preserve_order(),634 );635 let mut out = Vec::with_capacity(keys.len());636 for key in keys {637 let value = obj638 .get(s.clone(), key.clone())?639 .expect("item in object")640 .manifest(s.clone(), ty)?;641 out.push((key, value));642 }643 Ok(out)644 }645646 /// Expects value to be array, outputs manifested values647 pub fn manifest_stream(&self, s: State, ty: &ManifestFormat) -> Result<Vec<IStr>> {648 let arr = match self {649 Self::Arr(a) => a,650 _ => throw!(StreamManifestOutputIsNotAArray),651 };652 let mut out = Vec::with_capacity(arr.len());653 for i in arr.iter(s.clone()) {654 out.push(i?.manifest(s.clone(), ty)?);655 }656 Ok(out)657 }658659 pub fn manifest(&self, s: State, ty: &ManifestFormat) -> Result<IStr> {660 Ok(match ty {661 ManifestFormat::YamlStream(format) => {662 let arr = match self {663 Self::Arr(a) => a,664 _ => throw!(StreamManifestOutputIsNotAArray),665 };666 let mut out = String::new();667668 match format as &ManifestFormat {669 ManifestFormat::YamlStream(_) => throw!(StreamManifestOutputCannotBeRecursed),670 ManifestFormat::String => throw!(StreamManifestCannotNestString),671 _ => {}672 };673674 if !arr.is_empty() {675 for v in arr.iter(s.clone()) {676 out.push_str("---\n");677 out.push_str(&v?.manifest(s.clone(), format)?);678 out.push('\n');679 }680 out.push_str("...");681 }682683 out.into()684 }685 ManifestFormat::Yaml {686 padding,687 #[cfg(feature = "exp-preserve-order")]688 preserve_order,689 } => self.to_yaml(690 s,691 *padding,692 #[cfg(feature = "exp-preserve-order")]693 *preserve_order,694 )?,695 ManifestFormat::Json {696 padding,697 #[cfg(feature = "exp-preserve-order")]698 preserve_order,699 } => self.to_json(700 s,701 *padding,702 #[cfg(feature = "exp-preserve-order")]703 *preserve_order,704 )?,705 ManifestFormat::ToString => self.to_string(s)?,706 ManifestFormat::String => match self {707 Self::Str(s) => s.clone(),708 _ => throw!(StringManifestOutputIsNotAString),709 },710 })711 }712713 /// For manifestification714 pub fn to_json(715 &self,716 s: State,717 padding: usize,718 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,719 ) -> Result<IStr> {720 manifest_json_ex(721 s,722 self,723 &ManifestJsonOptions {724 padding: &" ".repeat(padding),725 mtype: if padding == 0 {726 ManifestType::Minify727 } else {728 ManifestType::Manifest729 },730 newline: "\n",731 key_val_sep: ": ",732 #[cfg(feature = "exp-preserve-order")]733 preserve_order,734 },735 )736 .map(Into::into)737 }738739 /// Calls `std.manifestJson`740 pub fn to_std_json(741 &self,742 s: State,743 padding: usize,744 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,745 ) -> Result<Rc<str>> {746 manifest_json_ex(747 s,748 self,749 &ManifestJsonOptions {750 padding: &" ".repeat(padding),751 mtype: ManifestType::Std,752 newline: "\n",753 key_val_sep: ": ",754 #[cfg(feature = "exp-preserve-order")]755 preserve_order,756 },757 )758 .map(Into::into)759 }760761 pub fn to_yaml(762 &self,763 s: State,764 padding: usize,765 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,766 ) -> Result<IStr> {767 let padding = &" ".repeat(padding);768 manifest_yaml_ex(769 s,770 self,771 &ManifestYamlOptions {772 padding,773 arr_element_padding: padding,774 quote_keys: false,775 #[cfg(feature = "exp-preserve-order")]776 preserve_order,777 },778 )779 .map(Into::into)780 }781 pub fn into_indexable(self) -> Result<IndexableVal> {782 Ok(match self {783 Val::Str(s) => IndexableVal::Str(s),784 Val::Arr(arr) => IndexableVal::Arr(arr),785 _ => throw!(ValueIsNotIndexable(self.value_type())),786 })787 }788}789790const fn is_function_like(val: &Val) -> bool {791 matches!(val, Val::Func(_))792}793794/// Native implementation of `std.primitiveEquals`795pub fn primitive_equals(val_a: &Val, val_b: &Val) -> Result<bool> {796 Ok(match (val_a, val_b) {797 (Val::Bool(a), Val::Bool(b)) => a == b,798 (Val::Null, Val::Null) => true,799 (Val::Str(a), Val::Str(b)) => a == b,800 (Val::Num(a), Val::Num(b)) => (a - b).abs() <= f64::EPSILON,801 (Val::Arr(_), Val::Arr(_)) => throw!(RuntimeError(802 "primitiveEquals operates on primitive types, got array".into(),803 )),804 (Val::Obj(_), Val::Obj(_)) => throw!(RuntimeError(805 "primitiveEquals operates on primitive types, got object".into(),806 )),807 (a, b) if is_function_like(a) && is_function_like(b) => {808 throw!(RuntimeError("cannot test equality of functions".into()))809 }810 (_, _) => false,811 })812}813814/// Native implementation of `std.equals`815pub fn equals(s: State, val_a: &Val, val_b: &Val) -> Result<bool> {816 if val_a.value_type() != val_b.value_type() {817 return Ok(false);818 }819 match (val_a, val_b) {820 (Val::Arr(a), Val::Arr(b)) => {821 if ArrValue::ptr_eq(a, b) {822 return Ok(true);823 }824 if a.len() != b.len() {825 return Ok(false);826 }827 for (a, b) in a.iter(s.clone()).zip(b.iter(s.clone())) {828 if !equals(s.clone(), &a?, &b?)? {829 return Ok(false);830 }831 }832 Ok(true)833 }834 (Val::Obj(a), Val::Obj(b)) => {835 if ObjValue::ptr_eq(a, b) {836 return Ok(true);837 }838 let fields = a.fields(839 #[cfg(feature = "exp-preserve-order")]840 false,841 );842 if fields843 != b.fields(844 #[cfg(feature = "exp-preserve-order")]845 false,846 ) {847 return Ok(false);848 }849 for field in fields {850 if !equals(851 s.clone(),852 &a.get(s.clone(), field.clone())?.expect("field exists"),853 &b.get(s.clone(), field)?.expect("field exists"),854 )? {855 return Ok(false);856 }857 }858 Ok(true)859 }860 (a, b) => Ok(primitive_equals(a, b)?),861 }862}1use std::{cell::RefCell, fmt::Debug, rc::Rc};23use jrsonnet_gcmodule::{Cc, Trace};4use jrsonnet_interner::{IBytes, IStr};5use jrsonnet_types::ValType;67use crate::{8 error::{Error::*, LocError},9 function::FuncVal,10 gc::{GcHashMap, TraceBox},11 stdlib::manifest::{12 manifest_json_ex, manifest_yaml_ex, ManifestJsonOptions, ManifestType, ManifestYamlOptions,13 },14 throw,15 typed::BoundedUsize,16 ObjValue, Result, State, Unbound, WeakObjValue,17};1819pub trait ThunkValue: Trace {20 type Output;21 fn get(self: Box<Self>, s: State) -> Result<Self::Output>;22}2324#[derive(Trace)]25enum ThunkInner<T: Trace> {26 Computed(T),27 Errored(LocError),28 Waiting(TraceBox<dyn ThunkValue<Output = T>>),29 Pending,30}3132#[allow(clippy::module_name_repetitions)]33#[derive(Clone, Trace)]34pub struct Thunk<T: Trace>(Cc<RefCell<ThunkInner<T>>>);3536impl<T> Thunk<T>37where38 T: Clone + Trace,39{40 pub fn new(f: TraceBox<dyn ThunkValue<Output = T>>) -> Self {41 Self(Cc::new(RefCell::new(ThunkInner::Waiting(f))))42 }43 pub fn evaluated(val: T) -> Self {44 Self(Cc::new(RefCell::new(ThunkInner::Computed(val))))45 }46 pub fn force(&self, s: State) -> Result<()> {47 self.evaluate(s)?;48 Ok(())49 }50 pub fn evaluate(&self, s: State) -> Result<T> {51 match &*self.0.borrow() {52 ThunkInner::Computed(v) => return Ok(v.clone()),53 ThunkInner::Errored(e) => return Err(e.clone()),54 ThunkInner::Pending => return Err(InfiniteRecursionDetected.into()),55 ThunkInner::Waiting(..) => (),56 };57 let value = if let ThunkInner::Waiting(value) =58 std::mem::replace(&mut *self.0.borrow_mut(), ThunkInner::Pending)59 {60 value61 } else {62 unreachable!()63 };64 let new_value = match value.0.get(s) {65 Ok(v) => v,66 Err(e) => {67 *self.0.borrow_mut() = ThunkInner::Errored(e.clone());68 return Err(e);69 }70 };71 *self.0.borrow_mut() = ThunkInner::Computed(new_value.clone());72 Ok(new_value)73 }74}7576type CacheKey = (Option<WeakObjValue>, Option<WeakObjValue>);7778#[derive(Trace, Clone)]79pub struct CachedUnbound<I, T>80where81 I: Unbound<Bound = T>,82 T: Trace,83{84 cache: Cc<RefCell<GcHashMap<CacheKey, T>>>,85 value: I,86}87impl<I: Unbound<Bound = T>, T: Trace> CachedUnbound<I, T> {88 pub fn new(value: I) -> Self {89 Self {90 cache: Cc::new(RefCell::new(GcHashMap::new())),91 value,92 }93 }94}95impl<I: Unbound<Bound = T>, T: Clone + Trace> Unbound for CachedUnbound<I, T> {96 type Bound = T;97 fn bind(&self, s: State, sup: Option<ObjValue>, this: Option<ObjValue>) -> Result<T> {98 let cache_key = (99 sup.as_ref().map(|s| s.clone().downgrade()),100 this.as_ref().map(|t| t.clone().downgrade()),101 );102 {103 if let Some(t) = self.cache.borrow().get(&cache_key) {104 return Ok(t.clone());105 }106 }107 let bound = self.value.bind(s, sup, this)?;108109 {110 let mut cache = self.cache.borrow_mut();111 cache.insert(cache_key, bound.clone());112 }113114 Ok(bound)115 }116}117118impl<T: Debug + Trace> Debug for Thunk<T> {119 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {120 write!(f, "Lazy")121 }122}123impl<T: Trace> PartialEq for Thunk<T> {124 fn eq(&self, other: &Self) -> bool {125 Cc::ptr_eq(&self.0, &other.0)126 }127}128129#[derive(Clone)]130pub enum ManifestFormat {131 YamlStream(Box<ManifestFormat>),132 Yaml {133 padding: usize,134 #[cfg(feature = "exp-preserve-order")]135 preserve_order: bool,136 },137 Json {138 padding: usize,139 #[cfg(feature = "exp-preserve-order")]140 preserve_order: bool,141 },142 ToString,143 String,144}145impl ManifestFormat {146 #[cfg(feature = "exp-preserve-order")]147 fn preserve_order(&self) -> bool {148 match self {149 ManifestFormat::YamlStream(s) => s.preserve_order(),150 ManifestFormat::Yaml { preserve_order, .. } => *preserve_order,151 ManifestFormat::Json { preserve_order, .. } => *preserve_order,152 ManifestFormat::ToString => false,153 ManifestFormat::String => false,154 }155 }156}157158#[derive(Debug, Clone, Trace)]159pub struct Slice {160 pub(crate) inner: ArrValue,161 pub(crate) from: u32,162 pub(crate) to: u32,163 pub(crate) step: u32,164}165impl Slice {166 const fn from(&self) -> usize {167 self.from as usize168 }169 const fn to(&self) -> usize {170 self.to as usize171 }172 const fn step(&self) -> usize {173 self.step as usize174 }175 const fn len(&self) -> usize {176 // TODO: use div_ceil177 let diff = self.to() - self.from();178 let rem = diff % self.step();179 let div = diff / self.step();180181 if rem == 0 {182 div183 } else {184 div + 1185 }186 }187}188189#[derive(Debug, Clone, Trace)]190// may contrain other ArrValue191#[trace(tracking(force))]192pub enum ArrValue {193 Bytes(#[trace(skip)] IBytes),194 Lazy(Cc<Vec<Thunk<Val>>>),195 Eager(Cc<Vec<Val>>),196 Extended(Box<(Self, Self)>),197 Range(i32, i32),198 Slice(Box<Slice>),199 Reversed(Box<Self>),200}201202#[cfg(target_pointer_width = "64")]203static_assertions::assert_eq_size!(ArrValue, [u8; 16]);204205impl ArrValue {206 pub fn new_eager() -> Self {207 Self::Eager(Cc::new(Vec::new()))208 }209 pub fn empty() -> Self {210 Self::new_range(0, 0)211 }212213 /// # Panics214 /// If a > b215 #[inline]216 pub fn new_range(a: i32, b: i32) -> Self {217 assert!(a <= b);218 Self::Range(a, b)219 }220221 /// # Panics222 /// If passed numbers are incorrect223 #[must_use]224 pub fn slice(self, from: Option<usize>, to: Option<usize>, step: Option<usize>) -> Self {225 let len = self.len();226 let from = from.unwrap_or(0);227 let to = to.unwrap_or(len).min(len);228 let step = step.unwrap_or(1);229 assert!(from < to);230 assert!(step > 0);231232 Self::Slice(Box::new(Slice {233 inner: self,234 from: from as u32,235 to: to as u32,236 step: step as u32,237 }))238 }239240 pub fn len(&self) -> usize {241 match self {242 Self::Bytes(i) => i.len(),243 Self::Lazy(l) => l.len(),244 Self::Eager(e) => e.len(),245 Self::Extended(v) => v.0.len() + v.1.len(),246 Self::Range(a, b) => a.abs_diff(*b) as usize + 1,247 Self::Reversed(i) => i.len(),248 Self::Slice(s) => s.len(),249 }250 }251252 pub fn is_empty(&self) -> bool {253 self.len() == 0254 }255256 pub fn get(&self, s: State, index: usize) -> Result<Option<Val>> {257 match self {258 Self::Bytes(i) => i259 .get(index)260 .map_or(Ok(None), |v| Ok(Some(Val::Num(f64::from(*v))))),261 Self::Lazy(vec) => {262 if let Some(v) = vec.get(index) {263 Ok(Some(v.evaluate(s)?))264 } else {265 Ok(None)266 }267 }268 Self::Eager(vec) => Ok(vec.get(index).cloned()),269 Self::Extended(v) => {270 let a_len = v.0.len();271 if a_len > index {272 v.0.get(s, index)273 } else {274 v.1.get(s, index - a_len)275 }276 }277 Self::Range(a, _) => {278 if index >= self.len() {279 return Ok(None);280 }281 Ok(Some(Val::Num(((*a as isize) + index as isize) as f64)))282 }283 Self::Reversed(v) => {284 let len = v.len();285 if index >= len {286 return Ok(None);287 }288 v.get(s, len - index - 1)289 }290 Self::Slice(v) => {291 let index = v.from() + index * v.step();292 if index >= v.to() {293 return Ok(None);294 }295 v.inner.get(s, index)296 }297 }298 }299300 pub fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {301 match self {302 Self::Bytes(i) => i303 .get(index)304 .map(|b| Thunk::evaluated(Val::Num(f64::from(*b)))),305 Self::Lazy(vec) => vec.get(index).cloned(),306 Self::Eager(vec) => vec.get(index).cloned().map(Thunk::evaluated),307 Self::Extended(v) => {308 let a_len = v.0.len();309 if a_len > index {310 v.0.get_lazy(index)311 } else {312 v.1.get_lazy(index - a_len)313 }314 }315 Self::Range(a, _) => {316 if index >= self.len() {317 return None;318 }319 Some(Thunk::evaluated(Val::Num(320 ((*a as isize) + index as isize) as f64,321 )))322 }323 Self::Reversed(v) => {324 let len = v.len();325 if index >= len {326 return None;327 }328 v.get_lazy(len - index - 1)329 }330 Self::Slice(s) => {331 let index = s.from() + index * s.step();332 if index >= s.to() {333 return None;334 }335 s.inner.get_lazy(index)336 }337 }338 }339340 pub fn evaluated(&self, s: State) -> Result<Cc<Vec<Val>>> {341 Ok(match self {342 Self::Bytes(i) => {343 let mut out = Vec::with_capacity(i.len());344 for v in i.iter() {345 out.push(Val::Num(f64::from(*v)));346 }347 Cc::new(out)348 }349 Self::Lazy(vec) => {350 let mut out = Vec::with_capacity(vec.len());351 for item in vec.iter() {352 out.push(item.evaluate(s.clone())?);353 }354 Cc::new(out)355 }356 Self::Eager(vec) => vec.clone(),357 Self::Extended(_v) => {358 let mut out = Vec::with_capacity(self.len());359 for item in self.iter(s) {360 out.push(item?);361 }362 Cc::new(out)363 }364 Self::Range(a, b) => {365 let mut out = Vec::with_capacity(self.len());366 for i in *a..*b {367 out.push(Val::Num(f64::from(i)));368 }369 Cc::new(out)370 }371 Self::Reversed(r) => {372 let mut r = r.evaluated(s)?;373 Cc::update_with(&mut r, |v| v.reverse());374 r375 }376 Self::Slice(v) => {377 let mut out = Vec::with_capacity(v.inner.len());378 for v in v379 .inner380 .iter_lazy()381 .skip(v.from())382 .take(v.to() - v.from())383 .step_by(v.step())384 {385 out.push(v.evaluate(s.clone())?);386 }387 Cc::new(out)388 }389 })390 }391392 pub fn iter(&self, s: State) -> impl DoubleEndedIterator<Item = Result<Val>> + '_ {393 (0..self.len()).map(move |idx| match self {394 Self::Bytes(b) => Ok(Val::Num(f64::from(b[idx]))),395 Self::Lazy(l) => l[idx].evaluate(s.clone()),396 Self::Eager(e) => Ok(e[idx].clone()),397 Self::Extended(..) | Self::Range(..) | Self::Reversed(..) | Self::Slice(..) => {398 self.get(s.clone(), idx).map(|e| e.expect("idx < len"))399 }400 })401 }402403 pub fn iter_lazy(&self) -> impl DoubleEndedIterator<Item = Thunk<Val>> + '_ {404 (0..self.len()).map(move |idx| match self {405 Self::Bytes(b) => Thunk::evaluated(Val::Num(f64::from(b[idx]))),406 Self::Lazy(l) => l[idx].clone(),407 Self::Eager(e) => Thunk::evaluated(e[idx].clone()),408 Self::Slice(..) | Self::Extended(..) | Self::Range(..) | Self::Reversed(..) => {409 self.get_lazy(idx).expect("idx < len")410 }411 })412 }413414 #[must_use]415 pub fn reversed(self) -> Self {416 Self::Reversed(Box::new(self))417 }418419 pub fn map(self, s: State, mapper: impl Fn(Val) -> Result<Val>) -> Result<Self> {420 let mut out = Vec::with_capacity(self.len());421422 for value in self.iter(s) {423 out.push(mapper(value?)?);424 }425426 Ok(Self::Eager(Cc::new(out)))427 }428429 pub fn filter(self, s: State, filter: impl Fn(&Val) -> Result<bool>) -> Result<Self> {430 let mut out = Vec::with_capacity(self.len());431432 for value in self.iter(s) {433 let value = value?;434 if filter(&value)? {435 out.push(value);436 }437 }438439 Ok(Self::Eager(Cc::new(out)))440 }441442 pub fn ptr_eq(a: &Self, b: &Self) -> bool {443 match (a, b) {444 (Self::Lazy(a), Self::Lazy(b)) => Cc::ptr_eq(a, b),445 (Self::Eager(a), Self::Eager(b)) => Cc::ptr_eq(a, b),446 _ => false,447 }448 }449}450451impl From<Vec<Thunk<Val>>> for ArrValue {452 fn from(v: Vec<Thunk<Val>>) -> Self {453 Self::Lazy(Cc::new(v))454 }455}456457impl From<Vec<Val>> for ArrValue {458 fn from(v: Vec<Val>) -> Self {459 Self::Eager(Cc::new(v))460 }461}462463#[allow(clippy::module_name_repetitions)]464pub enum IndexableVal {465 Str(IStr),466 Arr(ArrValue),467}468impl IndexableVal {469 pub fn slice(470 self,471 index: Option<BoundedUsize<0, { i32::MAX as usize }>>,472 end: Option<BoundedUsize<0, { i32::MAX as usize }>>,473 step: Option<BoundedUsize<1, { i32::MAX as usize }>>,474 ) -> Result<Self> {475 match &self {476 IndexableVal::Str(s) => {477 let index = index.as_deref().copied().unwrap_or(0);478 let end = end.as_deref().copied().unwrap_or(usize::MAX);479 let step = step.as_deref().copied().unwrap_or(1);480481 if index >= end {482 return Ok(Self::Str("".into()));483 }484485 Ok(Self::Str(486 (s.chars()487 .skip(index)488 .take(end - index)489 .step_by(step)490 .collect::<String>())491 .into(),492 ))493 }494 IndexableVal::Arr(arr) => {495 let index = index.as_deref().copied().unwrap_or(0);496 let end = end.as_deref().copied().unwrap_or(usize::MAX).min(arr.len());497 let step = step.as_deref().copied().unwrap_or(1);498499 if index >= end {500 return Ok(Self::Arr(ArrValue::new_eager()));501 }502503 Ok(Self::Arr(ArrValue::Slice(Box::new(Slice {504 inner: arr.clone(),505 from: index as u32,506 to: end as u32,507 step: step as u32,508 }))))509 }510 }511 }512}513514#[derive(Debug, Clone, Trace)]515pub enum Val {516 Bool(bool),517 Null,518 Str(IStr),519 Num(f64),520 Arr(ArrValue),521 Obj(ObjValue),522 Func(FuncVal),523}524525impl From<IndexableVal> for Val {526 fn from(v: IndexableVal) -> Self {527 match v {528 IndexableVal::Str(s) => Self::Str(s),529 IndexableVal::Arr(a) => Self::Arr(a),530 }531 }532}533534// Broken between stable and nightly, as there is new layout size optimization535// #[cfg(target_pointer_width = "64")]536// static_assertions::assert_eq_size!(Val, [u8; 24]);537538impl Val {539 pub const fn as_bool(&self) -> Option<bool> {540 match self {541 Self::Bool(v) => Some(*v),542 _ => None,543 }544 }545 pub const fn as_null(&self) -> Option<()> {546 match self {547 Self::Null => Some(()),548 _ => None,549 }550 }551 pub fn as_str(&self) -> Option<IStr> {552 match self {553 Self::Str(s) => Some(s.clone()),554 _ => None,555 }556 }557 pub const fn as_num(&self) -> Option<f64> {558 match self {559 Self::Num(n) => Some(*n),560 _ => None,561 }562 }563 pub fn as_arr(&self) -> Option<ArrValue> {564 match self {565 Self::Arr(a) => Some(a.clone()),566 _ => None,567 }568 }569 pub fn as_obj(&self) -> Option<ObjValue> {570 match self {571 Self::Obj(o) => Some(o.clone()),572 _ => None,573 }574 }575 pub fn as_func(&self) -> Option<FuncVal> {576 match self {577 Self::Func(f) => Some(f.clone()),578 _ => None,579 }580 }581582 /// Creates `Val::Num` after checking for numeric overflow.583 /// As numbers are `f64`, we can just check for their finity.584 pub fn new_checked_num(num: f64) -> Result<Self> {585 if num.is_finite() {586 Ok(Self::Num(num))587 } else {588 throw!(RuntimeError("overflow".into()))589 }590 }591592 pub const fn value_type(&self) -> ValType {593 match self {594 Self::Str(..) => ValType::Str,595 Self::Num(..) => ValType::Num,596 Self::Arr(..) => ValType::Arr,597 Self::Obj(..) => ValType::Obj,598 Self::Bool(_) => ValType::Bool,599 Self::Null => ValType::Null,600 Self::Func(..) => ValType::Func,601 }602 }603604 pub fn to_string(&self, s: State) -> Result<IStr> {605 Ok(match self {606 Self::Bool(true) => "true".into(),607 Self::Bool(false) => "false".into(),608 Self::Null => "null".into(),609 Self::Str(s) => s.clone(),610 v => manifest_json_ex(611 s,612 v,613 &ManifestJsonOptions {614 padding: "",615 mtype: ManifestType::ToString,616 newline: "\n",617 key_val_sep: ": ",618 #[cfg(feature = "exp-preserve-order")]619 preserve_order: false,620 },621 )?622 .into(),623 })624 }625626 /// Expects value to be object, outputs (key, manifested value) pairs627 pub fn manifest_multi(&self, s: State, ty: &ManifestFormat) -> Result<Vec<(IStr, IStr)>> {628 let obj = match self {629 Self::Obj(obj) => obj,630 _ => throw!(MultiManifestOutputIsNotAObject),631 };632 let keys = obj.fields(633 #[cfg(feature = "exp-preserve-order")]634 ty.preserve_order(),635 );636 let mut out = Vec::with_capacity(keys.len());637 for key in keys {638 let value = obj639 .get(s.clone(), key.clone())?640 .expect("item in object")641 .manifest(s.clone(), ty)?;642 out.push((key, value));643 }644 Ok(out)645 }646647 /// Expects value to be array, outputs manifested values648 pub fn manifest_stream(&self, s: State, ty: &ManifestFormat) -> Result<Vec<IStr>> {649 let arr = match self {650 Self::Arr(a) => a,651 _ => throw!(StreamManifestOutputIsNotAArray),652 };653 let mut out = Vec::with_capacity(arr.len());654 for i in arr.iter(s.clone()) {655 out.push(i?.manifest(s.clone(), ty)?);656 }657 Ok(out)658 }659660 pub fn manifest(&self, s: State, ty: &ManifestFormat) -> Result<IStr> {661 Ok(match ty {662 ManifestFormat::YamlStream(format) => {663 let arr = match self {664 Self::Arr(a) => a,665 _ => throw!(StreamManifestOutputIsNotAArray),666 };667 let mut out = String::new();668669 match format as &ManifestFormat {670 ManifestFormat::YamlStream(_) => throw!(StreamManifestOutputCannotBeRecursed),671 ManifestFormat::String => throw!(StreamManifestCannotNestString),672 _ => {}673 };674675 if !arr.is_empty() {676 for v in arr.iter(s.clone()) {677 out.push_str("---\n");678 out.push_str(&v?.manifest(s.clone(), format)?);679 out.push('\n');680 }681 out.push_str("...");682 }683684 out.into()685 }686 ManifestFormat::Yaml {687 padding,688 #[cfg(feature = "exp-preserve-order")]689 preserve_order,690 } => self.to_yaml(691 s,692 *padding,693 #[cfg(feature = "exp-preserve-order")]694 *preserve_order,695 )?,696 ManifestFormat::Json {697 padding,698 #[cfg(feature = "exp-preserve-order")]699 preserve_order,700 } => self.to_json(701 s,702 *padding,703 #[cfg(feature = "exp-preserve-order")]704 *preserve_order,705 )?,706 ManifestFormat::ToString => self.to_string(s)?,707 ManifestFormat::String => match self {708 Self::Str(s) => s.clone(),709 _ => throw!(StringManifestOutputIsNotAString),710 },711 })712 }713714 /// For manifestification715 pub fn to_json(716 &self,717 s: State,718 padding: usize,719 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,720 ) -> Result<IStr> {721 manifest_json_ex(722 s,723 self,724 &ManifestJsonOptions {725 padding: &" ".repeat(padding),726 mtype: if padding == 0 {727 ManifestType::Minify728 } else {729 ManifestType::Manifest730 },731 newline: "\n",732 key_val_sep: ": ",733 #[cfg(feature = "exp-preserve-order")]734 preserve_order,735 },736 )737 .map(Into::into)738 }739740 /// Calls `std.manifestJson`741 pub fn to_std_json(742 &self,743 s: State,744 padding: usize,745 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,746 ) -> Result<Rc<str>> {747 manifest_json_ex(748 s,749 self,750 &ManifestJsonOptions {751 padding: &" ".repeat(padding),752 mtype: ManifestType::Std,753 newline: "\n",754 key_val_sep: ": ",755 #[cfg(feature = "exp-preserve-order")]756 preserve_order,757 },758 )759 .map(Into::into)760 }761762 pub fn to_yaml(763 &self,764 s: State,765 padding: usize,766 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,767 ) -> Result<IStr> {768 let padding = &" ".repeat(padding);769 manifest_yaml_ex(770 s,771 self,772 &ManifestYamlOptions {773 padding,774 arr_element_padding: padding,775 quote_keys: false,776 #[cfg(feature = "exp-preserve-order")]777 preserve_order,778 },779 )780 .map(Into::into)781 }782 pub fn into_indexable(self) -> Result<IndexableVal> {783 Ok(match self {784 Val::Str(s) => IndexableVal::Str(s),785 Val::Arr(arr) => IndexableVal::Arr(arr),786 _ => throw!(ValueIsNotIndexable(self.value_type())),787 })788 }789}790791const fn is_function_like(val: &Val) -> bool {792 matches!(val, Val::Func(_))793}794795/// Native implementation of `std.primitiveEquals`796pub fn primitive_equals(val_a: &Val, val_b: &Val) -> Result<bool> {797 Ok(match (val_a, val_b) {798 (Val::Bool(a), Val::Bool(b)) => a == b,799 (Val::Null, Val::Null) => true,800 (Val::Str(a), Val::Str(b)) => a == b,801 (Val::Num(a), Val::Num(b)) => (a - b).abs() <= f64::EPSILON,802 (Val::Arr(_), Val::Arr(_)) => throw!(RuntimeError(803 "primitiveEquals operates on primitive types, got array".into(),804 )),805 (Val::Obj(_), Val::Obj(_)) => throw!(RuntimeError(806 "primitiveEquals operates on primitive types, got object".into(),807 )),808 (a, b) if is_function_like(a) && is_function_like(b) => {809 throw!(RuntimeError("cannot test equality of functions".into()))810 }811 (_, _) => false,812 })813}814815/// Native implementation of `std.equals`816pub fn equals(s: State, val_a: &Val, val_b: &Val) -> Result<bool> {817 if val_a.value_type() != val_b.value_type() {818 return Ok(false);819 }820 match (val_a, val_b) {821 (Val::Arr(a), Val::Arr(b)) => {822 if ArrValue::ptr_eq(a, b) {823 return Ok(true);824 }825 if a.len() != b.len() {826 return Ok(false);827 }828 for (a, b) in a.iter(s.clone()).zip(b.iter(s.clone())) {829 if !equals(s.clone(), &a?, &b?)? {830 return Ok(false);831 }832 }833 Ok(true)834 }835 (Val::Obj(a), Val::Obj(b)) => {836 if ObjValue::ptr_eq(a, b) {837 return Ok(true);838 }839 let fields = a.fields(840 #[cfg(feature = "exp-preserve-order")]841 false,842 );843 if fields844 != b.fields(845 #[cfg(feature = "exp-preserve-order")]846 false,847 ) {848 return Ok(false);849 }850 for field in fields {851 if !equals(852 s.clone(),853 &a.get(s.clone(), field.clone())?.expect("field exists"),854 &b.get(s.clone(), field)?.expect("field exists"),855 )? {856 return Ok(false);857 }858 }859 Ok(true)860 }861 (a, b) => Ok(primitive_equals(a, b)?),862 }863}crates/jrsonnet-stdlib/src/encoding.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/encoding.rs
+++ b/crates/jrsonnet-stdlib/src/encoding.rs
@@ -28,7 +28,7 @@
#[builtin]
pub fn builtin_base64_decode_bytes(input: IStr) -> Result<IBytes> {
- Ok(base64::decode(&input.as_bytes())
+ Ok(base64::decode(input.as_bytes())
.map_err(|_| RuntimeError("bad base64".into()))?
.as_slice()
.into())
@@ -36,6 +36,6 @@
#[builtin]
pub fn builtin_base64_decode(input: IStr) -> Result<String> {
- let bytes = base64::decode(&input.as_bytes()).map_err(|_| RuntimeError("bad base64".into()))?;
+ let bytes = base64::decode(input.as_bytes()).map_err(|_| RuntimeError("bad base64".into()))?;
Ok(String::from_utf8(bytes).map_err(|_| RuntimeError("bad utf8".into()))?)
}
crates/jrsonnet-stdlib/src/hash.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/hash.rs
+++ b/crates/jrsonnet-stdlib/src/hash.rs
@@ -2,5 +2,5 @@
#[builtin]
pub fn builtin_md5(str: IStr) -> Result<String> {
- Ok(format!("{:x}", md5::compute(&str.as_bytes())))
+ Ok(format!("{:x}", md5::compute(str.as_bytes())))
}
crates/jrsonnet-stdlib/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/lib.rs
+++ b/crates/jrsonnet-stdlib/src/lib.rs
@@ -366,7 +366,7 @@
#[builtin]
fn builtin_substr(str: IStr, from: usize, len: usize) -> Result<String> {
- Ok(str.chars().skip(from as usize).take(len as usize).collect())
+ Ok(str.chars().skip(from).take(len).collect())
}
#[builtin(fields(
@@ -380,7 +380,7 @@
.ext_vars
.get(&x)
.cloned()
- .ok_or(UndefinedExternalVariable(x))?
+ .ok_or_else(|| UndefinedExternalVariable(x))?
.evaluate_arg(s.clone(), ctx, true)?
.evaluate(s)?))
}
@@ -402,7 +402,7 @@
#[builtin]
fn builtin_char(n: u32) -> Result<char> {
- Ok(std::char::from_u32(n as u32).ok_or(InvalidUnicodeCodepointGot(n as u32))?)
+ Ok(std::char::from_u32(n).ok_or_else(|| InvalidUnicodeCodepointGot(n))?)
}
#[builtin(fields(