difftreelog
feat minify json
in: master
2 files changed
crates/jrsonnet-evaluator/src/builtin/manifest.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/builtin/manifest.rs
+++ b/crates/jrsonnet-evaluator/src/builtin/manifest.rs
@@ -2,15 +2,17 @@
use crate::error::Result;
use crate::{throw, Val};
-#[derive(PartialEq)]
+#[derive(PartialEq, Clone, Copy)]
pub enum ManifestType {
// Applied in manifestification
Manifest,
/// Used for std.manifestJson
/// Empty array/objects extends to "[\n\n]" instead of "[ ]" as in manifest
Std,
- // No line breaks, used in `obj+''`
+ /// No line breaks, used in `obj+''`
ToString,
+ /// Minified json
+ Minify,
}
pub struct ManifestJsonOptions<'s> {
@@ -30,6 +32,7 @@
options: &ManifestJsonOptions<'_>,
) -> Result<()> {
use std::fmt::Write;
+ let mtype = options.mtype;
match val.unwrap_if_lazy()? {
Val::Bool(v) => {
if v {
@@ -44,7 +47,7 @@
Val::Arr(items) => {
buf.push('[');
if !items.is_empty() {
- if options.mtype != ManifestType::ToString {
+ if mtype != ManifestType::ToString && mtype != ManifestType::Minify {
buf.push('\n');
}
@@ -53,9 +56,9 @@
for (i, item) in items.iter().enumerate() {
if i != 0 {
buf.push(',');
- if options.mtype == ManifestType::ToString {
+ if mtype == ManifestType::ToString {
buf.push(' ');
- } else {
+ } else if mtype != ManifestType::Minify {
buf.push('\n');
}
}
@@ -64,14 +67,14 @@
}
cur_padding.truncate(old_len);
- if options.mtype != ManifestType::ToString {
+ if mtype != ManifestType::ToString && mtype != ManifestType::Minify {
buf.push('\n');
buf.push_str(cur_padding);
}
- } else if options.mtype == ManifestType::Std {
+ } else if mtype == ManifestType::Std {
buf.push_str("\n\n");
buf.push_str(cur_padding);
- } else if options.mtype == ManifestType::ToString {
+ } else if mtype == ManifestType::ToString {
buf.push(' ');
}
buf.push(']');
@@ -80,7 +83,7 @@
buf.push('{');
let fields = obj.visible_fields();
if !fields.is_empty() {
- if options.mtype != ManifestType::ToString {
+ if mtype != ManifestType::ToString && mtype != ManifestType::Minify {
buf.push('\n');
}
@@ -89,9 +92,9 @@
for (i, field) in fields.into_iter().enumerate() {
if i != 0 {
buf.push(',');
- if options.mtype == ManifestType::ToString {
+ if mtype == ManifestType::ToString {
buf.push(' ');
- } else {
+ } else if mtype != ManifestType::Minify {
buf.push('\n');
}
}
@@ -102,14 +105,14 @@
}
cur_padding.truncate(old_len);
- if options.mtype != ManifestType::ToString {
+ if mtype != ManifestType::ToString && mtype != ManifestType::Minify {
buf.push('\n');
buf.push_str(cur_padding);
}
- } else if options.mtype == ManifestType::Std {
+ } else if mtype == ManifestType::Std {
buf.push_str("\n\n");
buf.push_str(cur_padding);
- } else if options.mtype == ManifestType::ToString {
+ } else if mtype == ManifestType::ToString {
buf.push(' ');
}
buf.push('}');
crates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth1use crate::{2 builtin::manifest::{manifest_json_ex, ManifestJsonOptions, ManifestType},3 error::Error::*,4 evaluate,5 function::{parse_function_call, parse_function_call_map, place_args},6 throw, with_state, Context, ObjValue, Result,7};8use jrsonnet_parser::{el, Arg, ArgsDesc, Expr, LocExpr, ParamsDesc};9use std::{10 cell::RefCell,11 collections::HashMap,12 fmt::{Debug, Display},13 rc::Rc,14};1516enum LazyValInternals {17 Computed(Val),18 Waiting(Box<dyn Fn() -> Result<Val>>),19}20#[derive(Clone)]21pub struct LazyVal(Rc<RefCell<LazyValInternals>>);22impl LazyVal {23 pub fn new(f: Box<dyn Fn() -> Result<Val>>) -> Self {24 LazyVal(Rc::new(RefCell::new(LazyValInternals::Waiting(f))))25 }26 pub fn new_resolved(val: Val) -> Self {27 LazyVal(Rc::new(RefCell::new(LazyValInternals::Computed(val))))28 }29 pub fn evaluate(&self) -> Result<Val> {30 let new_value = match &*self.0.borrow() {31 LazyValInternals::Computed(v) => return Ok(v.clone()),32 LazyValInternals::Waiting(f) => f()?,33 };34 *self.0.borrow_mut() = LazyValInternals::Computed(new_value.clone());35 Ok(new_value)36 }37}3839#[macro_export]40macro_rules! lazy_val {41 ($f: expr) => {42 $crate::LazyVal::new(Box::new($f))43 };44}45#[macro_export]46macro_rules! resolved_lazy_val {47 ($f: expr) => {48 $crate::LazyVal::new_resolved($f)49 };50}51impl Debug for LazyVal {52 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {53 write!(f, "Lazy")54 }55}56impl PartialEq for LazyVal {57 fn eq(&self, other: &Self) -> bool {58 Rc::ptr_eq(&self.0, &other.0)59 }60}6162#[derive(Debug, PartialEq)]63pub struct FuncDesc {64 pub name: Rc<str>,65 pub ctx: Context,66 pub params: ParamsDesc,67 pub body: LocExpr,68}69impl FuncDesc {70 /// This function is always inlined to make tailstrict work71 pub fn evaluate(&self, call_ctx: Context, args: &ArgsDesc, tailstrict: bool) -> Result<Val> {72 let ctx = parse_function_call(73 call_ctx,74 Some(self.ctx.clone()),75 &self.params,76 args,77 tailstrict,78 )?;79 evaluate(ctx, &self.body)80 }8182 pub fn evaluate_map(83 &self,84 call_ctx: Context,85 args: &HashMap<Rc<str>, Val>,86 tailstrict: bool,87 ) -> Result<Val> {88 let ctx = parse_function_call_map(89 call_ctx,90 Some(self.ctx.clone()),91 &self.params,92 args,93 tailstrict,94 )?;95 evaluate(ctx, &self.body)96 }9798 pub fn evaluate_values(&self, call_ctx: Context, args: &[Val]) -> Result<Val> {99 let ctx = place_args(call_ctx, Some(self.ctx.clone()), &self.params, args)?;100 evaluate(ctx, &self.body)101 }102}103104#[derive(Debug, Clone, Copy, PartialEq)]105pub enum ValType {106 Bool,107 Null,108 Str,109 Num,110 Arr,111 Obj,112 Func,113}114impl ValType {115 pub fn name(&self) -> &'static str {116 use ValType::*;117 match self {118 Bool => "boolean",119 Null => "null",120 Str => "string",121 Num => "number",122 Arr => "array",123 Obj => "object",124 Func => "function",125 }126 }127}128impl Display for ValType {129 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {130 write!(f, "{}", self.name())131 }132}133134#[derive(Debug, Clone)]135pub enum Val {136 Bool(bool),137 Null,138 Str(Rc<str>),139 Num(f64),140 Lazy(LazyVal),141 Arr(Rc<Vec<Val>>),142 Obj(ObjValue),143 Func(Rc<FuncDesc>),144145 // Library functions implemented in native146 Intristic(Rc<str>, Rc<str>),147}148macro_rules! matches_unwrap {149 ($e: expr, $p: pat, $r: expr) => {150 match $e {151 $p => $r,152 _ => panic!("no match"),153 }154 };155}156impl Val {157 /// Creates Val::Num after checking for overflow. As numbers are f64, we can just check for finity158 pub fn new_checked_num(num: f64) -> Result<Val> {159 if num.is_finite() {160 Ok(Val::Num(num))161 } else {162 throw!(RuntimeError("overflow".into()))163 }164 }165166 pub fn assert_type(&self, context: &'static str, val_type: ValType) -> Result<()> {167 let this_type = self.value_type()?;168 if this_type != val_type {169 throw!(TypeMismatch(context, vec![val_type], this_type))170 } else {171 Ok(())172 }173 }174 pub fn try_cast_bool(self, context: &'static str) -> Result<bool> {175 self.assert_type(context, ValType::Bool)?;176 Ok(matches_unwrap!(self.unwrap_if_lazy()?, Val::Bool(v), v))177 }178 pub fn try_cast_str(self, context: &'static str) -> Result<Rc<str>> {179 self.assert_type(context, ValType::Str)?;180 Ok(matches_unwrap!(self.unwrap_if_lazy()?, Val::Str(v), v))181 }182 pub fn try_cast_num(self, context: &'static str) -> Result<f64> {183 self.assert_type(context, ValType::Num)?;184 Ok(matches_unwrap!(self.unwrap_if_lazy()?, Val::Num(v), v))185 }186 pub fn unwrap_if_lazy(&self) -> Result<Self> {187 Ok(if let Val::Lazy(v) = self {188 v.evaluate()?.unwrap_if_lazy()?189 } else {190 self.clone()191 })192 }193 pub fn value_type(&self) -> Result<ValType> {194 Ok(match self {195 Val::Str(..) => ValType::Str,196 Val::Num(..) => ValType::Num,197 Val::Arr(..) => ValType::Arr,198 Val::Obj(..) => ValType::Obj,199 Val::Func(..) => ValType::Func,200 Val::Bool(_) => ValType::Bool,201 Val::Null => ValType::Null,202 Val::Intristic(_, _) => ValType::Func,203 Val::Lazy(_) => self.clone().unwrap_if_lazy()?.value_type()?,204 })205 }206207 pub fn into_string(self) -> Result<Rc<str>> {208 Ok(match self.unwrap_if_lazy()? {209 Val::Bool(true) => "true".into(),210 Val::Bool(false) => "false".into(),211 Val::Null => "null".into(),212 Val::Str(s) => s,213 v => manifest_json_ex(214 &v,215 &ManifestJsonOptions {216 padding: &"",217 mtype: ManifestType::ToString,218 },219 )?220 .into(),221 })222 }223224 /// For manifestification225 pub fn into_json(self, padding: usize) -> Result<Rc<str>> {226 manifest_json_ex(227 &self,228 &ManifestJsonOptions {229 padding: &" ".repeat(padding),230 mtype: ManifestType::Manifest,231 },232 )233 .map(|s| s.into())234 }235236 /// Calls std.manifestJson237 #[cfg(feature = "faster")]238 pub fn into_std_json(self, padding: usize) -> Result<Rc<str>> {239 manifest_json_ex(240 &self,241 &ManifestJsonOptions {242 padding: &" ".repeat(padding),243 mtype: ManifestType::Std,244 },245 )246 .map(|s| s.into())247 }248249 /// Calls std.manifestJson250 #[cfg(not(feature = "faster"))]251 pub fn into_std_json(self, padding: usize) -> Result<Rc<str>> {252 with_state(|s| {253 let ctx = s254 .create_default_context()?255 .with_var("__tmp__to_json__".into(), self)?;256 Ok(evaluate(257 ctx,258 &el!(Expr::Apply(259 el!(Expr::Index(260 el!(Expr::Var("std".into())),261 el!(Expr::Str("manifestJsonEx".into()))262 )),263 ArgsDesc(vec![264 Arg(None, el!(Expr::Var("__tmp__to_json__".into()))),265 Arg(None, el!(Expr::Str(" ".repeat(padding).into())))266 ]),267 false268 )),269 )?270 .try_cast_str("to json")?)271 })272 }273 pub fn into_yaml(self, padding: usize) -> Result<Rc<str>> {274 with_state(|s| {275 let ctx = s276 .create_default_context()?277 .with_var("__tmp__to_json__".into(), self);278 Ok(evaluate(279 ctx,280 &el!(Expr::Apply(281 el!(Expr::Index(282 el!(Expr::Var("std".into())),283 el!(Expr::Str("manifestYamlDoc".into()))284 )),285 ArgsDesc(vec![286 Arg(None, el!(Expr::Var("__tmp__to_json__".into()))),287 Arg(None, el!(Expr::Str(" ".repeat(padding).into())))288 ]),289 false290 )),291 )?292 .try_cast_str("to json")?)293 })294 }295}296297fn is_function_like(val: &Val) -> bool {298 matches!(val, Val::Func(_) | Val::Intristic(_, _))299}300301/// Implements std.primitiveEquals builtin302pub fn primitive_equals(val_a: &Val, val_b: &Val) -> Result<bool> {303 Ok(match (val_a.unwrap_if_lazy()?, val_b.unwrap_if_lazy()?) {304 (Val::Bool(a), Val::Bool(b)) => a == b,305 (Val::Null, Val::Null) => true,306 (Val::Str(a), Val::Str(b)) => a == b,307 (Val::Num(a), Val::Num(b)) => (a - b).abs() <= f64::EPSILON,308 (Val::Arr(_), Val::Arr(_)) => throw!(RuntimeError(309 "primitiveEquals operates on primitive types, got array".into(),310 )),311 (Val::Obj(_), Val::Obj(_)) => throw!(RuntimeError(312 "primitiveEquals operates on primitive types, got object".into(),313 )),314 (a, b) if is_function_like(&a) && is_function_like(&b) => {315 throw!(RuntimeError("cannot test equality of functions".into()))316 }317 (_, _) => false,318 })319}320321/// Native implementation of std.equals322pub fn equals(val_a: &Val, val_b: &Val) -> Result<bool> {323 let val_a = val_a.unwrap_if_lazy()?;324 let val_b = val_b.unwrap_if_lazy()?;325326 if val_a.value_type()? != val_b.value_type()? {327 return Ok(false);328 }329 match (val_a, val_b) {330 // Cant test for ptr equality, because all fields needs to be evaluated331 (Val::Arr(a), Val::Arr(b)) => {332 if a.len() != b.len() {333 return Ok(false);334 }335 for (a, b) in a.iter().zip(b.iter()) {336 if !equals(&a.unwrap_if_lazy()?, &b.unwrap_if_lazy()?)? {337 return Ok(false);338 }339 }340 Ok(true)341 }342 (Val::Obj(a), Val::Obj(b)) => {343 let fields = a.visible_fields();344 if fields != b.visible_fields() {345 return Ok(false);346 }347 for field in fields {348 if !equals(&a.get(field.clone())?.unwrap(), &b.get(field)?.unwrap())? {349 return Ok(false);350 }351 }352 Ok(true)353 }354 (a, b) => Ok(primitive_equals(&a, &b)?),355 }356}