git.delta.rocks / jrsonnet / refs/commits / 449686f01d55

difftreelog

source

crates/jrsonnet-evaluator/src/evaluate/destructure.rs4.9 KiBsourcehistory
1use jrsonnet_ir::{BindSpec, Destruct};23use crate::{4	Context, ContextBuilder, Pending, Thunk, Val, error::Result, evaluate_method,5	evaluate_named_param,6};78#[allow(clippy::too_many_lines)]9#[allow(unused_variables)]10pub fn destruct(11	d: &Destruct,12	parent: Thunk<Val>,13	fctx: Pending<Context>,14	new_bindings: &mut ContextBuilder,15) -> Result<()> {16	match d {17		Destruct::Full(v) => {18			new_bindings.try_bind(v.clone(), parent)?;19		}20		#[cfg(feature = "exp-destruct")]21		Destruct::Skip => {}22		#[cfg(feature = "exp-destruct")]23		Destruct::Array { start, rest, end } => {24			use jrsonnet_ir::DestructRest;2526			use crate::bail;2728			let min_len = start.len() + end.len();29			let has_rest = rest.is_some();30			let full = Thunk!(move || {31				let v = parent.evaluate()?;32				let Val::Arr(arr) = v else {33					bail!("expected array");34				};35				if !has_rest {36					if arr.len() != min_len {37						bail!("expected {} elements, got {}", min_len, arr.len())38					}39				} else if arr.len() < min_len {40					bail!(41						"expected at least {} elements, but array was only {}",42						min_len,43						arr.len()44					)45				}46				Ok(arr)47			});4849			{50				for (i, d) in start.iter().enumerate() {51					let full = full.clone();52					destruct(53						d,54						Thunk!(move || Ok(full.evaluate()?.get(i)?.expect("length is checked"))),55						fctx.clone(),56						new_bindings,57					)?;58				}59			}6061			match rest {62				Some(DestructRest::Keep(v)) => {63					let start = start.len();64					let end = end.len();65					let full = full.clone();66					destruct(67						&Destruct::Full(v.clone()),68						Thunk!(move || {69							let full = full.evaluate()?;70							let to = full.len() - end;71							Ok(Val::Arr(full.slice(72								Some(start as i32),73								Some(to as i32),74								None,75							)))76						}),77						fctx.clone(),78						new_bindings,79					)?;80				}81				Some(DestructRest::Drop) | None => {}82			}8384			{85				for (i, d) in end.iter().enumerate() {86					let full = full.clone();87					let end = end.len();88					destruct(89						d,90						Thunk!(move || {91							let full = full.evaluate()?;92							Ok(full.get(full.len() - end + i)?.expect("length is checked"))93						}),94						fctx.clone(),95						new_bindings,96					)?;97				}98			}99		}100		#[cfg(feature = "exp-destruct")]101		Destruct::Object { fields, rest } => {102			use jrsonnet_ir::DestructRest;103			use rustc_hash::FxHashSet;104105			use crate::{ObjValueBuilder, bail};106107			let captured_fields: FxHashSet<_> = fields.iter().map(|f| f.0.clone()).collect();108			let field_names: Vec<_> = fields109				.iter()110				.map(|f| (f.0.clone(), f.2.is_some()))111				.collect();112			let has_rest = rest.is_some();113			let full = Thunk!(move || {114				let v = parent.evaluate()?;115				let Val::Obj(obj) = v else {116					bail!("expected object");117				};118				for (field, has_default) in &field_names {119					if !has_default && !obj.has_field_ex(field.clone(), true) {120						bail!("missing field: {field}");121					}122				}123				if !has_rest {124					let len = obj.len();125					if len > field_names.len() {126						bail!("too many fields, and rest not found");127					}128				}129				Ok(obj)130			});131132			match rest {133				Some(DestructRest::Keep(v)) => {134					let full = full.clone();135					destruct(136						&Destruct::Full(v.clone()),137						Thunk!(move || {138							let full = full.evaluate()?;139							let mut builder = ObjValueBuilder::new();140							builder.extend_with_core(full.as_standalone());141							builder.with_fields_omitted(captured_fields);142							Ok(Val::Obj(builder.build()))143						}),144						fctx.clone(),145						new_bindings,146					)?;147				}148				Some(DestructRest::Drop) | None => {}149			}150151			for (field, d, default) in fields {152				let default = default.clone().map(|e| (fctx.clone(), e));153				let value = {154					let field = field.clone();155					let full = full.clone();156					Thunk!(move || {157						let full = full.evaluate()?;158						if let Some(field) = full.get(field)? {159							Ok(field)160						} else {161							let (fctx, expr) = default.as_ref().expect("shape is checked");162							Ok(crate::evaluate(fctx.clone().unwrap(), expr)?)163						}164					})165				};166167				if let Some(d) = d {168					destruct(d, value, fctx.clone(), new_bindings)?;169				} else {170					destruct(171						&Destruct::Full(field.clone()),172						value,173						fctx.clone(),174						new_bindings,175					)?;176				}177			}178		}179	}180	Ok(())181}182183pub fn evaluate_dest(184	d: &BindSpec,185	fctx: Pending<Context>,186	new_bindings: &mut ContextBuilder,187) -> Result<()> {188	match d {189		BindSpec::Field { into, value } => {190			let name = into.name();191			let value = value.clone();192			let data = {193				let fctx = fctx.clone();194				Thunk!(move || evaluate_named_param(fctx.unwrap(), &value, name))195			};196			destruct(into, data, fctx, new_bindings)?;197		}198		BindSpec::Function {199			name,200			params,201			value,202		} => {203			let params = params.clone();204			let name = name.clone();205			let value = value.clone();206			new_bindings.try_bind(207				name.clone(),208				Thunk!(move || Ok(evaluate_method(fctx.unwrap(), name, params, value))),209			)?;210		}211	}212	Ok(())213}