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

difftreelog

perf std.manifestTomlEx builtin

Yaroslav Bolyukin2022-12-03parent: #68bea05.patch.diff
in: master

5 files changed

modifiedcrates/jrsonnet-cli/src/manifest.rsdiffbeforeafterboth
--- a/crates/jrsonnet-cli/src/manifest.rs
+++ b/crates/jrsonnet-cli/src/manifest.rs
@@ -1,4 +1,4 @@
-use std::{path::PathBuf, str::FromStr};
+use std::path::PathBuf;
 
 use clap::{Parser, ValueEnum};
 use jrsonnet_evaluator::{
@@ -6,7 +6,7 @@
 	manifest::{JsonFormat, ManifestFormat, StringFormat, ToStringFormat, YamlStreamFormat},
 	State,
 };
-use jrsonnet_stdlib::YamlFormat;
+use jrsonnet_stdlib::{TomlFormat, YamlFormat};
 
 use crate::ConfigureState;
 
@@ -16,18 +16,7 @@
 	String,
 	Json,
 	Yaml,
-}
-
-impl FromStr for ManifestFormatName {
-	type Err = &'static str;
-	fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
-		Ok(match s {
-			"string" => ManifestFormatName::String,
-			"json" => ManifestFormatName::Json,
-			"yaml" => ManifestFormatName::Yaml,
-			_ => return Err("no such format"),
-		})
-	}
+	Toml,
 }
 
 #[derive(Parser)]
@@ -44,7 +33,7 @@
 	#[clap(long, short = 'y', conflicts_with = "string")]
 	yaml_stream: bool,
 	/// Number of spaces to pad output manifest with.
-	/// `0` for hard tabs, `-1` for single line output [default: 3 for json, 2 for yaml]
+	/// `0` for hard tabs, `-1` for single line output [default: 3 for json, 2 for yaml/toml]
 	#[clap(long)]
 	line_padding: Option<usize>,
 	/// Preserve order in object manifestification
@@ -72,6 +61,11 @@
 					#[cfg(feature = "exp-preserve-order")]
 					preserve_order,
 				)),
+				ManifestFormatName::Toml => Box::new(TomlFormat::cli(
+					self.line_padding.unwrap_or(2),
+					#[cfg(feature = "exp-preserve-order")]
+					preserve_order,
+				)),
 			}
 		};
 		Ok(if self.yaml_stream {
modifiedcrates/jrsonnet-stdlib/src/lib.rsdiffbeforeafterboth
--- a/crates/jrsonnet-stdlib/src/lib.rs
+++ b/crates/jrsonnet-stdlib/src/lib.rs
@@ -121,6 +121,7 @@
 		("escapeStringJson", builtin_escape_string_json::INST),
 		("manifestJsonEx", builtin_manifest_json_ex::INST),
 		("manifestYamlDoc", builtin_manifest_yaml_doc::INST),
+		("manifestTomlEx", builtin_manifest_toml_ex::INST),
 		// Parsing
 		("parseJson", builtin_parse_json::INST),
 		("parseYaml", builtin_parse_yaml::INST),
modifiedcrates/jrsonnet-stdlib/src/manifest/mod.rsdiffbeforeafterboth
--- a/crates/jrsonnet-stdlib/src/manifest/mod.rs
+++ b/crates/jrsonnet-stdlib/src/manifest/mod.rs
@@ -1,3 +1,4 @@
+mod toml;
 mod yaml;
 
 use jrsonnet_evaluator::{
@@ -5,8 +6,9 @@
 	function::builtin,
 	manifest::{escape_string_json, JsonFormat},
 	typed::Any,
-	IStr,
+	IStr, ObjValue, Val,
 };
+pub use toml::TomlFormat;
 pub use yaml::YamlFormat;
 
 #[builtin]
@@ -47,3 +49,16 @@
 		preserve_order.unwrap_or(false),
 	))
 }
+
+#[builtin]
+pub fn builtin_manifest_toml_ex(
+	value: ObjValue,
+	indent: IStr,
+	#[cfg(feature = "exp-preserve-order")] preserve_order: Option<bool>,
+) -> Result<String> {
+	Val::Obj(value).manifest(TomlFormat::std_to_toml(
+		indent.to_string(),
+		#[cfg(feature = "exp-preserve-order")]
+		preserve_order.unwrap_or(false),
+	))
+}
addedcrates/jrsonnet-stdlib/src/manifest/toml.rsdiffbeforeafterboth
after · crates/jrsonnet-stdlib/src/manifest/toml.rs
1use std::borrow::Cow;23use jrsonnet_evaluator::{4	manifest::{escape_string_json_buf, ManifestFormat},5	throw,6	val::ArrValue,7	IStr, ObjValue, Result, Val,8};910pub struct TomlFormat<'s> {11	/// Padding before fields, i.e12	/// ```toml13	/// [a]14	///   b = 115	/// ## <- this16	/// ```17	padding: Cow<'s, str>,18	/// Do not emit sections for objects, consisting only from sections:19	/// ```toml20	/// # false21	/// [a]22	/// [a.b]23	///24	/// # true25	/// [a.b]26	/// ```27	skip_empty_sections: bool,28	/// If true - then order of fields is preserved as written,29	/// instead of sorting alphabetically30	#[cfg(feature = "exp-preserve-order")]31	preserve_order: bool,32}33impl TomlFormat<'_> {34	pub fn cli(35		padding: usize,36		#[cfg(feature = "exp-preserve-order")] preserve_order: bool,37	) -> Self {38		let padding = " ".repeat(padding);39		Self {40			padding: Cow::Owned(padding),41			skip_empty_sections: true,42			#[cfg(feature = "exp-preserve-order")]43			preserve_order,44		}45	}46	pub fn std_to_toml(47		padding: String,48		#[cfg(feature = "exp-preserve-order")] preserve_order: bool,49	) -> Self {50		Self {51			padding: Cow::Owned(padding),52			skip_empty_sections: false,53			#[cfg(feature = "exp-preserve-order")]54			preserve_order,55		}56	}57}5859fn bare_allowed(s: &str) -> bool {60	s.bytes()61		.all(|c| matches!(c, b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'_' | b'-'))62}6364fn escape_key_toml_buf(key: &str, buf: &mut String) {65	if bare_allowed(key) {66		buf.push_str(key);67	} else {68		escape_string_json_buf(key, buf);69	}70}7172fn is_section(val: &Val) -> Result<bool> {73	Ok(match val {74		Val::Arr(a) => {75			if a.is_empty() {76				return Ok(false);77			}78			for e in a.iter() {79				let e = e?;80				if !matches!(e, Val::Obj(_)) {81					return Ok(false);82				}83			}84			true85		}86		Val::Obj(_) => true,87		_ => false,88	})89}9091fn manifest_value(92	val: &Val,93	inline: bool,94	buf: &mut String,95	cur_padding: &str,96	options: &TomlFormat<'_>,97) -> Result<()> {98	use std::fmt::Write;99	match val {100		Val::Bool(true) => buf.push_str("true"),101		Val::Bool(false) => buf.push_str("false"),102		Val::Str(s) => {103			escape_string_json_buf(&s.clone().into_flat(), buf);104		}105		Val::Num(n) => write!(buf, "{n}").unwrap(),106		Val::Arr(a) => {107			if a.is_empty() {108				buf.push_str("[]");109				return Ok(());110			}111			for (i, e) in a.iter().enumerate() {112				let e = e?;113				if i != 0 {114					buf.push(',');115				} else {116					buf.push('[');117				}118				if inline {119					buf.push(' ');120				} else {121					buf.push('\n');122					buf.push_str(cur_padding);123					buf.push_str(&options.padding);124				}125				manifest_value(&e, true, buf, "", options)?;126			}127			if inline {128				buf.push(' ');129			} else {130				buf.push('\n');131				buf.push_str(cur_padding);132			}133			buf.push(']');134		}135		Val::Obj(o) => {136			if o.is_empty() {137				buf.push_str("{}");138			}139			buf.push_str("{ ");140			for (i, (k, v)) in o141				.iter(142					#[cfg(feature = "exp-preserve-order")]143					options.preserve_order,144				)145				.enumerate()146			{147				let v = v?;148				if i != 0 {149					buf.push_str(", ");150				}151				escape_key_toml_buf(&k, buf);152				buf.push_str(" = ");153				manifest_value(&v, true, buf, "", options)?;154			}155			buf.push_str(" }");156		}157		Val::Null => {158			throw!("tried to manifest null")159		}160		Val::Func(_) => {161			throw!("tried to manifest function")162		}163	}164	Ok(())165}166167fn manifest_table_internal(168	obj: &ObjValue,169	path: &mut Vec<IStr>,170	buf: &mut String,171	cur_padding: &mut String,172	options: &TomlFormat<'_>,173) -> Result<()> {174	let mut sections = Vec::new();175	let mut first = true;176	for (key, value) in obj.iter(177		#[cfg(feature = "exp-preserve-order")]178		options.preserve_order,179	) {180		let value = value?;181		if !is_section(&value)? {182			if !first {183				buf.push('\n');184			}185			first = false;186			buf.push_str(cur_padding);187			escape_key_toml_buf(&key, buf);188			buf.push_str(" = ");189			manifest_value(&value, false, buf, cur_padding, options)?;190		} else {191			sections.push((key, value));192		}193	}194	for (k, v) in sections {195		if !first {196			buf.push_str("\n\n");197		}198		first = false;199		path.push(k);200		match v {201			Val::Obj(obj) => manifest_table(&obj, path, buf, cur_padding, options)?,202			Val::Arr(arr) => manifest_table_array(&arr, path, buf, cur_padding, options)?,203			_ => unreachable!("iterating over sections"),204		}205		path.pop();206	}207	Ok(())208}209210fn manifest_table(211	obj: &ObjValue,212	path: &mut Vec<IStr>,213	buf: &mut String,214	cur_padding: &mut String,215	options: &TomlFormat<'_>,216) -> Result<()> {217	if options.skip_empty_sections218		&& !obj.is_empty()219		&& obj220			.iter(221				#[cfg(feature = "exp-preserve-order")]222				false,223			)224			.try_fold(true, |c, (_, v)| Ok(c && is_section(&v?)?) as Result<bool>)?225	{226		manifest_table_internal(obj, path, buf, cur_padding, options)?;227		return Ok(());228	}229	buf.push_str(cur_padding);230	buf.push('[');231	for (i, k) in path.iter().enumerate() {232		if i != 0 {233			buf.push('.');234		}235		escape_key_toml_buf(k, buf);236	}237	buf.push(']');238	if obj.is_empty() {239		return Ok(());240	}241	buf.push('\n');242	let prev_len = cur_padding.len();243	cur_padding.push_str(&options.padding);244	manifest_table_internal(obj, path, buf, cur_padding, options)?;245	cur_padding.truncate(prev_len);246	Ok(())247}248fn manifest_table_array(249	arr: &ArrValue,250	path: &mut Vec<IStr>,251	buf: &mut String,252	cur_padding: &mut String,253	options: &TomlFormat<'_>,254) -> Result<()> {255	let mut formatted_path = String::new();256	{257		formatted_path.push_str(cur_padding);258		formatted_path.push_str("[[");259		for (i, k) in path.iter().enumerate() {260			if i != 0 {261				formatted_path.push('.');262			}263			escape_key_toml_buf(k, &mut formatted_path);264		}265		formatted_path.push_str("]]");266	}267	let prev_len = cur_padding.len();268	cur_padding.push_str(&options.padding);269	for (i, e) in arr.iter().enumerate() {270		let obj = e.expect("already tested").as_obj().expect("already tested");271		if i != 0 {272			buf.push_str("\n\n");273		}274		buf.push_str(&formatted_path);275		if obj.is_empty() {276			continue;277		}278		buf.push('\n');279		manifest_table_internal(&obj, path, buf, cur_padding, options)?;280	}281	cur_padding.truncate(prev_len);282	Ok(())283}284285impl ManifestFormat for TomlFormat<'_> {286	fn manifest_buf(&self, val: Val, buf: &mut String) -> jrsonnet_evaluator::Result<()> {287		match val {288			Val::Obj(obj) => {289				manifest_table_internal(&obj, &mut Vec::new(), buf, &mut String::new(), self)290			}291			_ => throw!("toml body should be object"),292		}293	}294}
modifiedcrates/jrsonnet-stdlib/src/std.jsonnetdiffbeforeafterboth
--- a/crates/jrsonnet-stdlib/src/std.jsonnet
+++ b/crates/jrsonnet-stdlib/src/std.jsonnet
@@ -100,86 +100,6 @@
 
   manifestToml(value):: std.manifestTomlEx(value, '  '),
 
-  manifestTomlEx(value, indent)::
-    local
-      escapeStringToml = std.escapeStringJson,
-      escapeKeyToml(key) =
-        local bare_allowed = std.set(std.stringChars('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-'));
-        if std.setUnion(std.set(std.stringChars(key)), bare_allowed) == bare_allowed then key else escapeStringToml(key),
-      isTableArray(v) = std.isArray(v) && std.length(v) > 0 && std.foldl(function(a, b) a && std.isObject(b), v, true),
-      isSection(v) = std.isObject(v) || isTableArray(v),
-      renderValue(v, indexedPath, inline, cindent) =
-        if v == true then
-          'true'
-        else if v == false then
-          'false'
-        else if v == null then
-          error 'Tried to manifest "null" at ' + indexedPath
-        else if std.isNumber(v) then
-          '' + v
-        else if std.isString(v) then
-          escapeStringToml(v)
-        else if std.isFunction(v) then
-          error 'Tried to manifest function at ' + indexedPath
-        else if std.isArray(v) then
-          if std.length(v) == 0 then
-            '[]'
-          else
-            local range = std.range(0, std.length(v) - 1);
-            local new_indent = if inline then '' else cindent + indent;
-            local separator = if inline then ' ' else '\n';
-            local lines = ['[' + separator]
-                          + std.join([',' + separator],
-                                     [
-                                       [new_indent + renderValue(v[i], indexedPath + [i], true, '')]
-                                       for i in range
-                                     ])
-                          + [separator + (if inline then '' else cindent) + ']'];
-            std.join('', lines)
-        else if std.isObject(v) then
-          local lines = ['{ ']
-                        + std.join([', '],
-                                   [
-                                     [escapeKeyToml(k) + ' = ' + renderValue(v[k], indexedPath + [k], true, '')]
-                                     for k in std.objectFields(v)
-                                   ])
-                        + [' }'];
-          std.join('', lines),
-      renderTableInternal(v, path, indexedPath, cindent) =
-        local kvp = std.flattenArrays([
-          [cindent + escapeKeyToml(k) + ' = ' + renderValue(v[k], indexedPath + [k], false, cindent)]
-          for k in std.objectFields(v)
-          if !isSection(v[k])
-        ]);
-        local sections = [std.join('\n', kvp)] + [
-          (
-            if std.isObject(v[k]) then
-              renderTable(v[k], path + [k], indexedPath + [k], cindent)
-            else
-              renderTableArray(v[k], path + [k], indexedPath + [k], cindent)
-          )
-          for k in std.objectFields(v)
-          if isSection(v[k])
-        ];
-        std.join('\n\n', sections),
-      renderTable(v, path, indexedPath, cindent) =
-        cindent + '[' + std.join('.', std.map(escapeKeyToml, path)) + ']'
-        + (if v == {} then '' else '\n')
-        + renderTableInternal(v, path, indexedPath, cindent + indent),
-      renderTableArray(v, path, indexedPath, cindent) =
-        local range = std.range(0, std.length(v) - 1);
-        local sections = [
-          (cindent + '[[' + std.join('.', std.map(escapeKeyToml, path)) + ']]'
-           + (if v[i] == {} then '' else '\n')
-           + renderTableInternal(v[i], path, indexedPath + [i], cindent + indent))
-          for i in range
-        ];
-        std.join('\n\n', sections);
-    if std.isObject(value) then
-      renderTableInternal(value, [], [], '')
-    else
-      error 'TOML body must be an object. Got ' + std.type(value),
-
   escapeStringPython(str)::
     std.escapeStringJson(str),