1use crate::{2 create_error, lazy_binding, Context, Error, FunctionDefault, FunctionRhs, LazyBinding,3 ObjValue, Result,4};5use closure::closure;6use jsonnet_parser::ParamsDesc;7use std::{8 cell::RefCell,9 collections::HashMap,10 fmt::{Debug, Display},11 rc::Rc,12};1314struct LazyValInternals {15 pub f: Box<dyn Fn() -> Result<Val>>,16 pub cached: RefCell<Option<Val>>,17}18#[derive(Clone)]19pub struct LazyVal(Rc<LazyValInternals>);20impl LazyVal {21 pub fn new(f: Box<dyn Fn() -> Result<Val>>) -> Self {22 LazyVal(Rc::new(LazyValInternals {23 f,24 cached: RefCell::new(None),25 }))26 }27 pub fn evaluate(&self) -> Result<Val> {28 {29 let cached = self.0.cached.borrow();30 if cached.is_some() {31 return Ok(cached.clone().unwrap());32 }33 }34 let result = (self.0.f)()?;35 self.0.cached.borrow_mut().replace(result.clone());36 Ok(result)37 }38}39#[macro_export]40macro_rules! lazy_val {41 ($f: expr) => {42 $crate::LazyVal::new(Box::new($f))43 };44}45impl Debug for LazyVal {46 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {47 if self.0.cached.borrow().is_some() {48 write!(f, "{:?}", self.0.cached.borrow().clone().unwrap())49 } else {50 write!(f, "Lazy")51 }52 }53}54impl PartialEq for LazyVal {55 fn eq(&self, other: &Self) -> bool {56 Rc::ptr_eq(&self.0, &other.0)57 }58}5960#[derive(Debug, PartialEq, Clone)]61pub struct FuncDesc {62 pub ctx: Context,63 pub params: ParamsDesc,64 pub eval_rhs: FunctionRhs,65 pub eval_default: FunctionDefault,66}67impl FuncDesc {68 69 pub fn evaluate(&self, args: Vec<(Option<String>, Val)>) -> Result<Val> {70 let mut new_bindings: HashMap<String, LazyBinding> = HashMap::new();71 let future_ctx = Context::new_future();7273 74 75 76 77 78 79 80 81 82 83 84 85 86 for (name, val) in args.clone().into_iter().filter(|e| e.0.is_some()) {87 new_bindings.insert(88 name.as_ref().unwrap().clone(),89 lazy_binding!(90 closure!(clone val, |_, _| Ok(lazy_val!(closure!(clone val, || Ok(val.clone())))))91 ),92 );93 }94 for (i, param) in self.params.0.iter().enumerate() {95 if let Some((None, val)) = args.get(i) {96 new_bindings.insert(97 param.0.clone(),98 lazy_binding!(99 closure!(clone val, |_, _| Ok(lazy_val!(closure!(clone val, || Ok(val.clone())))))100 ),101 );102 }103 }104 let ctx = self105 .ctx106 .extend(new_bindings, None, None, None)?107 .into_future(future_ctx);108 self.eval_rhs.0(ctx)109 }110}111112#[derive(Debug)]113pub enum ValType {114 Bool,115 Null,116 Str,117 Num,118 Arr,119 Obj,120 Func,121}122impl ValType {123 pub fn name(&self) -> &'static str {124 use ValType::*;125 match self {126 Bool => "boolean",127 Null => "null",128 Str => "string",129 Num => "number",130 Arr => "array",131 Obj => "object",132 Func => "function",133 }134 }135}136impl Display for ValType {137 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {138 write!(f, "{}", self.name())139 }140}141142#[derive(Debug, PartialEq, Clone)]143pub enum Val {144 Bool(bool),145 Null,146 Str(String),147 Num(f64),148 Lazy(LazyVal),149 Arr(Vec<Val>),150 Obj(ObjValue),151 Func(FuncDesc),152153 154 Intristic(String, String),155}156impl Val {157 pub fn try_cast_bool(self, context: &'static str) -> Result<bool> {158 match self.unwrap_if_lazy()? {159 Val::Bool(v) => Ok(v),160 v => create_error(Error::TypeMismatch(161 context,162 vec![ValType::Bool],163 v.value_type()?,164 )),165 }166 }167 pub fn try_cast_str(self, context: &'static str) -> Result<String> {168 match self.unwrap_if_lazy()? {169 Val::Str(v) => Ok(v),170 v => create_error(Error::TypeMismatch(171 context,172 vec![ValType::Str],173 v.value_type()?,174 )),175 }176 }177 pub fn unwrap_if_lazy(self) -> Result<Self> {178 Ok(if let Val::Lazy(v) = self {179 v.evaluate()?.unwrap_if_lazy()?180 } else {181 self182 })183 }184 pub fn value_type(&self) -> Result<ValType> {185 Ok(match self {186 Val::Str(..) => ValType::Str,187 Val::Num(..) => ValType::Num,188 Val::Arr(..) => ValType::Arr,189 Val::Obj(..) => ValType::Obj,190 Val::Func(..) => ValType::Func,191 Val::Bool(_) => ValType::Bool,192 Val::Null => ValType::Null,193 Val::Intristic(_, _) => ValType::Func,194 Val::Lazy(_) => self.clone().unwrap_if_lazy()?.value_type()?,195 })196 }197}