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.rsdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-stdlib/src/manifest/toml.rs
@@ -0,0 +1,294 @@
+use std::borrow::Cow;
+
+use jrsonnet_evaluator::{
+ manifest::{escape_string_json_buf, ManifestFormat},
+ throw,
+ val::ArrValue,
+ IStr, ObjValue, Result, Val,
+};
+
+pub struct TomlFormat<'s> {
+ /// Padding before fields, i.e
+ /// ```toml
+ /// [a]
+ /// b = 1
+ /// ## <- this
+ /// ```
+ padding: Cow<'s, str>,
+ /// Do not emit sections for objects, consisting only from sections:
+ /// ```toml
+ /// # false
+ /// [a]
+ /// [a.b]
+ ///
+ /// # true
+ /// [a.b]
+ /// ```
+ skip_empty_sections: bool,
+ /// If true - then order of fields is preserved as written,
+ /// instead of sorting alphabetically
+ #[cfg(feature = "exp-preserve-order")]
+ preserve_order: bool,
+}
+impl TomlFormat<'_> {
+ pub fn cli(
+ padding: usize,
+ #[cfg(feature = "exp-preserve-order")] preserve_order: bool,
+ ) -> Self {
+ let padding = " ".repeat(padding);
+ Self {
+ padding: Cow::Owned(padding),
+ skip_empty_sections: true,
+ #[cfg(feature = "exp-preserve-order")]
+ preserve_order,
+ }
+ }
+ pub fn std_to_toml(
+ padding: String,
+ #[cfg(feature = "exp-preserve-order")] preserve_order: bool,
+ ) -> Self {
+ Self {
+ padding: Cow::Owned(padding),
+ skip_empty_sections: false,
+ #[cfg(feature = "exp-preserve-order")]
+ preserve_order,
+ }
+ }
+}
+
+fn bare_allowed(s: &str) -> bool {
+ s.bytes()
+ .all(|c| matches!(c, b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'_' | b'-'))
+}
+
+fn escape_key_toml_buf(key: &str, buf: &mut String) {
+ if bare_allowed(key) {
+ buf.push_str(key);
+ } else {
+ escape_string_json_buf(key, buf);
+ }
+}
+
+fn is_section(val: &Val) -> Result<bool> {
+ Ok(match val {
+ Val::Arr(a) => {
+ if a.is_empty() {
+ return Ok(false);
+ }
+ for e in a.iter() {
+ let e = e?;
+ if !matches!(e, Val::Obj(_)) {
+ return Ok(false);
+ }
+ }
+ true
+ }
+ Val::Obj(_) => true,
+ _ => false,
+ })
+}
+
+fn manifest_value(
+ val: &Val,
+ inline: bool,
+ buf: &mut String,
+ cur_padding: &str,
+ options: &TomlFormat<'_>,
+) -> Result<()> {
+ use std::fmt::Write;
+ match val {
+ Val::Bool(true) => buf.push_str("true"),
+ Val::Bool(false) => buf.push_str("false"),
+ Val::Str(s) => {
+ escape_string_json_buf(&s.clone().into_flat(), buf);
+ }
+ Val::Num(n) => write!(buf, "{n}").unwrap(),
+ Val::Arr(a) => {
+ if a.is_empty() {
+ buf.push_str("[]");
+ return Ok(());
+ }
+ for (i, e) in a.iter().enumerate() {
+ let e = e?;
+ if i != 0 {
+ buf.push(',');
+ } else {
+ buf.push('[');
+ }
+ if inline {
+ buf.push(' ');
+ } else {
+ buf.push('\n');
+ buf.push_str(cur_padding);
+ buf.push_str(&options.padding);
+ }
+ manifest_value(&e, true, buf, "", options)?;
+ }
+ if inline {
+ buf.push(' ');
+ } else {
+ buf.push('\n');
+ buf.push_str(cur_padding);
+ }
+ buf.push(']');
+ }
+ Val::Obj(o) => {
+ if o.is_empty() {
+ buf.push_str("{}");
+ }
+ buf.push_str("{ ");
+ for (i, (k, v)) in o
+ .iter(
+ #[cfg(feature = "exp-preserve-order")]
+ options.preserve_order,
+ )
+ .enumerate()
+ {
+ let v = v?;
+ if i != 0 {
+ buf.push_str(", ");
+ }
+ escape_key_toml_buf(&k, buf);
+ buf.push_str(" = ");
+ manifest_value(&v, true, buf, "", options)?;
+ }
+ buf.push_str(" }");
+ }
+ Val::Null => {
+ throw!("tried to manifest null")
+ }
+ Val::Func(_) => {
+ throw!("tried to manifest function")
+ }
+ }
+ Ok(())
+}
+
+fn manifest_table_internal(
+ obj: &ObjValue,
+ path: &mut Vec<IStr>,
+ buf: &mut String,
+ cur_padding: &mut String,
+ options: &TomlFormat<'_>,
+) -> Result<()> {
+ let mut sections = Vec::new();
+ let mut first = true;
+ for (key, value) in obj.iter(
+ #[cfg(feature = "exp-preserve-order")]
+ options.preserve_order,
+ ) {
+ let value = value?;
+ if !is_section(&value)? {
+ if !first {
+ buf.push('\n');
+ }
+ first = false;
+ buf.push_str(cur_padding);
+ escape_key_toml_buf(&key, buf);
+ buf.push_str(" = ");
+ manifest_value(&value, false, buf, cur_padding, options)?;
+ } else {
+ sections.push((key, value));
+ }
+ }
+ for (k, v) in sections {
+ if !first {
+ buf.push_str("\n\n");
+ }
+ first = false;
+ path.push(k);
+ match v {
+ Val::Obj(obj) => manifest_table(&obj, path, buf, cur_padding, options)?,
+ Val::Arr(arr) => manifest_table_array(&arr, path, buf, cur_padding, options)?,
+ _ => unreachable!("iterating over sections"),
+ }
+ path.pop();
+ }
+ Ok(())
+}
+
+fn manifest_table(
+ obj: &ObjValue,
+ path: &mut Vec<IStr>,
+ buf: &mut String,
+ cur_padding: &mut String,
+ options: &TomlFormat<'_>,
+) -> Result<()> {
+ if options.skip_empty_sections
+ && !obj.is_empty()
+ && obj
+ .iter(
+ #[cfg(feature = "exp-preserve-order")]
+ false,
+ )
+ .try_fold(true, |c, (_, v)| Ok(c && is_section(&v?)?) as Result<bool>)?
+ {
+ manifest_table_internal(obj, path, buf, cur_padding, options)?;
+ return Ok(());
+ }
+ buf.push_str(cur_padding);
+ buf.push('[');
+ for (i, k) in path.iter().enumerate() {
+ if i != 0 {
+ buf.push('.');
+ }
+ escape_key_toml_buf(k, buf);
+ }
+ buf.push(']');
+ if obj.is_empty() {
+ return Ok(());
+ }
+ buf.push('\n');
+ let prev_len = cur_padding.len();
+ cur_padding.push_str(&options.padding);
+ manifest_table_internal(obj, path, buf, cur_padding, options)?;
+ cur_padding.truncate(prev_len);
+ Ok(())
+}
+fn manifest_table_array(
+ arr: &ArrValue,
+ path: &mut Vec<IStr>,
+ buf: &mut String,
+ cur_padding: &mut String,
+ options: &TomlFormat<'_>,
+) -> Result<()> {
+ let mut formatted_path = String::new();
+ {
+ formatted_path.push_str(cur_padding);
+ formatted_path.push_str("[[");
+ for (i, k) in path.iter().enumerate() {
+ if i != 0 {
+ formatted_path.push('.');
+ }
+ escape_key_toml_buf(k, &mut formatted_path);
+ }
+ formatted_path.push_str("]]");
+ }
+ let prev_len = cur_padding.len();
+ cur_padding.push_str(&options.padding);
+ for (i, e) in arr.iter().enumerate() {
+ let obj = e.expect("already tested").as_obj().expect("already tested");
+ if i != 0 {
+ buf.push_str("\n\n");
+ }
+ buf.push_str(&formatted_path);
+ if obj.is_empty() {
+ continue;
+ }
+ buf.push('\n');
+ manifest_table_internal(&obj, path, buf, cur_padding, options)?;
+ }
+ cur_padding.truncate(prev_len);
+ Ok(())
+}
+
+impl ManifestFormat for TomlFormat<'_> {
+ fn manifest_buf(&self, val: Val, buf: &mut String) -> jrsonnet_evaluator::Result<()> {
+ match val {
+ Val::Obj(obj) => {
+ manifest_table_internal(&obj, &mut Vec::new(), buf, &mut String::new(), self)
+ }
+ _ => throw!("toml body should be object"),
+ }
+ }
+}
crates/jrsonnet-stdlib/src/std.jsonnetdiffbeforeafterboth1{2 local std = self,3 local id = std.id,45 thisFile:: error 'std.thisFile is deprecated, to enable its support in jrsonnet - recompile it with "legacy-this-file" support.\nThis will slow down stdlib caching a bit, though',67 toString(a):: '' + a,89 lstripChars(str, chars)::10 if std.length(str) > 0 && std.member(chars, str[0]) then11 std.lstripChars(str[1:], chars)12 else13 str,1415 rstripChars(str, chars)::16 local len = std.length(str);17 if len > 0 && std.member(chars, str[len - 1]) then18 std.rstripChars(str[:len - 1], chars)19 else20 str,2122 stripChars(str, chars)::23 std.lstripChars(std.rstripChars(str, chars), chars),2425 stringChars(str)::26 std.makeArray(std.length(str), function(i) str[i]),2728 split(str, c):: std.splitLimit(str, c, -1),2930 mapWithIndex(func, arr)::31 if !std.isFunction(func) then32 error ('std.mapWithIndex first param must be function, got ' + std.type(func))33 else if !std.isArray(arr) && !std.isString(arr) then34 error ('std.mapWithIndex second param must be array, got ' + std.type(arr))35 else36 std.makeArray(std.length(arr), function(i) func(i, arr[i])),3738 mapWithKey(func, obj)::39 if !std.isFunction(func) then40 error ('std.mapWithKey first param must be function, got ' + std.type(func))41 else if !std.isObject(obj) then42 error ('std.mapWithKey second param must be object, got ' + std.type(obj))43 else44 { [k]: func(k, obj[k]) for k in std.objectFields(obj) },4546 lines(arr)::47 std.join('\n', arr + ['']),4849 deepJoin(arr)::50 if std.isString(arr) then51 arr52 else if std.isArray(arr) then53 std.join('', [std.deepJoin(x) for x in arr])54 else55 error 'Expected string or array, got %s' % std.type(arr),5657 filterMap(filter_func, map_func, arr)::58 if !std.isFunction(filter_func) then59 error ('std.filterMap first param must be function, got ' + std.type(filter_func))60 else if !std.isFunction(map_func) then61 error ('std.filterMap second param must be function, got ' + std.type(map_func))62 else if !std.isArray(arr) then63 error ('std.filterMap third param must be array, got ' + std.type(arr))64 else65 std.map(map_func, std.filter(filter_func, arr)),6667 assertEqual(a, b)::68 if a == b then69 true70 else71 error 'Assertion failed. ' + a + ' != ' + b,7273 clamp(x, minVal, maxVal)::74 if x < minVal then minVal75 else if x > maxVal then maxVal76 else x,7778 flattenArrays(arrs)::79 std.foldl(function(a, b) a + b, arrs, []),8081 manifestIni(ini)::82 local body_lines(body) =83 std.join([], [84 local value_or_values = body[k];85 if std.isArray(value_or_values) then86 ['%s = %s' % [k, value] for value in value_or_values]87 else88 ['%s = %s' % [k, value_or_values]]8990 for k in std.objectFields(body)91 ]);9293 local section_lines(sname, sbody) = ['[%s]' % [sname]] + body_lines(sbody),94 main_body = if std.objectHas(ini, 'main') then body_lines(ini.main) else [],95 all_sections = [96 section_lines(k, ini.sections[k])97 for k in std.objectFields(ini.sections)98 ];99 std.join('\n', main_body + std.flattenArrays(all_sections) + ['']),100101 manifestToml(value):: std.manifestTomlEx(value, ' '),102103 manifestTomlEx(value, indent)::104 local105 escapeStringToml = std.escapeStringJson,106 escapeKeyToml(key) =107 local bare_allowed = std.set(std.stringChars('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-'));108 if std.setUnion(std.set(std.stringChars(key)), bare_allowed) == bare_allowed then key else escapeStringToml(key),109 isTableArray(v) = std.isArray(v) && std.length(v) > 0 && std.foldl(function(a, b) a && std.isObject(b), v, true),110 isSection(v) = std.isObject(v) || isTableArray(v),111 renderValue(v, indexedPath, inline, cindent) =112 if v == true then113 'true'114 else if v == false then115 'false'116 else if v == null then117 error 'Tried to manifest "null" at ' + indexedPath118 else if std.isNumber(v) then119 '' + v120 else if std.isString(v) then121 escapeStringToml(v)122 else if std.isFunction(v) then123 error 'Tried to manifest function at ' + indexedPath124 else if std.isArray(v) then125 if std.length(v) == 0 then126 '[]'127 else128 local range = std.range(0, std.length(v) - 1);129 local new_indent = if inline then '' else cindent + indent;130 local separator = if inline then ' ' else '\n';131 local lines = ['[' + separator]132 + std.join([',' + separator],133 [134 [new_indent + renderValue(v[i], indexedPath + [i], true, '')]135 for i in range136 ])137 + [separator + (if inline then '' else cindent) + ']'];138 std.join('', lines)139 else if std.isObject(v) then140 local lines = ['{ ']141 + std.join([', '],142 [143 [escapeKeyToml(k) + ' = ' + renderValue(v[k], indexedPath + [k], true, '')]144 for k in std.objectFields(v)145 ])146 + [' }'];147 std.join('', lines),148 renderTableInternal(v, path, indexedPath, cindent) =149 local kvp = std.flattenArrays([150 [cindent + escapeKeyToml(k) + ' = ' + renderValue(v[k], indexedPath + [k], false, cindent)]151 for k in std.objectFields(v)152 if !isSection(v[k])153 ]);154 local sections = [std.join('\n', kvp)] + [155 (156 if std.isObject(v[k]) then157 renderTable(v[k], path + [k], indexedPath + [k], cindent)158 else159 renderTableArray(v[k], path + [k], indexedPath + [k], cindent)160 )161 for k in std.objectFields(v)162 if isSection(v[k])163 ];164 std.join('\n\n', sections),165 renderTable(v, path, indexedPath, cindent) =166 cindent + '[' + std.join('.', std.map(escapeKeyToml, path)) + ']'167 + (if v == {} then '' else '\n')168 + renderTableInternal(v, path, indexedPath, cindent + indent),169 renderTableArray(v, path, indexedPath, cindent) =170 local range = std.range(0, std.length(v) - 1);171 local sections = [172 (cindent + '[[' + std.join('.', std.map(escapeKeyToml, path)) + ']]'173 + (if v[i] == {} then '' else '\n')174 + renderTableInternal(v[i], path, indexedPath + [i], cindent + indent))175 for i in range176 ];177 std.join('\n\n', sections);178 if std.isObject(value) then179 renderTableInternal(value, [], [], '')180 else181 error 'TOML body must be an object. Got ' + std.type(value),182183 escapeStringPython(str)::184 std.escapeStringJson(str),185186 escapeStringBash(str_)::187 local str = std.toString(str_);188 local trans(ch) =189 if ch == "'" then190 "'\"'\"'"191 else192 ch;193 "'%s'" % std.join('', [trans(ch) for ch in std.stringChars(str)]),194195 escapeStringDollars(str_)::196 local str = std.toString(str_);197 local trans(ch) =198 if ch == '$' then199 '$$'200 else201 ch;202 std.foldl(function(a, b) a + trans(b), std.stringChars(str), ''),203204 manifestJson(value):: std.manifestJsonEx(value, ' ') tailstrict,205206 manifestJsonMinified(value):: std.manifestJsonEx(value, '', '', ':'),207208 manifestYamlStream(value, indent_array_in_object=false, c_document_end=true)::209 if !std.isArray(value) then210 error 'manifestYamlStream only takes arrays, got ' + std.type(value)211 else212 '---\n' + std.join(213 '\n---\n', [std.manifestYamlDoc(e, indent_array_in_object) for e in value]214 ) + if c_document_end then '\n...\n' else '\n',215216217 manifestPython(v)::218 if std.isObject(v) then219 local fields = [220 '%s: %s' % [std.escapeStringPython(k), std.manifestPython(v[k])]221 for k in std.objectFields(v)222 ];223 '{%s}' % [std.join(', ', fields)]224 else if std.isArray(v) then225 '[%s]' % [std.join(', ', [std.manifestPython(v2) for v2 in v])]226 else if std.isString(v) then227 '%s' % [std.escapeStringPython(v)]228 else if std.isFunction(v) then229 error 'cannot manifest function'230 else if std.isNumber(v) then231 std.toString(v)232 else if v == true then233 'True'234 else if v == false then235 'False'236 else if v == null then237 'None',238239 manifestPythonVars(conf)::240 local vars = ['%s = %s' % [k, std.manifestPython(conf[k])] for k in std.objectFields(conf)];241 std.join('\n', vars + ['']),242243 manifestXmlJsonml(value)::244 if !std.isArray(value) then245 error 'Expected a JSONML value (an array), got %s' % std.type(value)246 else247 local aux(v) =248 if std.isString(v) then249 v250 else251 local tag = v[0];252 local has_attrs = std.length(v) > 1 && std.isObject(v[1]);253 local attrs = if has_attrs then v[1] else {};254 local children = if has_attrs then v[2:] else v[1:];255 local attrs_str =256 std.join('', [' %s="%s"' % [k, attrs[k]] for k in std.objectFields(attrs)]);257 std.deepJoin(['<', tag, attrs_str, '>', [aux(x) for x in children], '</', tag, '>']);258259 aux(value),260261 uniq(arr, keyF=id)::262 local f(a, b) =263 if std.length(a) == 0 then264 [b]265 else if keyF(a[std.length(a) - 1]) == keyF(b) then266 a267 else268 a + [b];269 std.foldl(f, arr, []),270271 set(arr, keyF=id)::272 std.uniq(std.sort(arr, keyF), keyF),273274 setMember(x, arr, keyF=id)::275 // TODO(dcunnin): Binary chop for O(log n) complexity276 std.length(std.setInter([x], arr, keyF)) > 0,277278 setUnion(a, b, keyF=id)::279 // NOTE: order matters, values in `a` win280 local aux(a, b, i, j, acc) =281 if i >= std.length(a) then282 acc + b[j:]283 else if j >= std.length(b) then284 acc + a[i:]285 else286 local ak = keyF(a[i]);287 local bk = keyF(b[j]);288 if ak == bk then289 aux(a, b, i + 1, j + 1, acc + [a[i]]) tailstrict290 else if ak < bk then291 aux(a, b, i + 1, j, acc + [a[i]]) tailstrict292 else293 aux(a, b, i, j + 1, acc + [b[j]]) tailstrict;294 aux(a, b, 0, 0, []),295296 setInter(a, b, keyF=id)::297 local aux(a, b, i, j, acc) =298 if i >= std.length(a) || j >= std.length(b) then299 acc300 else301 if keyF(a[i]) == keyF(b[j]) then302 aux(a, b, i + 1, j + 1, acc + [a[i]]) tailstrict303 else if keyF(a[i]) < keyF(b[j]) then304 aux(a, b, i + 1, j, acc) tailstrict305 else306 aux(a, b, i, j + 1, acc) tailstrict;307 aux(a, b, 0, 0, []) tailstrict,308309 setDiff(a, b, keyF=id)::310 local aux(a, b, i, j, acc) =311 if i >= std.length(a) then312 acc313 else if j >= std.length(b) then314 acc + a[i:]315 else316 if keyF(a[i]) == keyF(b[j]) then317 aux(a, b, i + 1, j + 1, acc) tailstrict318 else if keyF(a[i]) < keyF(b[j]) then319 aux(a, b, i + 1, j, acc + [a[i]]) tailstrict320 else321 aux(a, b, i, j + 1, acc) tailstrict;322 aux(a, b, 0, 0, []) tailstrict,323324 mergePatch(target, patch)::325 if std.isObject(patch) then326 local target_object =327 if std.isObject(target) then target else {};328329 local target_fields =330 if std.isObject(target_object) then std.objectFields(target_object) else [];331332 local null_fields = [k for k in std.objectFields(patch) if patch[k] == null];333 local both_fields = std.setUnion(target_fields, std.objectFields(patch));334335 {336 [k]:337 if !std.objectHas(patch, k) then338 target_object[k]339 else if !std.objectHas(target_object, k) then340 std.mergePatch(null, patch[k]) tailstrict341 else342 std.mergePatch(target_object[k], patch[k]) tailstrict343 for k in std.setDiff(both_fields, null_fields)344 }345 else346 patch,347348 get(o, f, default=null, inc_hidden=true)::349 if std.objectHasEx(o, f, inc_hidden) then o[f] else default,350351 objectFields(o)::352 std.objectFieldsEx(o, false),353354 objectFieldsAll(o)::355 std.objectFieldsEx(o, true),356357 objectHas(o, f)::358 std.objectHasEx(o, f, false),359360 objectHasAll(o, f)::361 std.objectHasEx(o, f, true),362363 objectValues(o)::364 [o[k] for k in std.objectFields(o)],365366 objectValuesAll(o)::367 [o[k] for k in std.objectFieldsAll(o)],368369 resolvePath(f, r)::370 local arr = std.split(f, '/');371 std.join('/', std.makeArray(std.length(arr) - 1, function(i) arr[i]) + [r]),372373 prune(a)::374 local isContent(b) =375 if b == null then376 false377 else if std.isArray(b) then378 std.length(b) > 0379 else if std.isObject(b) then380 std.length(b) > 0381 else382 true;383 if std.isArray(a) then384 [std.prune(x) for x in a if isContent($.prune(x))]385 else if std.isObject(a) then {386 [x]: $.prune(a[x])387 for x in std.objectFields(a)388 if isContent(std.prune(a[x]))389 } else390 a,391392 find(value, arr)::393 if !std.isArray(arr) then394 error 'find second parameter should be an array, got ' + std.type(arr)395 else396 std.filter(function(i) arr[i] == value, std.range(0, std.length(arr) - 1)),397}1{2 local std = self,3 local id = std.id,45 thisFile:: error 'std.thisFile is deprecated, to enable its support in jrsonnet - recompile it with "legacy-this-file" support.\nThis will slow down stdlib caching a bit, though',67 toString(a):: '' + a,89 lstripChars(str, chars)::10 if std.length(str) > 0 && std.member(chars, str[0]) then11 std.lstripChars(str[1:], chars)12 else13 str,1415 rstripChars(str, chars)::16 local len = std.length(str);17 if len > 0 && std.member(chars, str[len - 1]) then18 std.rstripChars(str[:len - 1], chars)19 else20 str,2122 stripChars(str, chars)::23 std.lstripChars(std.rstripChars(str, chars), chars),2425 stringChars(str)::26 std.makeArray(std.length(str), function(i) str[i]),2728 split(str, c):: std.splitLimit(str, c, -1),2930 mapWithIndex(func, arr)::31 if !std.isFunction(func) then32 error ('std.mapWithIndex first param must be function, got ' + std.type(func))33 else if !std.isArray(arr) && !std.isString(arr) then34 error ('std.mapWithIndex second param must be array, got ' + std.type(arr))35 else36 std.makeArray(std.length(arr), function(i) func(i, arr[i])),3738 mapWithKey(func, obj)::39 if !std.isFunction(func) then40 error ('std.mapWithKey first param must be function, got ' + std.type(func))41 else if !std.isObject(obj) then42 error ('std.mapWithKey second param must be object, got ' + std.type(obj))43 else44 { [k]: func(k, obj[k]) for k in std.objectFields(obj) },4546 lines(arr)::47 std.join('\n', arr + ['']),4849 deepJoin(arr)::50 if std.isString(arr) then51 arr52 else if std.isArray(arr) then53 std.join('', [std.deepJoin(x) for x in arr])54 else55 error 'Expected string or array, got %s' % std.type(arr),5657 filterMap(filter_func, map_func, arr)::58 if !std.isFunction(filter_func) then59 error ('std.filterMap first param must be function, got ' + std.type(filter_func))60 else if !std.isFunction(map_func) then61 error ('std.filterMap second param must be function, got ' + std.type(map_func))62 else if !std.isArray(arr) then63 error ('std.filterMap third param must be array, got ' + std.type(arr))64 else65 std.map(map_func, std.filter(filter_func, arr)),6667 assertEqual(a, b)::68 if a == b then69 true70 else71 error 'Assertion failed. ' + a + ' != ' + b,7273 clamp(x, minVal, maxVal)::74 if x < minVal then minVal75 else if x > maxVal then maxVal76 else x,7778 flattenArrays(arrs)::79 std.foldl(function(a, b) a + b, arrs, []),8081 manifestIni(ini)::82 local body_lines(body) =83 std.join([], [84 local value_or_values = body[k];85 if std.isArray(value_or_values) then86 ['%s = %s' % [k, value] for value in value_or_values]87 else88 ['%s = %s' % [k, value_or_values]]8990 for k in std.objectFields(body)91 ]);9293 local section_lines(sname, sbody) = ['[%s]' % [sname]] + body_lines(sbody),94 main_body = if std.objectHas(ini, 'main') then body_lines(ini.main) else [],95 all_sections = [96 section_lines(k, ini.sections[k])97 for k in std.objectFields(ini.sections)98 ];99 std.join('\n', main_body + std.flattenArrays(all_sections) + ['']),100101 manifestToml(value):: std.manifestTomlEx(value, ' '),102103 escapeStringPython(str)::104 std.escapeStringJson(str),105106 escapeStringBash(str_)::107 local str = std.toString(str_);108 local trans(ch) =109 if ch == "'" then110 "'\"'\"'"111 else112 ch;113 "'%s'" % std.join('', [trans(ch) for ch in std.stringChars(str)]),114115 escapeStringDollars(str_)::116 local str = std.toString(str_);117 local trans(ch) =118 if ch == '$' then119 '$$'120 else121 ch;122 std.foldl(function(a, b) a + trans(b), std.stringChars(str), ''),123124 manifestJson(value):: std.manifestJsonEx(value, ' ') tailstrict,125126 manifestJsonMinified(value):: std.manifestJsonEx(value, '', '', ':'),127128 manifestYamlStream(value, indent_array_in_object=false, c_document_end=true)::129 if !std.isArray(value) then130 error 'manifestYamlStream only takes arrays, got ' + std.type(value)131 else132 '---\n' + std.join(133 '\n---\n', [std.manifestYamlDoc(e, indent_array_in_object) for e in value]134 ) + if c_document_end then '\n...\n' else '\n',135136137 manifestPython(v)::138 if std.isObject(v) then139 local fields = [140 '%s: %s' % [std.escapeStringPython(k), std.manifestPython(v[k])]141 for k in std.objectFields(v)142 ];143 '{%s}' % [std.join(', ', fields)]144 else if std.isArray(v) then145 '[%s]' % [std.join(', ', [std.manifestPython(v2) for v2 in v])]146 else if std.isString(v) then147 '%s' % [std.escapeStringPython(v)]148 else if std.isFunction(v) then149 error 'cannot manifest function'150 else if std.isNumber(v) then151 std.toString(v)152 else if v == true then153 'True'154 else if v == false then155 'False'156 else if v == null then157 'None',158159 manifestPythonVars(conf)::160 local vars = ['%s = %s' % [k, std.manifestPython(conf[k])] for k in std.objectFields(conf)];161 std.join('\n', vars + ['']),162163 manifestXmlJsonml(value)::164 if !std.isArray(value) then165 error 'Expected a JSONML value (an array), got %s' % std.type(value)166 else167 local aux(v) =168 if std.isString(v) then169 v170 else171 local tag = v[0];172 local has_attrs = std.length(v) > 1 && std.isObject(v[1]);173 local attrs = if has_attrs then v[1] else {};174 local children = if has_attrs then v[2:] else v[1:];175 local attrs_str =176 std.join('', [' %s="%s"' % [k, attrs[k]] for k in std.objectFields(attrs)]);177 std.deepJoin(['<', tag, attrs_str, '>', [aux(x) for x in children], '</', tag, '>']);178179 aux(value),180181 uniq(arr, keyF=id)::182 local f(a, b) =183 if std.length(a) == 0 then184 [b]185 else if keyF(a[std.length(a) - 1]) == keyF(b) then186 a187 else188 a + [b];189 std.foldl(f, arr, []),190191 set(arr, keyF=id)::192 std.uniq(std.sort(arr, keyF), keyF),193194 setMember(x, arr, keyF=id)::195 // TODO(dcunnin): Binary chop for O(log n) complexity196 std.length(std.setInter([x], arr, keyF)) > 0,197198 setUnion(a, b, keyF=id)::199 // NOTE: order matters, values in `a` win200 local aux(a, b, i, j, acc) =201 if i >= std.length(a) then202 acc + b[j:]203 else if j >= std.length(b) then204 acc + a[i:]205 else206 local ak = keyF(a[i]);207 local bk = keyF(b[j]);208 if ak == bk then209 aux(a, b, i + 1, j + 1, acc + [a[i]]) tailstrict210 else if ak < bk then211 aux(a, b, i + 1, j, acc + [a[i]]) tailstrict212 else213 aux(a, b, i, j + 1, acc + [b[j]]) tailstrict;214 aux(a, b, 0, 0, []),215216 setInter(a, b, keyF=id)::217 local aux(a, b, i, j, acc) =218 if i >= std.length(a) || j >= std.length(b) then219 acc220 else221 if keyF(a[i]) == keyF(b[j]) then222 aux(a, b, i + 1, j + 1, acc + [a[i]]) tailstrict223 else if keyF(a[i]) < keyF(b[j]) then224 aux(a, b, i + 1, j, acc) tailstrict225 else226 aux(a, b, i, j + 1, acc) tailstrict;227 aux(a, b, 0, 0, []) tailstrict,228229 setDiff(a, b, keyF=id)::230 local aux(a, b, i, j, acc) =231 if i >= std.length(a) then232 acc233 else if j >= std.length(b) then234 acc + a[i:]235 else236 if keyF(a[i]) == keyF(b[j]) then237 aux(a, b, i + 1, j + 1, acc) tailstrict238 else if keyF(a[i]) < keyF(b[j]) then239 aux(a, b, i + 1, j, acc + [a[i]]) tailstrict240 else241 aux(a, b, i, j + 1, acc) tailstrict;242 aux(a, b, 0, 0, []) tailstrict,243244 mergePatch(target, patch)::245 if std.isObject(patch) then246 local target_object =247 if std.isObject(target) then target else {};248249 local target_fields =250 if std.isObject(target_object) then std.objectFields(target_object) else [];251252 local null_fields = [k for k in std.objectFields(patch) if patch[k] == null];253 local both_fields = std.setUnion(target_fields, std.objectFields(patch));254255 {256 [k]:257 if !std.objectHas(patch, k) then258 target_object[k]259 else if !std.objectHas(target_object, k) then260 std.mergePatch(null, patch[k]) tailstrict261 else262 std.mergePatch(target_object[k], patch[k]) tailstrict263 for k in std.setDiff(both_fields, null_fields)264 }265 else266 patch,267268 get(o, f, default=null, inc_hidden=true)::269 if std.objectHasEx(o, f, inc_hidden) then o[f] else default,270271 objectFields(o)::272 std.objectFieldsEx(o, false),273274 objectFieldsAll(o)::275 std.objectFieldsEx(o, true),276277 objectHas(o, f)::278 std.objectHasEx(o, f, false),279280 objectHasAll(o, f)::281 std.objectHasEx(o, f, true),282283 objectValues(o)::284 [o[k] for k in std.objectFields(o)],285286 objectValuesAll(o)::287 [o[k] for k in std.objectFieldsAll(o)],288289 resolvePath(f, r)::290 local arr = std.split(f, '/');291 std.join('/', std.makeArray(std.length(arr) - 1, function(i) arr[i]) + [r]),292293 prune(a)::294 local isContent(b) =295 if b == null then296 false297 else if std.isArray(b) then298 std.length(b) > 0299 else if std.isObject(b) then300 std.length(b) > 0301 else302 true;303 if std.isArray(a) then304 [std.prune(x) for x in a if isContent($.prune(x))]305 else if std.isObject(a) then {306 [x]: $.prune(a[x])307 for x in std.objectFields(a)308 if isContent(std.prune(a[x]))309 } else310 a,311312 find(value, arr)::313 if !std.isArray(arr) then314 error 'find second parameter should be an array, got ' + std.type(arr)315 else316 std.filter(function(i) arr[i] == value, std.range(0, std.length(arr) - 1)),317}