difftreelog
perf move ini manifestification to native
in: master
5 files changed
crates/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 {
crates/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),
crates/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,
+ ))
+}
crates/jrsonnet-stdlib/src/manifest/toml.rsdiffbeforeafterboth1use 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}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}crates/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 =