1use crate::{2 error::Error::*,3 evaluate,4 function::{parse_function_call, parse_function_call_map, place_args},5 throw, with_state, Context, ObjValue, Result,6};7use jrsonnet_parser::{el, Arg, ArgsDesc, Expr, LocExpr, ParamsDesc};8use std::{9 cell::RefCell,10 collections::HashMap,11 fmt::{Debug, Display},12 rc::Rc,13};1415enum LazyValInternals {16 Computed(Val),17 Waiting(Box<dyn Fn() -> Result<Val>>),18}19#[derive(Clone)]20pub struct LazyVal(Rc<RefCell<LazyValInternals>>);21impl LazyVal {22 pub fn new(f: Box<dyn Fn() -> Result<Val>>) -> Self {23 LazyVal(Rc::new(RefCell::new(LazyValInternals::Waiting(f))))24 }25 pub fn new_resolved(val: Val) -> Self {26 LazyVal(Rc::new(RefCell::new(LazyValInternals::Computed(val))))27 }28 pub fn evaluate(&self) -> Result<Val> {29 let new_value = match &*self.0.borrow() {30 LazyValInternals::Computed(v) => return Ok(v.clone()),31 LazyValInternals::Waiting(f) => f()?,32 };33 *self.0.borrow_mut() = LazyValInternals::Computed(new_value.clone());34 Ok(new_value)35 }36}3738#[macro_export]39macro_rules! lazy_val {40 ($f: expr) => {41 $crate::LazyVal::new(Box::new($f))42 };43}44#[macro_export]45macro_rules! resolved_lazy_val {46 ($f: expr) => {47 $crate::LazyVal::new_resolved($f)48 };49}50impl Debug for LazyVal {51 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {52 write!(f, "Lazy")53 }54}55impl PartialEq for LazyVal {56 fn eq(&self, other: &Self) -> bool {57 Rc::ptr_eq(&self.0, &other.0)58 }59}6061#[derive(Debug, PartialEq)]62pub struct FuncDesc {63 pub name: Rc<str>,64 pub ctx: Context,65 pub params: ParamsDesc,66 pub body: LocExpr,67}68impl FuncDesc {69 70 pub fn evaluate(&self, call_ctx: Context, args: &ArgsDesc, tailstrict: bool) -> Result<Val> {71 let ctx = parse_function_call(72 call_ctx,73 Some(self.ctx.clone()),74 &self.params,75 args,76 tailstrict,77 )?;78 evaluate(ctx, &self.body)79 }8081 pub fn evaluate_map(82 &self,83 call_ctx: Context,84 args: &HashMap<Rc<str>, Val>,85 tailstrict: bool,86 ) -> Result<Val> {87 let ctx = parse_function_call_map(88 call_ctx,89 Some(self.ctx.clone()),90 &self.params,91 args,92 tailstrict,93 )?;94 evaluate(ctx, &self.body)95 }9697 pub fn evaluate_values(&self, call_ctx: Context, args: &[Val]) -> Result<Val> {98 let ctx = place_args(call_ctx, Some(self.ctx.clone()), &self.params, args)?;99 evaluate(ctx, &self.body)100 }101}102103#[derive(Debug, Clone, Copy, PartialEq)]104pub enum ValType {105 Bool,106 Null,107 Str,108 Num,109 Arr,110 Obj,111 Func,112}113impl ValType {114 pub fn name(&self) -> &'static str {115 use ValType::*;116 match self {117 Bool => "boolean",118 Null => "null",119 Str => "string",120 Num => "number",121 Arr => "array",122 Obj => "object",123 Func => "function",124 }125 }126}127impl Display for ValType {128 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {129 write!(f, "{}", self.name())130 }131}132133#[derive(Debug, Clone)]134pub enum Val {135 Bool(bool),136 Null,137 Str(Rc<str>),138 Num(f64),139 Lazy(LazyVal),140 Arr(Rc<Vec<Val>>),141 Obj(ObjValue),142 Func(Rc<FuncDesc>),143144 145 Intristic(Rc<str>, Rc<str>),146}147macro_rules! matches_unwrap {148 ($e: expr, $p: pat, $r: expr) => {149 match $e {150 $p => $r,151 _ => panic!("no match"),152 }153 };154}155impl Val {156 157 pub fn new_checked_num(num: f64) -> Result<Val> {158 if num.is_finite() {159 Ok(Val::Num(num))160 } else {161 throw!(RuntimeError("overflow".into()))162 }163 }164165 pub fn assert_type(&self, context: &'static str, val_type: ValType) -> Result<()> {166 let this_type = self.value_type()?;167 if this_type != val_type {168 throw!(TypeMismatch(context, vec![val_type], this_type))169 } else {170 Ok(())171 }172 }173 pub fn try_cast_bool(self, context: &'static str) -> Result<bool> {174 self.assert_type(context, ValType::Bool)?;175 Ok(matches_unwrap!(self.unwrap_if_lazy()?, Val::Bool(v), v))176 }177 pub fn try_cast_str(self, context: &'static str) -> Result<Rc<str>> {178 self.assert_type(context, ValType::Str)?;179 Ok(matches_unwrap!(self.unwrap_if_lazy()?, Val::Str(v), v))180 }181 pub fn try_cast_num(self, context: &'static str) -> Result<f64> {182 self.assert_type(context, ValType::Num)?;183 Ok(matches_unwrap!(self.unwrap_if_lazy()?, Val::Num(v), v))184 }185 pub fn unwrap_if_lazy(&self) -> Result<Self> {186 Ok(if let Val::Lazy(v) = self {187 v.evaluate()?.unwrap_if_lazy()?188 } else {189 self.clone()190 })191 }192 pub fn value_type(&self) -> Result<ValType> {193 Ok(match self {194 Val::Str(..) => ValType::Str,195 Val::Num(..) => ValType::Num,196 Val::Arr(..) => ValType::Arr,197 Val::Obj(..) => ValType::Obj,198 Val::Func(..) => ValType::Func,199 Val::Bool(_) => ValType::Bool,200 Val::Null => ValType::Null,201 Val::Intristic(_, _) => ValType::Func,202 Val::Lazy(_) => self.clone().unwrap_if_lazy()?.value_type()?,203 })204 }205 #[cfg(feature = "faster")]206 pub fn into_json(self, padding: usize) -> Result<Rc<str>> {207 manifest_json_ex(&self, &" ".repeat(padding)).map(|s| s.into())208 }209 #[cfg(not(feature = "faster"))]210 pub fn into_json(self, padding: usize) -> Result<Rc<str>> {211 with_state(|s| {212 let ctx = s213 .create_default_context()?214 .with_var("__tmp__to_json__".into(), self)?;215 Ok(evaluate(216 ctx,217 &el!(Expr::Apply(218 el!(Expr::Index(219 el!(Expr::Var("std".into())),220 el!(Expr::Str("manifestJsonEx".into()))221 )),222 ArgsDesc(vec![223 Arg(None, el!(Expr::Var("__tmp__to_json__".into()))),224 Arg(None, el!(Expr::Str(" ".repeat(padding).into())))225 ]),226 false227 )),228 )?229 .try_cast_str("to json")?)230 })231 }232 pub fn into_yaml(self, padding: usize) -> Result<Rc<str>> {233 with_state(|s| {234 let ctx = s235 .create_default_context()?236 .with_var("__tmp__to_json__".into(), self)?;237 Ok(evaluate(238 ctx,239 &el!(Expr::Apply(240 el!(Expr::Index(241 el!(Expr::Var("std".into())),242 el!(Expr::Str("manifestYamlDoc".into()))243 )),244 ArgsDesc(vec![245 Arg(None, el!(Expr::Var("__tmp__to_json__".into()))),246 Arg(None, el!(Expr::Str(" ".repeat(padding).into())))247 ]),248 false249 )),250 )?251 .try_cast_str("to json")?)252 })253 }254}255256fn is_function_like(val: &Val) -> bool {257 matches!(val, Val::Func(_) | Val::Intristic(_, _))258}259260261pub fn primitive_equals(val_a: &Val, val_b: &Val) -> Result<bool> {262 Ok(match (val_a.unwrap_if_lazy()?, val_b.unwrap_if_lazy()?) {263 (Val::Bool(a), Val::Bool(b)) => a == b,264 (Val::Null, Val::Null) => true,265 (Val::Str(a), Val::Str(b)) => a == b,266 (Val::Num(a), Val::Num(b)) => (a - b).abs() <= f64::EPSILON,267 (Val::Arr(_), Val::Arr(_)) => throw!(RuntimeError(268 "primitiveEquals operates on primitive types, got array".into(),269 )),270 (Val::Obj(_), Val::Obj(_)) => throw!(RuntimeError(271 "primitiveEquals operates on primitive types, got object".into(),272 )),273 (a, b) if is_function_like(&a) && is_function_like(&b) => {274 throw!(RuntimeError("cannot test equality of functions".into()))275 }276 (_, _) => false,277 })278}279280281pub fn equals(val_a: &Val, val_b: &Val) -> Result<bool> {282 let val_a = val_a.unwrap_if_lazy()?;283 let val_b = val_b.unwrap_if_lazy()?;284285 if val_a.value_type()? != val_b.value_type()? {286 return Ok(false);287 }288 match (val_a, val_b) {289 290 (Val::Arr(a), Val::Arr(b)) => {291 if a.len() != b.len() {292 return Ok(false);293 }294 for (a, b) in a.iter().zip(b.iter()) {295 if !equals(&a.unwrap_if_lazy()?, &b.unwrap_if_lazy()?)? {296 return Ok(false);297 }298 }299 Ok(true)300 }301 (Val::Obj(a), Val::Obj(b)) => {302 let fields = a.visible_fields();303 if fields != b.visible_fields() {304 return Ok(false);305 }306 for field in fields {307 if !equals(&a.get(field.clone())?.unwrap(), &b.get(field)?.unwrap())? {308 return Ok(false);309 }310 }311 Ok(true)312 }313 (a, b) => Ok(primitive_equals(&a, &b)?),314 }315}316317pub fn manifest_json_ex(val: &Val, padding: &str) -> Result<String> {318 let mut out = String::new();319 manifest_json_ex_buf(val, &mut out, padding, &mut String::new())?;320 Ok(out)321}322fn manifest_json_ex_buf(323 val: &Val,324 buf: &mut String,325 padding: &str,326 cur_padding: &mut String,327) -> Result<()> {328 use std::fmt::Write;329 match val.unwrap_if_lazy()? {330 Val::Bool(v) => {331 if v {332 buf.push_str("true");333 } else {334 buf.push_str("false");335 }336 }337 Val::Null => buf.push_str("null"),338 Val::Str(s) => buf.push_str(&escape_string_json(&s)),339 Val::Num(n) => write!(buf, "{}", n).unwrap(),340 Val::Arr(items) => {341 buf.push_str("[\n");342 if !items.is_empty() {343 let old_len = cur_padding.len();344 cur_padding.push_str(padding);345 for (i, item) in items.iter().enumerate() {346 if i != 0 {347 buf.push_str(",\n")348 }349 buf.push_str(cur_padding);350 manifest_json_ex_buf(item, buf, padding, cur_padding)?;351 }352 cur_padding.truncate(old_len);353 }354 buf.push('\n');355 buf.push_str(cur_padding);356 buf.push(']');357 }358 Val::Obj(obj) => {359 buf.push_str("{\n");360 let fields = obj.visible_fields();361 if !fields.is_empty() {362 let old_len = cur_padding.len();363 cur_padding.push_str(padding);364 for (i, field) in fields.into_iter().enumerate() {365 if i != 0 {366 buf.push_str(",\n")367 }368 buf.push_str(cur_padding);369 buf.push_str(&escape_string_json(&field));370 buf.push_str(": ");371 manifest_json_ex_buf(&obj.get(field)?.unwrap(), buf, padding, cur_padding)?;372 }373 cur_padding.truncate(old_len);374 }375 buf.push('\n');376 buf.push_str(cur_padding);377 buf.push('}');378 }379 Val::Func(_) | Val::Intristic(_, _) => {380 throw!(RuntimeError("tried to manifest function".into()))381 }382 Val::Lazy(_) => unreachable!(),383 };384 Ok(())385}386pub fn escape_string_json(s: &str) -> String {387 use std::fmt::Write;388 let mut out = String::new();389 out.push('"');390 for c in s.chars() {391 match c {392 '"' => out.push_str("\\\""),393 '\\' => out.push_str("\\\\"),394 '\u{0008}' => out.push_str("\\b"),395 '\u{000c}' => out.push_str("\\f"),396 '\n' => out.push_str("\\n"),397 '\r' => out.push_str("\\r"),398 '\t' => out.push_str("\\t"),399 c if c < 32 as char || (c >= 127 as char && c <= 159 as char) => {400 write!(out, "\\u{:04x}", c as u32).unwrap()401 }402 c => out.push(c),403 }404 }405 out.push('"');406 out407}408409pub fn to_string(val: &Val) -> Result<Rc<str>> {410 Ok(match val {411 Val::Bool(true) => "true".into(),412 Val::Null => "null".into(),413 Val::Str(s) => s.clone(),414 v => v.clone().into_json(0)?,415 })416}417418#[test]419fn json_test() {420 assert_eq!(escape_string_json("\u{001f}"), "\"\\u001f\"")421}