git.delta.rocks / jrsonnet / refs/commits / 88907d8b259a

difftreelog

perf move ini manifestification to native

Yaroslav Bolyukin2024-05-19parent: #0ef9787.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
@@ -4,7 +4,7 @@
 use jrsonnet_evaluator::manifest::{
 	JsonFormat, ManifestFormat, StringFormat, ToStringFormat, YamlStreamFormat,
 };
-use jrsonnet_stdlib::{TomlFormat, XmlJsonmlFormat, YamlFormat};
+use jrsonnet_stdlib::{IniFormat, TomlFormat, XmlJsonmlFormat, YamlFormat};
 
 #[derive(Clone, Copy, ValueEnum)]
 pub enum ManifestFormatName {
@@ -14,6 +14,7 @@
 	Yaml,
 	Toml,
 	XmlJsonml,
+	Ini,
 }
 
 #[derive(Parser)]
@@ -72,6 +73,10 @@
 					preserve_order,
 				)),
 				ManifestFormatName::XmlJsonml => Box::new(XmlJsonmlFormat::cli()),
+				ManifestFormatName::Ini => Box::new(IniFormat::cli(
+					#[cfg(feature = "exp-preserve-order")]
+					preserve_order,
+				)),
 			}
 		};
 		if self.yaml_stream {
modifiedcrates/jrsonnet-stdlib/src/lib.rsdiffbeforeafterboth
--- a/crates/jrsonnet-stdlib/src/lib.rs
+++ b/crates/jrsonnet-stdlib/src/lib.rs
@@ -179,6 +179,7 @@
 		("manifestPython", builtin_manifest_python::INST),
 		("manifestPythonVars", builtin_manifest_python_vars::INST),
 		("manifestXmlJsonml", builtin_manifest_xml_jsonml::INST),
+		("manifestIni", builtin_manifest_ini::INST),
 		// Parse
 		("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,8 +1,10 @@
+mod ini;
 mod python;
 mod toml;
 mod xml;
 mod yaml;
 
+pub use ini::IniFormat;
 use jrsonnet_evaluator::{
 	function::builtin,
 	manifest::{escape_string_json, JsonFormat, YamlStreamFormat},
@@ -190,3 +192,17 @@
 pub fn builtin_manifest_xml_jsonml(value: Val) -> Result<String> {
 	value.manifest(XmlJsonmlFormat::std_to_xml())
 }
+
+#[builtin]
+pub fn builtin_manifest_ini(
+	ini: Val,
+
+	#[default(false)]
+	#[cfg(feature = "exp-preserve-order")]
+	preserve_order: bool,
+) -> Result<String> {
+	ini.manifest(IniFormat::std(
+		#[cfg(feature = "exp-preserve-order")]
+		preserve_order,
+	))
+}
modifiedcrates/jrsonnet-stdlib/src/manifest/toml.rsdiffbeforeafterboth
before · crates/jrsonnet-stdlib/src/manifest/toml.rs
1use std::borrow::Cow;23use jrsonnet_evaluator::{4	bail,5	manifest::{escape_string_json_buf, ManifestFormat},6	val::ArrValue,7	IStr, ObjValue, Result, ResultExt, State, 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		#[cfg(feature = "exp-bigint")]107		Val::BigInt(n) => write!(buf, "{n}").unwrap(),108		Val::Arr(a) => {109			buf.push('[');110111			let mut had_items = false;112			for (i, e) in a.iter().enumerate() {113				had_items = true;114				let e = e.with_description(|| format!("elem <{i}> evaluation"))?;115116				if i != 0 {117					buf.push(',');118				}119				if inline {120					buf.push(' ');121				} else {122					buf.push('\n');123					buf.push_str(cur_padding);124					buf.push_str(&options.padding);125				}126127				State::push_description(128					|| format!("elem <{i}> manifestification"),129					|| manifest_value(&e, true, buf, "", options),130				)?;131			}132133			if !had_items {134			} else if inline {135				buf.push(' ');136			} else {137				buf.push('\n');138				buf.push_str(cur_padding);139			}140			buf.push(']');141		}142		Val::Obj(o) => {143			o.run_assertions()?;144			buf.push('{');145146			let mut had_fields = false;147			for (i, (k, v)) in o148				.iter(149					#[cfg(feature = "exp-preserve-order")]150					options.preserve_order,151				)152				.enumerate()153			{154				had_fields = true;155				let v = v.with_description(|| format!("field <{k}> evaluation"))?;156157				if i != 0 {158					buf.push(',');159				}160				buf.push(' ');161162				escape_key_toml_buf(&k, buf);163				buf.push_str(" = ");164				State::push_description(165					|| format!("field <{k}> manifestification"),166					|| manifest_value(&v, true, buf, "", options),167				)?;168			}169170			if had_fields {171				buf.push(' ');172			}173174			buf.push('}');175		}176		Val::Null => {177			bail!("tried to manifest null")178		}179		Val::Func(_) => {180			bail!("tried to manifest function")181		}182	}183	Ok(())184}185186fn manifest_table_internal(187	obj: &ObjValue,188	path: &mut Vec<IStr>,189	buf: &mut String,190	cur_padding: &mut String,191	options: &TomlFormat<'_>,192) -> Result<()> {193	let mut sections = Vec::new();194	let mut first = true;195	for (key, value) in obj.iter(196		#[cfg(feature = "exp-preserve-order")]197		options.preserve_order,198	) {199		let value = value?;200		if is_section(&value)? {201			sections.push((key, value));202		} else {203			if !first {204				buf.push('\n');205			}206			first = false;207			buf.push_str(cur_padding);208			escape_key_toml_buf(&key, buf);209			buf.push_str(" = ");210			manifest_value(&value, false, buf, cur_padding, options)?;211		}212	}213	for (k, v) in sections {214		if !first {215			buf.push_str("\n\n");216		}217		first = false;218		path.push(k);219		match v {220			Val::Obj(obj) => manifest_table(&obj, path, buf, cur_padding, options)?,221			Val::Arr(arr) => manifest_table_array(&arr, path, buf, cur_padding, options)?,222			_ => unreachable!("iterating over sections"),223		}224		path.pop();225	}226	Ok(())227}228229fn manifest_table(230	obj: &ObjValue,231	path: &mut Vec<IStr>,232	buf: &mut String,233	cur_padding: &mut String,234	options: &TomlFormat<'_>,235) -> Result<()> {236	if options.skip_empty_sections237		&& !obj.is_empty()238		&& obj239			.iter(240				#[cfg(feature = "exp-preserve-order")]241				false,242			)243			.try_fold(true, |c, (_, v)| Ok(c && is_section(&v?)?) as Result<bool>)?244	{245		manifest_table_internal(obj, path, buf, cur_padding, options)?;246		return Ok(());247	}248	buf.push_str(cur_padding);249	buf.push('[');250	for (i, k) in path.iter().enumerate() {251		if i != 0 {252			buf.push('.');253		}254		escape_key_toml_buf(k, buf);255	}256	buf.push(']');257	if obj.is_empty() {258		return Ok(());259	}260	buf.push('\n');261	let prev_len = cur_padding.len();262	cur_padding.push_str(&options.padding);263	manifest_table_internal(obj, path, buf, cur_padding, options)?;264	cur_padding.truncate(prev_len);265	Ok(())266}267fn manifest_table_array(268	arr: &ArrValue,269	path: &mut Vec<IStr>,270	buf: &mut String,271	cur_padding: &mut String,272	options: &TomlFormat<'_>,273) -> Result<()> {274	let mut formatted_path = String::new();275	{276		formatted_path.push_str(cur_padding);277		formatted_path.push_str("[[");278		for (i, k) in path.iter().enumerate() {279			if i != 0 {280				formatted_path.push('.');281			}282			escape_key_toml_buf(k, &mut formatted_path);283		}284		formatted_path.push_str("]]");285	}286	let prev_len = cur_padding.len();287	cur_padding.push_str(&options.padding);288	for (i, e) in arr.iter().enumerate() {289		let obj = e.expect("already tested").as_obj().expect("already tested");290		if i != 0 {291			buf.push_str("\n\n");292		}293		buf.push_str(&formatted_path);294		if obj.is_empty() {295			continue;296		}297		buf.push('\n');298		manifest_table_internal(&obj, path, buf, cur_padding, options)?;299	}300	cur_padding.truncate(prev_len);301	Ok(())302}303304impl ManifestFormat for TomlFormat<'_> {305	fn manifest_buf(&self, val: Val, buf: &mut String) -> jrsonnet_evaluator::Result<()> {306		match val {307			Val::Obj(obj) => {308				manifest_table_internal(&obj, &mut Vec::new(), buf, &mut String::new(), self)309			}310			_ => bail!("toml body should be object"),311		}312	}313}
after · crates/jrsonnet-stdlib/src/manifest/toml.rs
1use std::borrow::Cow;23use jrsonnet_evaluator::{4	bail,5	manifest::{escape_string_json_buf, ManifestFormat},6	val::ArrValue,7	IStr, ObjValue, Result, ResultExt, State, 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		#[cfg(feature = "exp-bigint")]107		Val::BigInt(n) => write!(buf, "{n}").unwrap(),108		Val::Arr(a) => {109			buf.push('[');110111			let mut had_items = false;112			for (i, e) in a.iter().enumerate() {113				had_items = true;114				let e = e.with_description(|| format!("elem <{i}> evaluation"))?;115116				if i != 0 {117					buf.push(',');118				}119				if inline {120					buf.push(' ');121				} else {122					buf.push('\n');123					buf.push_str(cur_padding);124					buf.push_str(&options.padding);125				}126127				State::push_description(128					|| format!("elem <{i}> manifestification"),129					|| manifest_value(&e, true, buf, "", options),130				)?;131			}132133			if !had_items {134			} else if inline {135				buf.push(' ');136			} else {137				buf.push('\n');138				buf.push_str(cur_padding);139			}140			buf.push(']');141		}142		Val::Obj(o) => {143			o.run_assertions()?;144			buf.push('{');145146			let mut had_fields = false;147			for (i, (k, v)) in o148				.iter(149					#[cfg(feature = "exp-preserve-order")]150					options.preserve_order,151				)152				.enumerate()153			{154				had_fields = true;155				let v = v.with_description(|| format!("field <{k}> evaluation"))?;156157				if i != 0 {158					buf.push(',');159				}160				buf.push(' ');161162				escape_key_toml_buf(&k, buf);163				buf.push_str(" = ");164				State::push_description(165					|| format!("field <{k}> manifestification"),166					|| manifest_value(&v, true, buf, "", options),167				)?;168			}169170			if had_fields {171				buf.push(' ');172			}173174			buf.push('}');175		}176		Val::Null => {177			bail!("tried to manifest null")178		}179		Val::Func(_) => {180			bail!("tried to manifest function")181		}182	}183	Ok(())184}185186fn manifest_table_internal(187	obj: &ObjValue,188	path: &mut Vec<IStr>,189	buf: &mut String,190	cur_padding: &mut String,191	options: &TomlFormat<'_>,192) -> Result<()> {193	let mut sections = Vec::new();194	let mut first = true;195	for (key, value) in obj.iter(196		#[cfg(feature = "exp-preserve-order")]197		options.preserve_order,198	) {199		let value = value.with_description(|| format!("field <{key}> evaluation"))?;200		if is_section(&value)? {201			sections.push((key, value));202		} else {203			if !first {204				buf.push('\n');205			}206			first = false;207			buf.push_str(cur_padding);208			escape_key_toml_buf(&key, buf);209			buf.push_str(" = ");210			manifest_value(&value, false, buf, cur_padding, options)?;211		}212	}213	for (k, v) in sections {214		if !first {215			buf.push_str("\n\n");216		}217		first = false;218		path.push(k);219		match v {220			Val::Obj(obj) => manifest_table(&obj, path, buf, cur_padding, options)?,221			Val::Arr(arr) => manifest_table_array(&arr, path, buf, cur_padding, options)?,222			_ => unreachable!("iterating over sections"),223		}224		path.pop();225	}226	Ok(())227}228229fn manifest_table(230	obj: &ObjValue,231	path: &mut Vec<IStr>,232	buf: &mut String,233	cur_padding: &mut String,234	options: &TomlFormat<'_>,235) -> Result<()> {236	if options.skip_empty_sections237		&& !obj.is_empty()238		&& obj239			.iter(240				#[cfg(feature = "exp-preserve-order")]241				false,242			)243			.try_fold(true, |c, (_, v)| Ok(c && is_section(&v?)?) as Result<bool>)?244	{245		manifest_table_internal(obj, path, buf, cur_padding, options)?;246		return Ok(());247	}248	buf.push_str(cur_padding);249	buf.push('[');250	for (i, k) in path.iter().enumerate() {251		if i != 0 {252			buf.push('.');253		}254		escape_key_toml_buf(k, buf);255	}256	buf.push(']');257	if obj.is_empty() {258		return Ok(());259	}260	buf.push('\n');261	let prev_len = cur_padding.len();262	cur_padding.push_str(&options.padding);263	manifest_table_internal(obj, path, buf, cur_padding, options)?;264	cur_padding.truncate(prev_len);265	Ok(())266}267fn manifest_table_array(268	arr: &ArrValue,269	path: &mut Vec<IStr>,270	buf: &mut String,271	cur_padding: &mut String,272	options: &TomlFormat<'_>,273) -> Result<()> {274	let mut formatted_path = String::new();275	{276		formatted_path.push_str(cur_padding);277		formatted_path.push_str("[[");278		for (i, k) in path.iter().enumerate() {279			if i != 0 {280				formatted_path.push('.');281			}282			escape_key_toml_buf(k, &mut formatted_path);283		}284		formatted_path.push_str("]]");285	}286	let prev_len = cur_padding.len();287	cur_padding.push_str(&options.padding);288	for (i, e) in arr.iter().enumerate() {289		let obj = e.expect("already tested").as_obj().expect("already tested");290		if i != 0 {291			buf.push_str("\n\n");292		}293		buf.push_str(&formatted_path);294		if obj.is_empty() {295			continue;296		}297		buf.push('\n');298		manifest_table_internal(&obj, path, buf, cur_padding, options)?;299	}300	cur_padding.truncate(prev_len);301	Ok(())302}303304impl ManifestFormat for TomlFormat<'_> {305	fn manifest_buf(&self, val: Val, buf: &mut String) -> jrsonnet_evaluator::Result<()> {306		match val {307			Val::Obj(obj) => {308				manifest_table_internal(&obj, &mut Vec::new(), buf, &mut String::new(), self)309			}310			_ => bail!("toml body should be object"),311		}312	}313}
modifiedcrates/jrsonnet-stdlib/src/std.jsonnetdiffbeforeafterboth
--- a/crates/jrsonnet-stdlib/src/std.jsonnet
+++ b/crates/jrsonnet-stdlib/src/std.jsonnet
@@ -25,26 +25,6 @@
     else
       error 'Assertion failed. ' + a + ' != ' + b,
 
-  manifestIni(ini)::
-    local body_lines(body) =
-      std.join([], [
-        local value_or_values = body[k];
-        if std.isArray(value_or_values) then
-          ['%s = %s' % [k, value] for value in value_or_values]
-        else
-          ['%s = %s' % [k, value_or_values]]
-
-        for k in std.objectFields(body)
-      ]);
-
-    local section_lines(sname, sbody) = ['[%s]' % [sname]] + body_lines(sbody),
-          main_body = if std.objectHas(ini, 'main') then body_lines(ini.main) else [],
-          all_sections = [
-      section_lines(k, ini.sections[k])
-      for k in std.objectFields(ini.sections)
-    ];
-    std.join('\n', main_body + std.flattenArrays(all_sections) + ['']),
-
   mergePatch(target, patch)::
     if std.isObject(patch) then
       local target_object =