1use crate::{2 create_error, evaluate,3 function::{inline_parse_function_call, place_args},4 with_state, Context, Error, ObjValue, Result,5};6use jsonnet_parser::{el, Arg, ArgsDesc, Expr, LocExpr, ParamsDesc};7use std::{8 cell::RefCell,9 fmt::{Debug, Display},10 rc::Rc,11};1213enum LazyValInternals {14 Computed(Val),15 Waiting(Box<dyn Fn() -> Result<Val>>),16}17#[derive(Clone)]18pub struct LazyVal(Rc<RefCell<LazyValInternals>>);19impl LazyVal {20 pub fn new(f: Box<dyn Fn() -> Result<Val>>) -> Self {21 LazyVal(Rc::new(RefCell::new(LazyValInternals::Waiting(f))))22 }23 pub fn new_resolved(val: Val) -> Self {24 LazyVal(Rc::new(RefCell::new(LazyValInternals::Computed(val))))25 }26 pub fn evaluate(&self) -> Result<Val> {27 let new_value = match &*self.0.borrow() {28 LazyValInternals::Computed(v) => return Ok(v.clone()),29 LazyValInternals::Waiting(f) => f()?,30 };31 *self.0.borrow_mut() = LazyValInternals::Computed(new_value.clone());32 Ok(new_value)33 }34}3536#[macro_export]37macro_rules! lazy_val {38 ($f: expr) => {39 $crate::LazyVal::new(Box::new($f))40 };41}42#[macro_export]43macro_rules! resolved_lazy_val {44 ($f: expr) => {45 $crate::LazyVal::new_resolved($f)46 };47}48impl Debug for LazyVal {49 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {50 write!(f, "Lazy")51 }52}53impl PartialEq for LazyVal {54 fn eq(&self, other: &Self) -> bool {55 Rc::ptr_eq(&self.0, &other.0)56 }57}5859#[derive(Debug, PartialEq, Clone)]60pub struct FuncDesc {61 pub ctx: Context,62 pub params: ParamsDesc,63 pub body: LocExpr,64}65impl FuncDesc {66 67 pub fn evaluate(&self, call_ctx: Context, args: &ArgsDesc, tailstrict: bool) -> Result<Val> {68 let ctx = inline_parse_function_call(69 call_ctx,70 Some(self.ctx.clone()),71 &self.params,72 args,73 tailstrict,74 )?;75 evaluate(ctx, &self.body)76 }7778 pub fn evaluate_values(&self, call_ctx: Context, args: &[Val]) -> Result<Val> {79 let ctx = place_args(call_ctx, Some(self.ctx.clone()), &self.params, args)?;80 evaluate(ctx, &self.body)81 }82}8384#[derive(Debug, Clone)]85pub enum ValType {86 Bool,87 Null,88 Str,89 Num,90 Arr,91 Obj,92 Func,93}94impl ValType {95 pub fn name(&self) -> &'static str {96 use ValType::*;97 match self {98 Bool => "boolean",99 Null => "null",100 Str => "string",101 Num => "number",102 Arr => "array",103 Obj => "object",104 Func => "function",105 }106 }107}108impl Display for ValType {109 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {110 write!(f, "{}", self.name())111 }112}113114#[derive(Debug, PartialEq, Clone)]115pub enum Val {116 Bool(bool),117 Null,118 Str(String),119 Num(f64),120 Lazy(LazyVal),121 Arr(Vec<Val>),122 Obj(ObjValue),123 Func(FuncDesc),124125 126 Intristic(String, String),127}128impl Val {129 pub fn try_cast_bool(self, context: &'static str) -> Result<bool> {130 match self.unwrap_if_lazy()? {131 Val::Bool(v) => Ok(v),132 v => create_error(Error::TypeMismatch(133 context,134 vec![ValType::Bool],135 v.value_type()?,136 )),137 }138 }139 pub fn try_cast_str(self, context: &'static str) -> Result<String> {140 match self.unwrap_if_lazy()? {141 Val::Str(v) => Ok(v),142 v => create_error(Error::TypeMismatch(143 context,144 vec![ValType::Str],145 v.value_type()?,146 )),147 }148 }149 pub fn unwrap_if_lazy(self) -> Result<Self> {150 Ok(if let Val::Lazy(v) = self {151 v.evaluate()?.unwrap_if_lazy()?152 } else {153 self154 })155 }156 pub fn value_type(&self) -> Result<ValType> {157 Ok(match self {158 Val::Str(..) => ValType::Str,159 Val::Num(..) => ValType::Num,160 Val::Arr(..) => ValType::Arr,161 Val::Obj(..) => ValType::Obj,162 Val::Func(..) => ValType::Func,163 Val::Bool(_) => ValType::Bool,164 Val::Null => ValType::Null,165 Val::Intristic(_, _) => ValType::Func,166 Val::Lazy(_) => self.clone().unwrap_if_lazy()?.value_type()?,167 })168 }169 pub fn into_json(self, padding: usize) -> Result<String> {170 with_state(|s| {171 let ctx = s172 .create_default_context()?173 .with_var("__tmp__to_json__".to_owned(), self)?;174 if let Val::Str(result) = evaluate(175 ctx,176 &el!(Expr::Apply(177 el!(Expr::Index(178 el!(Expr::Var("std".to_owned())),179 el!(Expr::Str("manifestJsonEx".to_owned()))180 )),181 ArgsDesc(vec![182 Arg(None, el!(Expr::Var("__tmp__to_json__".to_owned()))),183 Arg(None, el!(Expr::Str(" ".repeat(padding))))184 ]),185 false186 )),187 )? {188 Ok(result)189 } else {190 unreachable!()191 }192 })193 }194}