git.delta.rocks / jrsonnet / refs/commits / 795a53dd5dd7

difftreelog

source

crates/jrsonnet-evaluator/src/evaluate/destructure.rs4.5 KiBsourcehistory
1use std::{collections::HashMap, hash::BuildHasher};23use jrsonnet_interner::IStr;4use jrsonnet_parser::{BindSpec, Destruct};56use crate::{7	bail,8	error::{ErrorKind::*, Result},9	evaluate_method, evaluate_named_param, Context, Pending, Thunk, Val,10};1112#[allow(clippy::too_many_lines)]13#[allow(unused_variables)]14pub fn destruct<H: BuildHasher>(15	d: &Destruct,16	parent: Thunk<Val>,17	fctx: Pending<Context>,18	new_bindings: &mut HashMap<IStr, Thunk<Val>, H>,19) -> Result<()> {20	match d {21		Destruct::Full(v) => {22			let old = new_bindings.insert(v.clone(), parent);23			if old.is_some() {24				bail!(DuplicateLocalVar(v.clone()))25			}26		}27		#[cfg(feature = "exp-destruct")]28		Destruct::Skip => {}29		#[cfg(feature = "exp-destruct")]30		Destruct::Array { start, rest, end } => {31			use jrsonnet_parser::DestructRest;3233			let min_len = start.len() + end.len();34			let has_rest = rest.is_some();35			let full = Thunk!(move || {36				let v = parent.evaluate()?;37				let Val::Arr(arr) = v else {38					bail!("expected array");39				};40				if !has_rest {41					if arr.len() != min_len {42						bail!("expected {} elements, got {}", min_len, arr.len())43					}44				} else if arr.len() < min_len {45					bail!(46						"expected at least {} elements, but array was only {}",47						min_len,48						arr.len()49					)50				}51				Ok(arr)52			});5354			{55				for (i, d) in start.iter().enumerate() {56					let full = full.clone();57					destruct(58						d,59						Thunk!(move || Ok(full.evaluate()?.get(i)?.expect("length is checked"))),60						fctx.clone(),61						new_bindings,62					)?;63				}64			}6566			match rest {67				Some(DestructRest::Keep(v)) => {68					let start = start.len();69					let end = end.len();70					let full = full.clone();71					destruct(72						&Destruct::Full(v.clone()),73						Thunk!(move || {74							let full = full.evaluate()?;75							let to = full.len() - end;76							Ok(Val::Arr(full.slice(77								Some(start as i32),78								Some(to as i32),79								None,80							)))81						}),82						fctx.clone(),83						new_bindings,84					)?;85				}86				Some(DestructRest::Drop) | None => {}87			}8889			{90				for (i, d) in end.iter().enumerate() {91					let full = full.clone();92					let end = end.len();93					destruct(94						d,95						Thunk!(move || {96							let full = full.evaluate()?;97							Ok(full.get(full.len() - end + i)?.expect("length is checked"))98						}),99						fctx.clone(),100						new_bindings,101					)?;102				}103			}104		}105		#[cfg(feature = "exp-destruct")]106		Destruct::Object { fields, rest } => {107			let field_names: Vec<_> = fields108				.iter()109				.map(|f| (f.0.clone(), f.2.is_some()))110				.collect();111			let has_rest = rest.is_some();112			let full = Thunk!(move || {113				let v = parent.evaluate()?;114				let Val::Obj(obj) = v else {115					bail!("expected object");116				};117				for (field, has_default) in &field_names {118					if !has_default && !obj.has_field_ex(field.clone(), true) {119						bail!("missing field: {field}");120					}121				}122				if !has_rest {123					let len = obj.len();124					if len > field_names.len() {125						bail!("too many fields, and rest not found");126					}127				}128				Ok(obj)129			});130131			for (field, d, default) in fields {132				let default = default.clone().map(|e| (fctx.clone(), e));133				let value = {134					let field = field.clone();135					let full = full.clone();136					Thunk!(move || {137						let full = full.evaluate()?;138						if let Some(field) = full.get(field)? {139							Ok(field)140						} else {141							let (fctx, expr) = default.as_ref().expect("shape is checked");142							Ok(evaluate(fctx.clone().unwrap(), expr)?)143						}144					})145				};146147				if let Some(d) = d {148					destruct(d, value, fctx.clone(), new_bindings)?;149				} else {150					destruct(151						&Destruct::Full(field.clone()),152						value,153						fctx.clone(),154						new_bindings,155					)?;156				}157			}158		}159	}160	Ok(())161}162163pub fn evaluate_dest<H: BuildHasher>(164	d: &BindSpec,165	fctx: Pending<Context>,166	new_bindings: &mut HashMap<IStr, Thunk<Val>, H>,167) -> Result<()> {168	match d {169		BindSpec::Field { into, value } => {170			let name = into.name();171			let value = value.clone();172			let data = {173				let fctx = fctx.clone();174				Thunk!(move || evaluate_named_param(fctx.unwrap(), &value, name))175			};176			destruct(into, data, fctx, new_bindings)?;177		}178		BindSpec::Function {179			name,180			params,181			value,182		} => {183			let params = params.clone();184			let name = name.clone();185			let value = value.clone();186			let old = new_bindings.insert(name.clone(), {187				let name = name.clone();188				Thunk!(move || Ok(evaluate_method(fctx.unwrap(), name, params, value)))189			});190			if old.is_some() {191				bail!(DuplicateLocalVar(name))192			}193		}194	}195	Ok(())196}