difftreelog
Merge pull request #74 from eagletmt/std-json-minified
in: master
Add std.manifestJsonMinified()
5 files changed
crates/jrsonnet-evaluator/src/builtin/manifest.rsdiffbeforeafterboth1use crate::error::Error::*;2use crate::error::Result;3use crate::{throw, Val};45#[derive(PartialEq, Clone, Copy)]6pub enum ManifestType {7 // Applied in manifestification8 Manifest,9 /// Used for std.manifestJson10 /// Empty array/objects extends to "[\n\n]" instead of "[ ]" as in manifest11 Std,12 /// No line breaks, used in `obj+''`13 ToString,14 /// Minified json15 Minify,16}1718pub struct ManifestJsonOptions<'s> {19 pub padding: &'s str,20 pub mtype: ManifestType,21}2223pub fn manifest_json_ex(val: &Val, options: &ManifestJsonOptions<'_>) -> Result<String> {24 let mut out = String::new();25 manifest_json_ex_buf(val, &mut out, &mut String::new(), options)?;26 Ok(out)27}28fn manifest_json_ex_buf(29 val: &Val,30 buf: &mut String,31 cur_padding: &mut String,32 options: &ManifestJsonOptions<'_>,33) -> Result<()> {34 use std::fmt::Write;35 let mtype = options.mtype;36 match val {37 Val::Bool(v) => {38 if *v {39 buf.push_str("true");40 } else {41 buf.push_str("false");42 }43 }44 Val::Null => buf.push_str("null"),45 Val::Str(s) => escape_string_json_buf(s, buf),46 Val::Num(n) => write!(buf, "{}", n).unwrap(),47 Val::Arr(items) => {48 buf.push('[');49 if !items.is_empty() {50 if mtype != ManifestType::ToString && mtype != ManifestType::Minify {51 buf.push('\n');52 }5354 let old_len = cur_padding.len();55 cur_padding.push_str(options.padding);56 for (i, item) in items.iter().enumerate() {57 if i != 0 {58 buf.push(',');59 if mtype == ManifestType::ToString {60 buf.push(' ');61 } else if mtype != ManifestType::Minify {62 buf.push('\n');63 }64 }65 buf.push_str(cur_padding);66 manifest_json_ex_buf(&item?, buf, cur_padding, options)?;67 }68 cur_padding.truncate(old_len);6970 if mtype != ManifestType::ToString && mtype != ManifestType::Minify {71 buf.push('\n');72 buf.push_str(cur_padding);73 }74 } else if mtype == ManifestType::Std {75 buf.push_str("\n\n");76 buf.push_str(cur_padding);77 } else if mtype == ManifestType::ToString || mtype == ManifestType::Manifest {78 buf.push(' ');79 }80 buf.push(']');81 }82 Val::Obj(obj) => {83 obj.run_assertions()?;84 buf.push('{');85 let fields = obj.fields();86 if !fields.is_empty() {87 if mtype != ManifestType::ToString && mtype != ManifestType::Minify {88 buf.push('\n');89 }9091 let old_len = cur_padding.len();92 cur_padding.push_str(options.padding);93 for (i, field) in fields.into_iter().enumerate() {94 if i != 0 {95 buf.push(',');96 if mtype == ManifestType::ToString {97 buf.push(' ');98 } else if mtype != ManifestType::Minify {99 buf.push('\n');100 }101 }102 buf.push_str(cur_padding);103 escape_string_json_buf(&field, buf);104 buf.push_str(": ");105 crate::push(106 None,107 || format!("field <{}> manifestification", field.clone()),108 || {109 let value = obj.get(field.clone())?.unwrap();110 manifest_json_ex_buf(&value, buf, cur_padding, options)111 },112 )?;113 }114 cur_padding.truncate(old_len);115116 if mtype != ManifestType::ToString && mtype != ManifestType::Minify {117 buf.push('\n');118 buf.push_str(cur_padding);119 }120 } else if mtype == ManifestType::Std {121 buf.push_str("\n\n");122 buf.push_str(cur_padding);123 } else if mtype == ManifestType::ToString || mtype == ManifestType::Manifest {124 buf.push(' ');125 }126 buf.push('}');127 }128 Val::Func(_) => throw!(RuntimeError("tried to manifest function".into())),129 };130 Ok(())131}132133pub fn escape_string_json(s: &str) -> String {134 let mut buf = String::new();135 escape_string_json_buf(s, &mut buf);136 buf137}138139fn escape_string_json_buf(s: &str, buf: &mut String) {140 use std::fmt::Write;141 buf.push('"');142 for c in s.chars() {143 match c {144 '"' => buf.push_str("\\\""),145 '\\' => buf.push_str("\\\\"),146 '\u{0008}' => buf.push_str("\\b"),147 '\u{000c}' => buf.push_str("\\f"),148 '\n' => buf.push_str("\\n"),149 '\r' => buf.push_str("\\r"),150 '\t' => buf.push_str("\\t"),151 c if c < 32 as char || (c >= 127 as char && c <= 159 as char) => {152 write!(buf, "\\u{:04x}", c as u32).unwrap()153 }154 c => buf.push(c),155 }156 }157 buf.push('"');158}159160pub struct ManifestYamlOptions<'s> {161 /// Padding before fields, i.e162 /// ```yaml163 /// a:164 /// b:165 /// ## <- this166 /// ```167 pub padding: &'s str,168 /// Padding before array elements in objects169 /// ```yaml170 /// a:171 /// - 1172 /// ## <- this173 /// ```174 pub arr_element_padding: &'s str,175}176177pub fn manifest_yaml_ex(val: &Val, options: &ManifestYamlOptions<'_>) -> Result<String> {178 let mut out = String::new();179 manifest_yaml_ex_buf(val, &mut out, &mut String::new(), options)?;180 Ok(out)181}182fn manifest_yaml_ex_buf(183 val: &Val,184 buf: &mut String,185 cur_padding: &mut String,186 options: &ManifestYamlOptions<'_>,187) -> Result<()> {188 use std::fmt::Write;189 match val {190 Val::Bool(v) => {191 if *v {192 buf.push_str("true")193 } else {194 buf.push_str("false")195 }196 }197 Val::Null => buf.push_str("null"),198 Val::Str(s) => {199 if s.is_empty() {200 buf.push_str("\"\"");201 } else if let Some(s) = s.strip_suffix('\n') {202 buf.push('|');203 for line in s.split('\n') {204 buf.push('\n');205 buf.push_str(options.padding);206 buf.push_str(line);207 }208 } else {209 escape_string_json_buf(s, buf)210 }211 }212 Val::Num(n) => write!(buf, "{}", *n).unwrap(),213 Val::Arr(a) => {214 if a.is_empty() {215 buf.push_str("[]");216 } else {217 for (i, item) in a.iter().enumerate() {218 if i != 0 {219 buf.push('\n');220 buf.push_str(cur_padding);221 }222 let item = item?;223 buf.push('-');224 match &item {225 Val::Arr(a) if !a.is_empty() => {226 buf.push('\n');227 buf.push_str(cur_padding);228 buf.push_str(options.padding);229 }230 _ => buf.push(' '),231 }232 let extra_padding = match &item {233 Val::Arr(a) => !a.is_empty(),234 Val::Obj(o) => !o.is_empty(),235 _ => false,236 };237 let prev_len = cur_padding.len();238 if extra_padding {239 cur_padding.push_str(options.padding);240 }241 manifest_yaml_ex_buf(&item, buf, cur_padding, options)?;242 cur_padding.truncate(prev_len);243 }244 }245 }246 Val::Obj(o) => {247 if o.is_empty() {248 buf.push_str("{}");249 } else {250 for (i, key) in o.fields().iter().enumerate() {251 if i != 0 {252 buf.push('\n');253 buf.push_str(cur_padding);254 }255 escape_string_json_buf(key, buf);256 buf.push(':');257 let prev_len = cur_padding.len();258 let item = o.get(key.clone())?.expect("field exists");259 match &item {260 Val::Arr(a) if !a.is_empty() => {261 buf.push('\n');262 buf.push_str(cur_padding);263 buf.push_str(options.arr_element_padding);264 cur_padding.push_str(options.arr_element_padding);265 }266 Val::Obj(o) if !o.is_empty() => {267 buf.push('\n');268 buf.push_str(cur_padding);269 buf.push_str(options.padding);270 cur_padding.push_str(options.padding);271 }272 _ => buf.push(' '),273 }274 manifest_yaml_ex_buf(&item, buf, cur_padding, options)?;275 cur_padding.truncate(prev_len);276 }277 }278 }279 Val::Func(_) => throw!(RuntimeError("tried to manifest function".into())),280 }281 Ok(())282}crates/jrsonnet-evaluator/src/builtin/mod.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/builtin/mod.rs
+++ b/crates/jrsonnet-evaluator/src/builtin/mod.rs
@@ -121,7 +121,7 @@
("trace".into(), builtin_trace),
("join".into(), builtin_join),
("escapeStringJson".into(), builtin_escape_string_json),
- ("manifestJsonEx".into(), builtin_manifest_json_ex),
+ ("manifestJsonExImpl".into(), builtin_manifest_json_ex_impl),
("manifestYamlDocImpl".into(), builtin_manifest_yaml_doc),
("reverse".into(), builtin_reverse),
("id".into(), builtin_id),
@@ -754,18 +754,22 @@
})
}
-fn builtin_manifest_json_ex(
+fn builtin_manifest_json_ex_impl(
context: Context,
_loc: Option<&ExprLocation>,
args: &ArgsDesc,
) -> Result<Val> {
- parse_args!(context, "manifestJsonEx", args, 2, [
+ parse_args!(context, "manifestJsonEx", args, 4, [
0, value: ty!(any);
1, indent: ty!(string) => Val::Str;
+ 2, newline: ty!(string) => Val::Str;
+ 3, key_val_sep: ty!(string) => Val::Str;
], {
Ok(Val::Str(manifest_json_ex(&value, &ManifestJsonOptions {
padding: &indent,
mtype: ManifestType::Std,
+ newline: &newline,
+ key_val_sep: &key_val_sep,
})?.into()))
})
}
crates/jrsonnet-evaluator/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/lib.rs
+++ b/crates/jrsonnet-evaluator/src/lib.rs
@@ -810,6 +810,14 @@
}
#[test]
+ fn json_minified() {
+ assert_json!(
+ r#"std.manifestJsonMinified({a:3, b:4, c:6})"#,
+ r#""{\"a\":3,\"b\":4,\"c\":6}""#
+ );
+ }
+
+ #[test]
fn parse_json() {
assert_json!(
r#"std.parseJson('{"a": -1,"b": 1,"c": 3.141,"d": []}')"#,
crates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/val.rs
+++ b/crates/jrsonnet-evaluator/src/val.rs
@@ -451,6 +451,8 @@
&ManifestJsonOptions {
padding: "",
mtype: ManifestType::ToString,
+ newline: "\n",
+ key_val_sep: ": ",
},
)?
.into(),
@@ -535,6 +537,8 @@
} else {
ManifestType::Manifest
},
+ newline: "\n",
+ key_val_sep: ": ",
},
)
.map(|s| s.into())
@@ -547,6 +551,8 @@
&ManifestJsonOptions {
padding: &" ".repeat(padding),
mtype: ManifestType::Std,
+ newline: "\n",
+ key_val_sep: ": ",
},
)
.map(|s| s.into())
crates/jrsonnet-stdlib/src/std.jsonnetdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/std.jsonnet
+++ b/crates/jrsonnet-stdlib/src/std.jsonnet
@@ -372,7 +372,11 @@
manifestJson(value):: std.manifestJsonEx(value, ' '),
- manifestJsonEx:: $intrinsic(manifestJsonEx),
+ manifestJsonMinified(value):: std.manifestJsonEx(value, '', '', ':'),
+
+ manifestJsonExImpl:: $intrinsic(manifestJsonExImpl),
+
+ manifestJsonEx(value, indent, newline='\n', key_val_sep=': '):: std.manifestJsonExImpl(value, indent, newline, key_val_sep),
manifestYamlDocImpl:: $intrinsic(manifestYamlDocImpl),