difftreelog
perf std.manifestTomlEx builtin
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
@@ -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 {
crates/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),
crates/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),
+ ))
+}
crates/jrsonnet-stdlib/src/manifest/toml.rsdiffbeforeafterboth1use 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}crates/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),