git.delta.rocks / jrsonnet / refs/commits / ff23e408f240

difftreelog

source

crates/jsonnet-evaluator/src/val.rs3.7 KiBsourcehistory
1use crate::{2	create_error, evaluate, function::inline_parse_function_call, Context, Error, ObjValue, Result,3};4use jsonnet_parser::{ArgsDesc, LocExpr, ParamsDesc};5use std::{6	cell::RefCell,7	fmt::{Debug, Display},8	rc::Rc,9};1011struct LazyValInternals {12	pub f: Option<Box<dyn Fn() -> Result<Val>>>,13	pub cached: RefCell<Option<Val>>,14}15#[derive(Clone)]16pub struct LazyVal(Rc<LazyValInternals>);17impl LazyVal {18	pub fn new(f: Box<dyn Fn() -> Result<Val>>) -> Self {19		LazyVal(Rc::new(LazyValInternals {20			f: Some(f),21			cached: RefCell::new(None),22		}))23	}24	pub fn new_resolved(val: Val) -> Self {25		LazyVal(Rc::new(LazyValInternals {26			f: None,27			cached: RefCell::new(Some(val)),28		}))29	}30	pub fn evaluate(&self) -> Result<Val> {31		{32			let cached = self.0.cached.borrow();33			if cached.is_some() {34				return Ok(cached.clone().unwrap());35			}36		}37		let result = (self.0.f.as_ref().unwrap())()?;38		self.0.cached.borrow_mut().replace(result.clone());39		Ok(result)40	}41}4243#[macro_export]44macro_rules! lazy_val {45	($f: expr) => {46		$crate::LazyVal::new(Box::new($f))47	};48}49#[macro_export]50macro_rules! resolved_lazy_val {51	($f: expr) => {52		$crate::LazyVal::new_resolved($f)53	};54}55impl Debug for LazyVal {56	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {57		if self.0.cached.borrow().is_some() {58			write!(f, "{:?}", self.0.cached.borrow().clone().unwrap())59		} else {60			write!(f, "Lazy")61		}62	}63}64impl PartialEq for LazyVal {65	fn eq(&self, other: &Self) -> bool {66		Rc::ptr_eq(&self.0, &other.0)67	}68}6970#[derive(Debug, PartialEq, Clone)]71pub struct FuncDesc {72	pub ctx: Context,73	pub params: ParamsDesc,74	pub body: LocExpr,75}76impl FuncDesc {77	// TODO: Check for unset variables78	/// This function is always inlined to make tailstrict work79	#[inline(always)]80	pub fn evaluate(&self, call_ctx: Context, args: &ArgsDesc, tailstrict: bool) -> Result<Val> {81		let ctx = inline_parse_function_call(82			call_ctx,83			Some(self.ctx.clone()),84			&self.params,85			args,86			tailstrict,87		)?;88		evaluate(ctx, &self.body)89	}90}9192#[derive(Debug)]93pub enum ValType {94	Bool,95	Null,96	Str,97	Num,98	Arr,99	Obj,100	Func,101}102impl ValType {103	pub fn name(&self) -> &'static str {104		use ValType::*;105		match self {106			Bool => "boolean",107			Null => "null",108			Str => "string",109			Num => "number",110			Arr => "array",111			Obj => "object",112			Func => "function",113		}114	}115}116impl Display for ValType {117	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {118		write!(f, "{}", self.name())119	}120}121122#[derive(Debug, PartialEq, Clone)]123pub enum Val {124	Bool(bool),125	Null,126	Str(String),127	Num(f64),128	Lazy(LazyVal),129	Arr(Vec<Val>),130	Obj(ObjValue),131	Func(FuncDesc),132133	// Library functions implemented in native134	Intristic(String, String),135}136impl Val {137	pub fn try_cast_bool(self, context: &'static str) -> Result<bool> {138		match self.unwrap_if_lazy()? {139			Val::Bool(v) => Ok(v),140			v => create_error(Error::TypeMismatch(141				context,142				vec![ValType::Bool],143				v.value_type()?,144			)),145		}146	}147	pub fn try_cast_str(self, context: &'static str) -> Result<String> {148		match self.unwrap_if_lazy()? {149			Val::Str(v) => Ok(v),150			v => create_error(Error::TypeMismatch(151				context,152				vec![ValType::Str],153				v.value_type()?,154			)),155		}156	}157	pub fn unwrap_if_lazy(self) -> Result<Self> {158		Ok(if let Val::Lazy(v) = self {159			v.evaluate()?.unwrap_if_lazy()?160		} else {161			self162		})163	}164	pub fn value_type(&self) -> Result<ValType> {165		Ok(match self {166			Val::Str(..) => ValType::Str,167			Val::Num(..) => ValType::Num,168			Val::Arr(..) => ValType::Arr,169			Val::Obj(..) => ValType::Obj,170			Val::Func(..) => ValType::Func,171			Val::Bool(_) => ValType::Bool,172			Val::Null => ValType::Null,173			Val::Intristic(_, _) => ValType::Func,174			Val::Lazy(_) => self.clone().unwrap_if_lazy()?.value_type()?,175		})176	}177}