difftreelog
feat object destructuring defaults
in: master
5 files changed
crates/jrsonnet-evaluator/src/dynamic.rsdiffbeforeafterboth1use std::cell::RefCell;23use gcmodule::{Cc, Trace};45#[derive(Clone, Trace)]6pub struct Pending<V: Trace + 'static>(pub Cc<RefCell<Option<V>>>);7impl<T: Trace + 'static> Pending<T> {8 pub fn new() -> Self {9 Self(Cc::new(RefCell::new(None)))10 }11 /// # Panics12 /// If wrapper is filled already13 pub fn fill(self, value: T) {14 assert!(self.0.borrow().is_none(), "wrapper is filled already");15 self.0.borrow_mut().replace(value);16 }17}18impl<T: Clone + Trace + 'static> Pending<T> {19 /// # Panics20 /// If wrapper is not yet filled21 pub fn unwrap(&self) -> T {22 self.0.borrow().as_ref().cloned().unwrap()23 }24}2526impl<T: Trace + 'static> Default for Pending<T> {27 fn default() -> Self {28 Self::new()29 }30}1use std::cell::RefCell;23use gcmodule::{Cc, Trace};45#[derive(Clone, Trace)]6pub struct Pending<V: Trace + 'static>(pub Cc<RefCell<Option<V>>>);7impl<T: Trace + 'static> Pending<T> {8 pub fn new() -> Self {9 Self(Cc::new(RefCell::new(None)))10 }11 pub fn new_filled(v: T) -> Self {12 Self(Cc::new(RefCell::new(Some(v))))13 }14 /// # Panics15 /// If wrapper is filled already16 pub fn fill(self, value: T) {17 assert!(self.0.borrow().is_none(), "wrapper is filled already");18 self.0.borrow_mut().replace(value);19 }20}21impl<T: Clone + Trace + 'static> Pending<T> {22 /// # Panics23 /// If wrapper is not yet filled24 pub fn unwrap(&self) -> T {25 self.0.borrow().as_ref().cloned().unwrap()26 }27}2829impl<T: Trace + 'static> Default for Pending<T> {30 fn default() -> Self {31 Self::new()32 }33}crates/jrsonnet-evaluator/src/evaluate/destructure.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/evaluate/destructure.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate/destructure.rs
@@ -12,9 +12,11 @@
};
#[allow(clippy::too_many_lines)]
+#[allow(unused_variables)]
pub fn destruct(
d: &Destruct,
parent: Thunk<Val>,
+ fctx: Pending<Context>,
new_bindings: &mut GcHashMap<IStr, Thunk<Val>>,
) -> Result<()> {
match d {
@@ -89,6 +91,7 @@
full: full.clone(),
index: i,
})),
+ fctx.clone(),
new_bindings,
)?;
}
@@ -119,6 +122,7 @@
start: start.len(),
end: end.len(),
})),
+ fctx.clone(),
new_bindings,
)?;
}
@@ -151,6 +155,7 @@
index: i,
end: end.len(),
})),
+ fctx.clone(),
new_bindings,
)?;
}
@@ -189,36 +194,51 @@
Ok(obj)
}
}
- let field_names: Vec<_> = fields.iter().map(|f| f.0.clone()).collect();
+ let field_names: Vec<_> = fields
+ .iter()
+ .filter(|f| f.2.is_none())
+ .map(|f| f.0.clone())
+ .collect();
let full = Thunk::new(tb!(DataThunk {
parent,
field_names: field_names.clone(),
has_rest: rest.is_some()
}));
- for (field, d) in fields {
+ for (field, d, default) in fields {
#[derive(Trace)]
struct FieldThunk {
full: Thunk<ObjValue>,
field: IStr,
+ default: Option<(Pending<Context>, LocExpr)>,
}
impl ThunkValue for FieldThunk {
type Output = Val;
fn get(self: Box<Self>, s: State) -> Result<Self::Output> {
let full = self.full.evaluate(s.clone())?;
- let field = full.get(s, self.field)?.expect("shape is checked");
- Ok(field)
+ if let Some(field) = full.get(s.clone(), self.field)? {
+ Ok(field)
+ } else {
+ let (fctx, expr) = self.default.as_ref().expect("shape is checked");
+ Ok(evaluate(s, fctx.clone().unwrap(), &expr)?)
+ }
}
}
let value = Thunk::new(tb!(FieldThunk {
full: full.clone(),
- field: field.clone()
+ field: field.clone(),
+ default: default.clone().map(|e| (fctx.clone(), e)),
}));
if let Some(d) = d {
- destruct(d, value, new_bindings)?;
+ destruct(d, value, fctx.clone(), new_bindings)?;
} else {
- destruct(&Destruct::Full(field.clone()), value, new_bindings)?;
+ destruct(
+ &Destruct::Full(field.clone()),
+ value,
+ fctx.clone(),
+ new_bindings,
+ )?;
}
}
}
@@ -251,10 +271,10 @@
}
let data = Thunk::new(tb!(EvaluateThunkValue {
name: into.name(),
- fctx,
+ fctx: fctx.clone(),
expr: value.clone(),
}));
- destruct(into, data, new_bindings)?;
+ destruct(into, data, fctx, new_bindings)?;
}
BindSpec::Function {
name,
crates/jrsonnet-evaluator/src/function/parse.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/function/parse.rs
+++ b/crates/jrsonnet-evaluator/src/function/parse.rs
@@ -56,7 +56,12 @@
args.unnamed_iter(s.clone(), ctx.clone(), tailstrict, &mut |id, arg| {
let name = params[id].0.clone();
- destruct(&name, arg, &mut passed_args)?;
+ destruct(
+ &name,
+ arg,
+ Pending::new_filled(ctx.clone()),
+ &mut passed_args,
+ )?;
filled_positionals += 1;
Ok(())
})?;
@@ -96,6 +101,7 @@
name: param.0.name().unwrap_or_else(|| "<destruct>".into()),
value: param.1.clone().expect("default exists"),
})),
+ fctx.clone(),
&mut defaults,
)?;
if param.0.name().is_some() {
@@ -230,6 +236,7 @@
name: param.0.name().unwrap_or_else(|| "<destruct>".into()),
value: v.clone(),
})),
+ fctx.clone(),
&mut bindings,
)?;
} else {
@@ -238,6 +245,7 @@
Thunk::new(tb!(DependsOnUnbound(
param.0.name().unwrap_or_else(|| "<destruct>".into())
))),
+ fctx.clone(),
&mut bindings,
)?;
}
crates/jrsonnet-parser/src/expr.rsdiffbeforeafterboth--- a/crates/jrsonnet-parser/src/expr.rs
+++ b/crates/jrsonnet-parser/src/expr.rs
@@ -188,7 +188,7 @@
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
-#[derive(Debug, Clone, PartialEq, Eq, Trace)]
+#[derive(Debug, Clone, PartialEq, Trace)]
pub enum Destruct {
Full(IStr),
#[cfg(feature = "exp-destruct")]
@@ -201,7 +201,7 @@
},
#[cfg(feature = "exp-destruct")]
Object {
- fields: Vec<(IStr, Option<Destruct>)>,
+ fields: Vec<(IStr, Option<Destruct>, Option<LocExpr>)>,
rest: Option<DestructRest>,
},
}
crates/jrsonnet-parser/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-parser/src/lib.rs
+++ b/crates/jrsonnet-parser/src/lib.rs
@@ -1,4 +1,4 @@
-#![allow(clippy::redundant_closure_call)]
+#![allow(clippy::redundant_closure_call, clippy::derive_partial_eq_without_eq)]
use std::rc::Rc;
@@ -109,7 +109,7 @@
}
pub rule destruct_object(s: &ParserSettings) -> expr::Destruct
= "{" _
- fields:(name:id() _ into:(":" _ into:destruct(s) {into})? {(name, into)})**comma()
+ fields:(name:id() into:(_ ":" _ into:destruct(s) {into})? default:(_ "=" _ v:expr(s) {v})? {(name, into, default)})**comma()
rest:(
comma() rest:destruct_rest()? {rest}
/ comma()? {None}