git.delta.rocks / jrsonnet / refs/commits / b118a83916a3

difftreelog

feat quote_keys option for Yaml

Yaroslav Bolyukin2021-11-27parent: #df9bc99.patch.diff
in: master

5 files changed

modifiedCargo.lockdiffbeforeafterboth
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -115,6 +115,12 @@
 ]
 
 [[package]]
+name = "dtoa"
+version = "0.4.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0"
+
+[[package]]
 name = "gcmodule"
 version = "0.3.3"
 source = "git+https://github.com/CertainLach/gcmodule?branch=jrsonnet#f72713c24c2b1bf5a78f1d01bee5a0f52bc2a094"
@@ -216,6 +222,7 @@
  "rustc-hash",
  "serde",
  "serde_json",
+ "serde_yaml",
  "thiserror",
 ]
 
@@ -273,6 +280,12 @@
 checksum = "8521a1b57e76b1ec69af7599e75e38e7b7fad6610f037db8c79b127201b5d119"
 
 [[package]]
+name = "linked-hash-map"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
+
+[[package]]
 name = "lock_api"
 version = "0.3.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -467,6 +480,17 @@
 ]
 
 [[package]]
+name = "serde_yaml"
+version = "0.8.21"
+source = "git+https://github.com/CertainLach/serde-yaml?branch=feature/old-octals-quirk#4bf0e325243539fdeb419e8d727ed1c161cbe445"
+dependencies = [
+ "dtoa",
+ "indexmap",
+ "serde",
+ "yaml-rust",
+]
+
+[[package]]
 name = "smallvec"
 version = "1.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -601,6 +625,15 @@
 checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
 
 [[package]]
+name = "yaml-rust"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
+dependencies = [
+ "linked-hash-map",
+]
+
+[[package]]
 name = "yansi-term"
 version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
modifiedcrates/jrsonnet-evaluator/src/builtin/manifest.rsdiffbeforeafterboth
before · crates/jrsonnet-evaluator/src/builtin/manifest.rs
1use crate::error::Error::*;2use crate::error::Result;3use crate::push_description_frame;4use crate::{throw, Val};56#[derive(PartialEq, Clone, Copy)]7pub enum ManifestType {8	// Applied in manifestification9	Manifest,10	/// Used for std.manifestJson11	/// Empty array/objects extends to "[\n\n]" instead of "[ ]" as in manifest12	Std,13	/// No line breaks, used in `obj+''`14	ToString,15	/// Minified json16	Minify,17}1819pub struct ManifestJsonOptions<'s> {20	pub padding: &'s str,21	pub mtype: ManifestType,22}2324pub fn manifest_json_ex(val: &Val, options: &ManifestJsonOptions<'_>) -> Result<String> {25	let mut out = String::new();26	manifest_json_ex_buf(val, &mut out, &mut String::new(), options)?;27	Ok(out)28}29fn manifest_json_ex_buf(30	val: &Val,31	buf: &mut String,32	cur_padding: &mut String,33	options: &ManifestJsonOptions<'_>,34) -> Result<()> {35	use std::fmt::Write;36	let mtype = options.mtype;37	match val {38		Val::Bool(v) => {39			if *v {40				buf.push_str("true");41			} else {42				buf.push_str("false");43			}44		}45		Val::Null => buf.push_str("null"),46		Val::Str(s) => escape_string_json_buf(s, buf),47		Val::Num(n) => write!(buf, "{}", n).unwrap(),48		Val::Arr(items) => {49			buf.push('[');50			if !items.is_empty() {51				if mtype != ManifestType::ToString && mtype != ManifestType::Minify {52					buf.push('\n');53				}5455				let old_len = cur_padding.len();56				cur_padding.push_str(options.padding);57				for (i, item) in items.iter().enumerate() {58					if i != 0 {59						buf.push(',');60						if mtype == ManifestType::ToString {61							buf.push(' ');62						} else if mtype != ManifestType::Minify {63							buf.push('\n');64						}65					}66					buf.push_str(cur_padding);67					manifest_json_ex_buf(&item?, buf, cur_padding, options)?;68				}69				cur_padding.truncate(old_len);7071				if mtype != ManifestType::ToString && mtype != ManifestType::Minify {72					buf.push('\n');73					buf.push_str(cur_padding);74				}75			} else if mtype == ManifestType::Std {76				buf.push_str("\n\n");77				buf.push_str(cur_padding);78			} else if mtype == ManifestType::ToString || mtype == ManifestType::Manifest {79				buf.push(' ');80			}81			buf.push(']');82		}83		Val::Obj(obj) => {84			obj.run_assertions()?;85			buf.push('{');86			let fields = obj.fields();87			if !fields.is_empty() {88				if mtype != ManifestType::ToString && mtype != ManifestType::Minify {89					buf.push('\n');90				}9192				let old_len = cur_padding.len();93				cur_padding.push_str(options.padding);94				for (i, field) in fields.into_iter().enumerate() {95					if i != 0 {96						buf.push(',');97						if mtype == ManifestType::ToString {98							buf.push(' ');99						} else if mtype != ManifestType::Minify {100							buf.push('\n');101						}102					}103					buf.push_str(cur_padding);104					escape_string_json_buf(&field, buf);105					buf.push_str(": ");106					push_description_frame(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							Ok(Val::Null)112						},113					)?;114				}115				cur_padding.truncate(old_len);116117				if mtype != ManifestType::ToString && mtype != ManifestType::Minify {118					buf.push('\n');119					buf.push_str(cur_padding);120				}121			} else if mtype == ManifestType::Std {122				buf.push_str("\n\n");123				buf.push_str(cur_padding);124			} else if mtype == ManifestType::ToString || mtype == ManifestType::Manifest {125				buf.push(' ');126			}127			buf.push('}');128		}129		Val::Func(_) => throw!(RuntimeError("tried to manifest function".into())),130	};131	Ok(())132}133134pub fn escape_string_json(s: &str) -> String {135	let mut buf = String::new();136	escape_string_json_buf(s, &mut buf);137	buf138}139140fn escape_string_json_buf(s: &str, buf: &mut String) {141	use std::fmt::Write;142	buf.push('"');143	for c in s.chars() {144		match c {145			'"' => buf.push_str("\\\""),146			'\\' => buf.push_str("\\\\"),147			'\u{0008}' => buf.push_str("\\b"),148			'\u{000c}' => buf.push_str("\\f"),149			'\n' => buf.push_str("\\n"),150			'\r' => buf.push_str("\\r"),151			'\t' => buf.push_str("\\t"),152			c if c < 32 as char || (c >= 127 as char && c <= 159 as char) => {153				write!(buf, "\\u{:04x}", c as u32).unwrap()154			}155			c => buf.push(c),156		}157	}158	buf.push('"');159}160161pub struct ManifestYamlOptions<'s> {162	/// Padding before fields, i.e163	/// ```yaml164	/// a:165	///   b:166	/// ## <- this167	/// ```168	pub padding: &'s str,169	/// Padding before array elements in objects170	/// ```yaml171	/// a:172	///   - 1173	/// ## <- this174	/// ```175	pub arr_element_padding: &'s str,176}177178pub fn manifest_yaml_ex(val: &Val, options: &ManifestYamlOptions<'_>) -> Result<String> {179	let mut out = String::new();180	manifest_yaml_ex_buf(val, &mut out, &mut String::new(), options)?;181	Ok(out)182}183fn manifest_yaml_ex_buf(184	val: &Val,185	buf: &mut String,186	cur_padding: &mut String,187	options: &ManifestYamlOptions<'_>,188) -> Result<()> {189	use std::fmt::Write;190	match val {191		Val::Bool(v) => {192			if *v {193				buf.push_str("true")194			} else {195				buf.push_str("false")196			}197		}198		Val::Null => buf.push_str("null"),199		Val::Str(s) => {200			if s.is_empty() {201				buf.push_str("\"\"");202			} else if let Some(s) = s.strip_suffix('\n') {203				buf.push('|');204				for line in s.split('\n') {205					buf.push('\n');206					buf.push_str(options.padding);207					buf.push_str(line);208				}209			} else {210				escape_string_json_buf(s, buf)211			}212		}213		Val::Num(n) => write!(buf, "{}", *n).unwrap(),214		Val::Arr(a) => {215			if a.is_empty() {216				buf.push_str("[]");217			} else {218				for (i, item) in a.iter().enumerate() {219					if i != 0 {220						buf.push('\n');221						buf.push_str(cur_padding);222					}223					let item = item?;224					buf.push('-');225					match &item {226						Val::Arr(a) if !a.is_empty() => {227							buf.push('\n');228							buf.push_str(cur_padding);229							buf.push_str(options.padding);230						}231						_ => buf.push(' '),232					}233					let extra_padding = match &item {234						Val::Arr(a) => !a.is_empty(),235						Val::Obj(o) => !o.is_empty(),236						_ => false,237					};238					let prev_len = cur_padding.len();239					if extra_padding {240						cur_padding.push_str(options.padding);241					}242					manifest_yaml_ex_buf(&item, buf, cur_padding, options)?;243					cur_padding.truncate(prev_len);244				}245			}246		}247		Val::Obj(o) => {248			if o.is_empty() {249				buf.push_str("{}");250			} else {251				for (i, key) in o.fields().iter().enumerate() {252					if i != 0 {253						buf.push('\n');254						buf.push_str(cur_padding);255					}256					escape_string_json_buf(key, buf);257					buf.push(':');258					let prev_len = cur_padding.len();259					let item = o.get(key.clone())?.expect("field exists");260					match &item {261						Val::Arr(a) if !a.is_empty() => {262							buf.push('\n');263							buf.push_str(cur_padding);264							buf.push_str(options.arr_element_padding);265							cur_padding.push_str(options.arr_element_padding);266						}267						Val::Obj(o) if !o.is_empty() => {268							buf.push('\n');269							buf.push_str(cur_padding);270							buf.push_str(options.padding);271							cur_padding.push_str(options.padding);272						}273						_ => buf.push(' '),274					}275					manifest_yaml_ex_buf(&item, buf, cur_padding, options)?;276					cur_padding.truncate(prev_len);277				}278			}279		}280		Val::Func(_) => throw!(RuntimeError("tried to manifest function".into())),281	}282	Ok(())283}
modifiedcrates/jrsonnet-evaluator/src/builtin/mod.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/builtin/mod.rs
+++ b/crates/jrsonnet-evaluator/src/builtin/mod.rs
@@ -753,13 +753,15 @@
 	_loc: &ExprLocation,
 	args: &ArgsDesc,
 ) -> Result<Val> {
-	parse_args!(context, "manifestYamlDoc", args, 2, [
+	parse_args!(context, "manifestYamlDoc", args, 3, [
 		0, value: ty!(any);
 		1, indent_array_in_object: ty!(boolean) => Val::Bool;
+		2, quote_keys: ty!(boolean) => Val::Bool;
 	], {
 		Ok(Val::Str(manifest_yaml_ex(&value, &ManifestYamlOptions {
 			padding: "  ",
 			arr_element_padding: if indent_array_in_object { "  " } else { "" },
+			quote_keys,
 		})?.into()))
 	})
 }
modifiedcrates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/val.rs
+++ b/crates/jrsonnet-evaluator/src/val.rs
@@ -556,6 +556,7 @@
 			&ManifestYamlOptions {
 				padding,
 				arr_element_padding: padding,
+				quote_keys: false,
 			},
 		)
 		.map(|s| s.into())
modifiedcrates/jrsonnet-stdlib/src/std.jsonnetdiffbeforeafterboth
--- a/crates/jrsonnet-stdlib/src/std.jsonnet
+++ b/crates/jrsonnet-stdlib/src/std.jsonnet
@@ -377,7 +377,7 @@
 
   manifestYamlDocImpl:: $intrinsic(manifestYamlDocImpl),
 
-  manifestYamlDoc(value, indent_array_in_object=false):: std.manifestYamlDocImpl(value, indent_array_in_object),
+  manifestYamlDoc(value, indent_array_in_object=false, quote_keys=true):: std.manifestYamlDocImpl(value, indent_array_in_object, quote_keys),
 
   manifestYamlStream(value, indent_array_in_object=false, c_document_end=true)::
     if !std.isArray(value) then