difftreelog
feat quote_keys option for Yaml
in: master
5 files changed
Cargo.lockdiffbeforeafterboth--- a/Cargo.lock
+++ b/Cargo.lock
@@ -115,6 +115,12 @@
]
[[package]]
+name = "dtoa"
+version = "0.4.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0"
+
+[[package]]
name = "gcmodule"
version = "0.3.3"
source = "git+https://github.com/CertainLach/gcmodule?branch=jrsonnet#f72713c24c2b1bf5a78f1d01bee5a0f52bc2a094"
@@ -216,6 +222,7 @@
"rustc-hash",
"serde",
"serde_json",
+ "serde_yaml",
"thiserror",
]
@@ -273,6 +280,12 @@
checksum = "8521a1b57e76b1ec69af7599e75e38e7b7fad6610f037db8c79b127201b5d119"
[[package]]
+name = "linked-hash-map"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
+
+[[package]]
name = "lock_api"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -467,6 +480,17 @@
]
[[package]]
+name = "serde_yaml"
+version = "0.8.21"
+source = "git+https://github.com/CertainLach/serde-yaml?branch=feature/old-octals-quirk#4bf0e325243539fdeb419e8d727ed1c161cbe445"
+dependencies = [
+ "dtoa",
+ "indexmap",
+ "serde",
+ "yaml-rust",
+]
+
+[[package]]
name = "smallvec"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -601,6 +625,15 @@
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
+name = "yaml-rust"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
+dependencies = [
+ "linked-hash-map",
+]
+
+[[package]]
name = "yansi-term"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
crates/jrsonnet-evaluator/src/builtin/manifest.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/builtin/manifest.rs
+++ b/crates/jrsonnet-evaluator/src/builtin/manifest.rs
@@ -173,6 +173,62 @@
/// ## <- this
/// ```
pub arr_element_padding: &'s str,
+ /// Should yaml keys appear unescaped, when possible
+ /// ```yaml
+ /// "safe_key": 1
+ /// # vs
+ /// safe_key: 1
+ /// ```
+ pub quote_keys: bool,
+}
+
+/// From https://github.com/chyh1990/yaml-rust/blob/da52a68615f2ecdd6b7e4567019f280c433c1521/src/emitter.rs#L289
+/// With added date check
+fn yaml_needs_quotes(string: &str) -> bool {
+ fn need_quotes_spaces(string: &str) -> bool {
+ string.starts_with(' ') || string.ends_with(' ')
+ }
+
+ string == ""
+ || need_quotes_spaces(string)
+ || string.starts_with(|character: char| match character {
+ '&' | '*' | '?' | '|' | '-' | '<' | '>' | '=' | '!' | '%' | '@' => true,
+ _ => false,
+ }) || string.contains(|character: char| match character {
+ ':'
+ | '{'
+ | '}'
+ | '['
+ | ']'
+ | ','
+ | '#'
+ | '`'
+ | '\"'
+ | '\''
+ | '\\'
+ | '\0'..='\x06'
+ | '\t'
+ | '\n'
+ | '\r'
+ | '\x0e'..='\x1a'
+ | '\x1c'..='\x1f' => true,
+ _ => false,
+ }) || [
+ // http://yaml.org/type/bool.html
+ // Note: 'y', 'Y', 'n', 'N', is not quoted deliberately, as in libyaml. PyYAML also parse
+ // them as string, not booleans, although it is violating the YAML 1.1 specification.
+ // See https://github.com/dtolnay/serde-yaml/pull/83#discussion_r152628088.
+ "yes", "Yes", "YES", "no", "No", "NO", "True", "TRUE", "true", "False", "FALSE", "false",
+ "on", "On", "ON", "off", "Off", "OFF", // http://yaml.org/type/null.html
+ "null", "Null", "NULL", "~",
+ ]
+ .contains(&string)
+ || (string.chars().all(|c| matches!(c, '0'..='9' | '-'))
+ && string.chars().filter(|c| *c == '-').count() == 2)
+ || string.starts_with('.')
+ || string.starts_with("0x")
+ || string.parse::<i64>().is_ok()
+ || string.parse::<f64>().is_ok()
}
pub fn manifest_yaml_ex(val: &Val, options: &ManifestYamlOptions<'_>) -> Result<String> {
@@ -206,8 +262,10 @@
buf.push_str(options.padding);
buf.push_str(line);
}
+ } else if !options.quote_keys && !yaml_needs_quotes(&s) {
+ buf.push_str(&s);
} else {
- escape_string_json_buf(s, buf)
+ escape_string_json_buf(s, buf);
}
}
Val::Num(n) => write!(buf, "{}", *n).unwrap(),
@@ -253,7 +311,11 @@
buf.push('\n');
buf.push_str(cur_padding);
}
- escape_string_json_buf(key, buf);
+ if !options.quote_keys && !yaml_needs_quotes(&key) {
+ buf.push_str(&key);
+ } else {
+ escape_string_json_buf(key, buf);
+ }
buf.push(':');
let prev_len = cur_padding.len();
let item = o.get(key.clone())?.expect("field exists");
crates/jrsonnet-evaluator/src/builtin/mod.rsdiffbeforeafterboth1use crate::{2 builtin::manifest::{manifest_yaml_ex, ManifestYamlOptions},3 equals,4 error::{Error::*, Result},5 operator::evaluate_mod_op,6 parse_args, primitive_equals, push_frame, throw, with_state, ArrValue, Context, FuncVal,7 IndexableVal, LazyVal, Val,8};9use format::{format_arr, format_obj};10use gcmodule::Cc;11use jrsonnet_interner::IStr;12use jrsonnet_parser::{ArgsDesc, ExprLocation};13use jrsonnet_types::ty;14use serde::Deserialize;15use serde_yaml::DeserializingQuirks;16use std::{collections::HashMap, convert::TryFrom, path::PathBuf, rc::Rc};1718pub mod stdlib;19pub use stdlib::*;2021use self::manifest::{escape_string_json, manifest_json_ex, ManifestJsonOptions, ManifestType};2223pub mod format;24pub mod manifest;25pub mod sort;2627pub fn std_format(str: IStr, vals: Val) -> Result<Val> {28 push_frame(29 &ExprLocation(Rc::from(PathBuf::from("std.jsonnet")), 0, 0),30 || format!("std.format of {}", str),31 || {32 Ok(match vals {33 Val::Arr(vals) => Val::Str(format_arr(&str, &vals.evaluated()?)?.into()),34 Val::Obj(obj) => Val::Str(format_obj(&str, &obj)?.into()),35 o => Val::Str(format_arr(&str, &[o])?.into()),36 })37 },38 )39}4041pub fn std_slice(42 indexable: IndexableVal,43 index: Option<usize>,44 end: Option<usize>,45 step: Option<usize>,46) -> Result<Val> {47 let index = index.unwrap_or(0);48 let end = end.unwrap_or_else(|| match &indexable {49 IndexableVal::Str(_) => usize::MAX,50 IndexableVal::Arr(v) => v.len(),51 });52 let step = step.unwrap_or(1);53 match &indexable {54 IndexableVal::Str(s) => Ok(Val::Str(55 (s.chars()56 .skip(index)57 .take(end - index)58 .step_by(step)59 .collect::<String>())60 .into(),61 )),62 IndexableVal::Arr(arr) => Ok(Val::Arr(63 (arr.iter()64 .skip(index)65 .take(end - index)66 .step_by(step)67 .collect::<Result<Vec<Val>>>()?)68 .into(),69 )),70 }71}7273type Builtin = fn(context: Context, loc: &ExprLocation, args: &ArgsDesc) -> Result<Val>;7475type BuiltinsType = HashMap<Box<str>, Builtin>;7677thread_local! {78 static BUILTINS: BuiltinsType = {79 [80 ("length".into(), builtin_length as Builtin),81 ("type".into(), builtin_type),82 ("makeArray".into(), builtin_make_array),83 ("codepoint".into(), builtin_codepoint),84 ("objectFieldsEx".into(), builtin_object_fields_ex),85 ("objectHasEx".into(), builtin_object_has_ex),86 ("slice".into(), builtin_slice),87 ("substr".into(), builtin_substr),88 ("primitiveEquals".into(), builtin_primitive_equals),89 ("equals".into(), builtin_equals),90 ("modulo".into(), builtin_modulo),91 ("mod".into(), builtin_mod),92 ("floor".into(), builtin_floor),93 ("ceil".into(), builtin_ceil),94 ("log".into(), builtin_log),95 ("pow".into(), builtin_pow),96 ("sqrt".into(), builtin_sqrt),97 ("sin".into(), builtin_sin),98 ("cos".into(), builtin_cos),99 ("tan".into(), builtin_tan),100 ("asin".into(), builtin_asin),101 ("acos".into(), builtin_acos),102 ("atan".into(), builtin_atan),103 ("exp".into(), builtin_exp),104 ("mantissa".into(), builtin_mantissa),105 ("exponent".into(), builtin_exponent),106 ("extVar".into(), builtin_ext_var),107 ("native".into(), builtin_native),108 ("filter".into(), builtin_filter),109 ("map".into(), builtin_map),110 ("flatMap".into(), builtin_flatmap),111 ("foldl".into(), builtin_foldl),112 ("foldr".into(), builtin_foldr),113 ("sortImpl".into(), builtin_sort_impl),114 ("format".into(), builtin_format),115 ("range".into(), builtin_range),116 ("char".into(), builtin_char),117 ("encodeUTF8".into(), builtin_encode_utf8),118 ("decodeUTF8".into(), builtin_decode_utf8),119 ("md5".into(), builtin_md5),120 ("base64".into(), builtin_base64),121 ("base64DecodeBytes".into(), builtin_base64_decode_bytes),122 ("base64Decode".into(), builtin_base64_decode),123 ("trace".into(), builtin_trace),124 ("join".into(), builtin_join),125 ("escapeStringJson".into(), builtin_escape_string_json),126 ("manifestJsonEx".into(), builtin_manifest_json_ex),127 ("manifestYamlDocImpl".into(), builtin_manifest_yaml_doc),128 ("reverse".into(), builtin_reverse),129 ("id".into(), builtin_id),130 ("strReplace".into(), builtin_str_replace),131 ("splitLimit".into(), builtin_splitlimit),132 ("parseJson".into(), builtin_parse_json),133 ("parseYaml".into(), builtin_parse_yaml),134 ("asciiUpper".into(), builtin_ascii_upper),135 ("asciiLower".into(), builtin_ascii_lower),136 ("member".into(), builtin_member),137 ("count".into(), builtin_count),138 ].iter().cloned().collect()139 };140}141142fn builtin_length(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {143 parse_args!(context, "length", args, 1, [144 0, x: ty!((string | object | array));145 ], {146 Ok(match x {147 Val::Str(n) => Val::Num(n.chars().count() as f64),148 Val::Arr(a) => Val::Num(a.len() as f64),149 Val::Obj(o) => Val::Num(150 o.fields_visibility()151 .into_iter()152 .filter(|(_k, v)| *v)153 .count() as f64,154 ),155 _ => unreachable!(),156 })157 })158}159160fn builtin_type(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {161 parse_args!(context, "type", args, 1, [162 0, x: ty!(any);163 ], {164 Ok(Val::Str(x.value_type().name().into()))165 })166}167168fn builtin_make_array(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {169 parse_args!(context, "makeArray", args, 2, [170 0, sz: ty!(BoundedNumber<(Some(0.0)), (None)>) => Val::Num;171 1, func: ty!(function) => Val::Func;172 ], {173 let mut out = Vec::with_capacity(sz as usize);174 for i in 0..sz as usize {175 out.push(LazyVal::new_resolved(func.evaluate_values(176 context.clone(),177 &[Val::Num(i as f64)]178 )?))179 }180 Ok(Val::Arr(out.into()))181 })182}183184fn builtin_codepoint(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {185 parse_args!(context, "codepoint", args, 1, [186 0, str: ty!(char) => Val::Str;187 ], {188 Ok(Val::Num(str.chars().next().unwrap() as u32 as f64))189 })190}191192fn builtin_object_fields_ex(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {193 parse_args!(context, "objectFieldsEx", args, 2, [194 0, obj: ty!(object) => Val::Obj;195 1, inc_hidden: ty!(boolean) => Val::Bool;196 ], {197 let out = obj.fields_ex(inc_hidden);198 Ok(Val::Arr(out.into_iter().map(Val::Str).collect::<Vec<_>>().into()))199 })200}201202fn builtin_object_has_ex(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {203 parse_args!(context, "objectHasEx", args, 3, [204 0, obj: ty!(object) => Val::Obj;205 1, f: ty!(string) => Val::Str;206 2, inc_hidden: ty!(boolean) => Val::Bool;207 ], {208 Ok(Val::Bool(obj.has_field_ex(f, inc_hidden)))209 })210}211212fn builtin_parse_json(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {213 parse_args!(context, "parseJson", args, 1, [214 0, s: ty!(string) => Val::Str;215 ], {216 let value: serde_json::Value = serde_json::from_str(&s).map_err(|e| RuntimeError(format!("failed to parse json: {}", e).into()))?;217 Ok(Val::try_from(&value)?)218 })219}220221fn builtin_parse_yaml(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {222 parse_args!(context, "parseYaml", args, 1, [223 0, s: ty!(string) => Val::Str;224 ], {225 let value = serde_yaml::Deserializer::from_str_with_quirks(&s, DeserializingQuirks { old_octals: true });226 let mut out = vec![];227 for item in value {228 let value = serde_json::Value::deserialize(item)229 .map_err(|e| RuntimeError(format!("failed to parse yaml: {}", e).into()))?;230 let val = Val::try_from(&value)?;231 out.push(val);232 }233 if out.is_empty() {234 Ok(Val::Null)235 } else if out.len() == 1 {236 Ok(out.into_iter().next().unwrap())237 } else {238 Ok(Val::Arr(out.into()))239 }240 })241}242243fn builtin_slice(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {244 parse_args!(context, "slice", args, 4, [245 0, indexable: ty!((string | array));246 1, index: ty!((number | null));247 2, end: ty!((number | null));248 3, step: ty!((number | null));249 ], {250 std_slice(251 indexable.into_indexable()?,252 index.try_cast_nullable_num("index")?.map(|v| v as usize),253 end.try_cast_nullable_num("end")?.map(|v| v as usize),254 step.try_cast_nullable_num("step")?.map(|v| v as usize),255 )256 })257}258259fn builtin_substr(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {260 parse_args!(context, "substr", args, 3, [261 0, str: ty!(string) => Val::Str;262 1, from: ty!(BoundedNumber<(Some(0.0)), (None)>) => Val::Num;263 2, len: ty!(BoundedNumber<(Some(0.0)), (None)>) => Val::Num;264 ], {265 let out: String = str.chars().skip(from as usize).take(len as usize).collect();266 Ok(Val::Str(out.into()))267 })268}269270fn builtin_primitive_equals(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {271 parse_args!(context, "primitiveEquals", args, 2, [272 0, a: ty!(any);273 1, b: ty!(any);274 ], {275 Ok(Val::Bool(primitive_equals(&a, &b)?))276 })277}278279fn builtin_equals(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {280 parse_args!(context, "equals", args, 2, [281 0, a: ty!(any);282 1, b: ty!(any);283 ], {284 Ok(Val::Bool(equals(&a, &b)?))285 })286}287288fn builtin_modulo(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {289 parse_args!(context, "modulo", args, 2, [290 0, a: ty!(number) => Val::Num;291 1, b: ty!(number) => Val::Num;292 ], {293 Ok(Val::Num(a % b))294 })295}296297fn builtin_mod(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {298 parse_args!(context, "mod", args, 2, [299 0, a: ty!((number | string));300 1, b: ty!(any);301 ], {302 evaluate_mod_op(&a, &b)303 })304}305306fn builtin_floor(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {307 parse_args!(context, "floor", args, 1, [308 0, x: ty!(number) => Val::Num;309 ], {310 Ok(Val::Num(x.floor()))311 })312}313314fn builtin_ceil(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {315 parse_args!(context, "ceil", args, 1, [316 0, x: ty!(number) => Val::Num;317 ], {318 Ok(Val::Num(x.ceil()))319 })320}321322fn builtin_log(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {323 parse_args!(context, "log", args, 1, [324 0, n: ty!(number) => Val::Num;325 ], {326 Ok(Val::Num(n.ln()))327 })328}329330fn builtin_pow(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {331 parse_args!(context, "pow", args, 2, [332 0, x: ty!(number) => Val::Num;333 1, n: ty!(number) => Val::Num;334 ], {335 Ok(Val::Num(x.powf(n)))336 })337}338339fn builtin_sqrt(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {340 parse_args!(context, "sqrt", args, 1, [341 0, x: ty!(BoundedNumber<(Some(0.0)), (None)>) => Val::Num;342 ], {343 Ok(Val::Num(x.sqrt()))344 })345}346347fn builtin_sin(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {348 parse_args!(context, "sin", args, 1, [349 0, x: ty!(number) => Val::Num;350 ], {351 Ok(Val::Num(x.sin()))352 })353}354355fn builtin_cos(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {356 parse_args!(context, "cos", args, 1, [357 0, x: ty!(number) => Val::Num;358 ], {359 Ok(Val::Num(x.cos()))360 })361}362363fn builtin_tan(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {364 parse_args!(context, "tan", args, 1, [365 0, x: ty!(number) => Val::Num;366 ], {367 Ok(Val::Num(x.tan()))368 })369}370371fn builtin_asin(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {372 parse_args!(context, "asin", args, 1, [373 0, x: ty!(number) => Val::Num;374 ], {375 Ok(Val::Num(x.asin()))376 })377}378379fn builtin_acos(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {380 parse_args!(context, "acos", args, 1, [381 0, x: ty!(number) => Val::Num;382 ], {383 Ok(Val::Num(x.acos()))384 })385}386387fn builtin_atan(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {388 parse_args!(context, "atan", args, 1, [389 0, x: ty!(number) => Val::Num;390 ], {391 Ok(Val::Num(x.atan()))392 })393}394395fn builtin_exp(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {396 parse_args!(context, "exp", args, 1, [397 0, x: ty!(number) => Val::Num;398 ], {399 Ok(Val::Num(x.exp()))400 })401}402403fn frexp(s: f64) -> (f64, i16) {404 if 0.0 == s {405 (s, 0)406 } else {407 let lg = s.abs().log2();408 let x = (lg - lg.floor() - 1.0).exp2();409 let exp = lg.floor() + 1.0;410 (s.signum() * x, exp as i16)411 }412}413414fn builtin_mantissa(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {415 parse_args!(context, "mantissa", args, 1, [416 0, x: ty!(number) => Val::Num;417 ], {418 Ok(Val::Num(frexp(x).0))419 })420}421422fn builtin_exponent(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {423 parse_args!(context, "exponent", args, 1, [424 0, x: ty!(number) => Val::Num;425 ], {426 Ok(Val::Num(frexp(x).1.into()))427 })428}429430fn builtin_ext_var(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {431 parse_args!(context, "extVar", args, 1, [432 0, x: ty!(string) => Val::Str;433 ], {434 Ok(with_state(|s| s.settings().ext_vars.get(&x).cloned()).ok_or(UndefinedExternalVariable(x))?)435 })436}437438fn builtin_native(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {439 parse_args!(context, "native", args, 1, [440 0, x: ty!(string) => Val::Str;441 ], {442 Ok(with_state(|s| s.settings().ext_natives.get(&x).cloned()).map(|v| Val::Func(Cc::new(FuncVal::NativeExt(x.clone(), v)))).ok_or(UndefinedExternalFunction(x))?)443 })444}445446fn builtin_filter(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {447 parse_args!(context, "filter", args, 2, [448 0, func: ty!(function) => Val::Func;449 1, arr: ty!(array) => Val::Arr;450 ], {451 Ok(Val::Arr(arr.filter(|val| func452 .evaluate_values(context.clone(), &[val.clone()])?453 .try_cast_bool("filter predicate"))?))454 })455}456457fn builtin_map(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {458 parse_args!(context, "map", args, 2, [459 0, func: ty!(function) => Val::Func;460 1, arr: ty!(array) => Val::Arr;461 ], {462 Ok(Val::Arr(arr.map(|val| func463 .evaluate_values(context.clone(), &[val]))?))464 })465}466467fn builtin_flatmap(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {468 parse_args!(context, "flatMap", args, 2, [469 0, func: ty!(function) => Val::Func;470 1, arr: ty!((array | string));471 ], {472 match arr {473 Val::Str(s) => {474 let mut out = String::new();475 for c in s.chars() {476 match func.evaluate_values(context.clone(), &[Val::Str(c.to_string().into())])? {477 Val::Str(o) => out.push_str(&o),478 _ => throw!(RuntimeError("in std.join all items should be strings".into())),479 };480 }481 Ok(Val::Str(out.into()))482 },483 Val::Arr(a) => {484 let mut out = Vec::new();485 for el in a.iter() {486 let el = el?;487 match func.evaluate_values(context.clone(), &[el])? {488 Val::Arr(o) => for oe in o.iter() {489 out.push(oe?)490 },491 _ => throw!(RuntimeError("in std.join all items should be arrays".into())),492 };493 }494 Ok(Val::Arr(out.into()))495 },496 _ => unreachable!(),497 }498 })499}500501fn builtin_foldl(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {502 parse_args!(context, "foldl", args, 3, [503 0, func: ty!(function) => Val::Func;504 1, arr: ty!(array) => Val::Arr;505 2, init: ty!(any);506 ], {507 let mut acc = init;508 for i in arr.iter() {509 acc = func.evaluate_values(context.clone(), &[acc, i?])?;510 }511 Ok(acc)512 })513}514515fn builtin_foldr(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {516 parse_args!(context, "foldr", args, 3, [517 0, func: ty!(function) => Val::Func;518 1, arr: ty!(array) => Val::Arr;519 2, init: ty!(any);520 ], {521 let mut acc = init;522 for i in arr.iter().rev() {523 acc = func.evaluate_values(context.clone(), &[i?, acc])?;524 }525 Ok(acc)526 })527}528529#[allow(non_snake_case)]530fn builtin_sort_impl(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {531 parse_args!(context, "sort", args, 2, [532 0, arr: ty!(array) => Val::Arr;533 1, keyF: ty!(function) => Val::Func;534 ], {535 if arr.len() <= 1 {536 return Ok(Val::Arr(arr))537 }538 Ok(Val::Arr(ArrValue::Eager(sort::sort(context, arr.evaluated()?, &keyF)?)))539 })540}541542fn builtin_format(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {543 parse_args!(context, "format", args, 2, [544 0, str: ty!(string) => Val::Str;545 1, vals: ty!(any)546 ], {547 std_format(str, vals)548 })549}550551fn builtin_range(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {552 parse_args!(context, "range", args, 2, [553 0, from: ty!(number) => Val::Num;554 1, to: ty!(number) => Val::Num;555 ], {556 if to < from {557 return Ok(Val::Arr(ArrValue::new_eager()))558 }559 let mut out = Vec::with_capacity((1+to as usize-from as usize).max(0));560 for i in from as usize..=to as usize {561 out.push(Val::Num(i as f64));562 }563 Ok(Val::Arr(out.into()))564 })565}566567fn builtin_char(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {568 parse_args!(context, "char", args, 1, [569 0, n: ty!(number) => Val::Num;570 ], {571 let mut out = String::new();572 out.push(std::char::from_u32(n as u32).ok_or_else(||573 InvalidUnicodeCodepointGot(n as u32)574 )?);575 Ok(Val::Str(out.into()))576 })577}578579fn builtin_encode_utf8(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {580 parse_args!(context, "encodeUTF8", args, 1, [581 0, str: ty!(string) => Val::Str;582 ], {583 Ok(Val::Arr((str.bytes().map(|b| Val::Num(b as f64)).collect::<Vec<Val>>()).into()))584 })585}586587fn builtin_decode_utf8(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {588 parse_args!(context, "decodeUTF8", args, 1, [589 0, arr: ty!((Array<ubyte>)) => Val::Arr;590 ], {591 let data: Result<Vec<u8>> = arr.iter().map(|v| v.map(|v| match v{592 Val::Num(n) => n as u8,593 _ => unreachable!(),594 })).collect();595 let data = data?;596 Ok(Val::Str(String::from_utf8(data).map_err(|_| RuntimeError("bad utf8".into()))?.into()))597 })598}599600fn builtin_md5(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {601 parse_args!(context, "md5", args, 1, [602 0, str: ty!(string) => Val::Str;603 ], {604 Ok(Val::Str(format!("{:x}", md5::compute(&str.as_bytes())).into()))605 })606}607608fn builtin_trace(context: Context, loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {609 parse_args!(context, "trace", args, 2, [610 0, str: ty!(string) => Val::Str;611 1, rest: ty!(any);612 ], {613 eprint!("TRACE:");614 with_state(|s|{615 let locs = s.map_source_locations(&loc.0, &[loc.1]);616 eprint!(" {}:{}", loc.0.file_name().unwrap().to_str().unwrap(), locs[0].line);617 });618 eprintln!(" {}", str);619 Ok(rest)620 })621}622623fn builtin_base64(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {624 parse_args!(context, "base64", args, 1, [625 0, input: ty!((string | (Array<number>)));626 ], {627 Ok(Val::Str(match input {628 Val::Str(s) => {629 base64::encode(s.bytes().collect::<Vec<_>>()).into()630 },631 Val::Arr(a) => {632 base64::encode(a.iter().map(|v| {633 Ok(v?.unwrap_num()? as u8)634 }).collect::<Result<Vec<_>>>()?).into()635 },636 _ => unreachable!()637 }))638 })639}640641fn builtin_base64_decode_bytes(642 context: Context,643 _loc: &ExprLocation,644 args: &ArgsDesc,645) -> Result<Val> {646 parse_args!(context, "base64DecodeBytes", args, 1, [647 0, input: ty!(string) => Val::Str;648 ], {649 Ok(Val::Arr(650 base64::decode(&input.as_bytes())651 .map_err(|_| RuntimeError("bad base64".into()))?652 .iter()653 .map(|v| Val::Num(*v as f64)).collect::<Vec<_>>().into()654 ))655 })656}657658fn builtin_base64_decode(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {659 parse_args!(context, "base64Decode", args, 1, [660 0, input: ty!(string) => Val::Str;661 ], {662 Ok(Val::Str(663 String::from_utf8(base64::decode(&input.as_bytes())664 .map_err(|_| RuntimeError("bad base64".into()))?)665 .map_err(|_| RuntimeError("bad utf8".into()))?.into()666 ))667 })668}669670fn builtin_join(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {671 parse_args!(context, "join", args, 2, [672 0, sep: ty!((string | array));673 1, arr: ty!(array) => Val::Arr;674 ], {675 Ok(match sep {676 Val::Arr(joiner_items) => {677 let mut out = Vec::new();678679 let mut first = true;680 for item in arr.iter() {681 let item = item?.clone();682 if let Val::Arr(items) = item {683 if !first {684 out.reserve(joiner_items.len());685 // TODO: extend686 for item in joiner_items.iter() {687 out.push(item?);688 }689 }690 first = false;691 out.reserve(items.len());692 // TODO: extend693 for item in items.iter() {694 out.push(item?);695 }696 } else {697 throw!(RuntimeError("in std.join all items should be arrays".into()));698 }699 }700701 Val::Arr(out.into())702 },703 Val::Str(sep) => {704 let mut out = String::new();705706 let mut first = true;707 for item in arr.iter() {708 let item = item?.clone();709 if let Val::Str(item) = item {710 if !first {711 out += &sep;712 }713 first = false;714 out += &item;715 } else {716 throw!(RuntimeError("in std.join all items should be strings".into()));717 }718 }719720 Val::Str(out.into())721 },722 _ => unreachable!()723 })724 })725}726727fn builtin_escape_string_json(728 context: Context,729 _loc: &ExprLocation,730 args: &ArgsDesc,731) -> Result<Val> {732 parse_args!(context, "escapeStringJson", args, 1, [733 0, str_: ty!(string) => Val::Str;734 ], {735 Ok(Val::Str(escape_string_json(&str_).into()))736 })737}738739fn builtin_manifest_json_ex(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {740 parse_args!(context, "manifestJsonEx", args, 2, [741 0, value: ty!(any);742 1, indent: ty!(string) => Val::Str;743 ], {744 Ok(Val::Str(manifest_json_ex(&value, &ManifestJsonOptions {745 padding: &indent,746 mtype: ManifestType::Std,747 })?.into()))748 })749}750751fn builtin_manifest_yaml_doc(752 context: Context,753 _loc: &ExprLocation,754 args: &ArgsDesc,755) -> Result<Val> {756 parse_args!(context, "manifestYamlDoc", args, 2, [757 0, value: ty!(any);758 1, indent_array_in_object: ty!(boolean) => Val::Bool;759 ], {760 Ok(Val::Str(manifest_yaml_ex(&value, &ManifestYamlOptions {761 padding: " ",762 arr_element_padding: if indent_array_in_object { " " } else { "" },763 })?.into()))764 })765}766767fn builtin_reverse(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {768 parse_args!(context, "reverse", args, 1, [769 0, value: ty!(array) => Val::Arr;770 ], {771 Ok(Val::Arr(value.reversed()))772 })773}774775fn builtin_id(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {776 parse_args!(context, "id", args, 1, [777 0, v: ty!(any);778 ], {779 Ok(v)780 })781}782783fn builtin_str_replace(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {784 parse_args!(context, "strReplace", args, 3, [785 0, str: ty!(string) => Val::Str;786 1, from: ty!(string) => Val::Str;787 2, to: ty!(string) => Val::Str;788 ], {789 Ok(Val::Str(str.replace(&from as &str, &to as &str).into()))790 })791}792793fn builtin_splitlimit(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {794 parse_args!(context, "splitLimit", args, 3, [795 0, str: ty!(string) => Val::Str;796 1, c: ty!(char) => Val::Str;797 2, maxsplits: ty!(number) => Val::Num;798 ], {799 let maxsplits = maxsplits as isize;800 let c = c.chars().next().unwrap();801802 let out: Vec<Val> = if maxsplits == -1 {803 str.split(c).map(|s| Val::Str(s.into())).collect()804 } else {805 str.splitn(maxsplits as usize + 1, c).map(|s| Val::Str(s.into())).collect()806 };807808 Ok(Val::Arr(out.into()))809 })810}811812fn builtin_ascii_upper(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {813 parse_args!(context, "asciiUpper", args, 1, [814 0, str: ty!(string) => Val::Str;815 ], {816 Ok(Val::Str(str.to_ascii_uppercase().into()))817 })818}819820fn builtin_ascii_lower(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {821 parse_args!(context, "asciiLower", args, 1, [822 0, str: ty!(string) => Val::Str;823 ], {824 Ok(Val::Str(str.to_ascii_lowercase().into()))825 })826}827828fn builtin_member(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {829 parse_args!(context, "member", args, 2, [830 0, arr: ty!((array | string));831 1, x: ty!(any);832 ], {833 match arr {834 Val::Str(s) => {835 let x = x.try_cast_str("x should be string")?;836 Ok(Val::Bool(!x.is_empty() && s.contains(&*x)))837 }838 Val::Arr(a) => {839 for item in a.iter() {840 let item = item?;841 if equals(&item, &x)? {842 return Ok(Val::Bool(true));843 }844 }845 Ok(Val::Bool(false))846 }847 _ => unreachable!(),848 }849 })850}851852fn builtin_count(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {853 parse_args!(context, "count", args, 2, [854 0, arr: ty!(array) => Val::Arr;855 1, x: ty!(any);856 ], {857 let mut count = 0;858 for item in arr.iter() {859 let item = item?;860 if equals(&item, &x)? {861 count += 1;862 }863 }864 Ok(Val::Num(count as f64))865 })866}867868pub fn call_builtin(869 context: Context,870 loc: &ExprLocation,871 name: &str,872 args: &ArgsDesc,873) -> Result<Val> {874 BUILTINS875 .with(|builtins| builtins.get(name).copied())876 .ok_or_else(|| IntrinsicNotFound(name.into()))?(context, loc, args)877}1use crate::{2 builtin::manifest::{manifest_yaml_ex, ManifestYamlOptions},3 equals,4 error::{Error::*, Result},5 operator::evaluate_mod_op,6 parse_args, primitive_equals, push_frame, throw, with_state, ArrValue, Context, FuncVal,7 IndexableVal, LazyVal, Val,8};9use format::{format_arr, format_obj};10use gcmodule::Cc;11use jrsonnet_interner::IStr;12use jrsonnet_parser::{ArgsDesc, ExprLocation};13use jrsonnet_types::ty;14use serde::Deserialize;15use serde_yaml::DeserializingQuirks;16use std::{collections::HashMap, convert::TryFrom, path::PathBuf, rc::Rc};1718pub mod stdlib;19pub use stdlib::*;2021use self::manifest::{escape_string_json, manifest_json_ex, ManifestJsonOptions, ManifestType};2223pub mod format;24pub mod manifest;25pub mod sort;2627pub fn std_format(str: IStr, vals: Val) -> Result<Val> {28 push_frame(29 &ExprLocation(Rc::from(PathBuf::from("std.jsonnet")), 0, 0),30 || format!("std.format of {}", str),31 || {32 Ok(match vals {33 Val::Arr(vals) => Val::Str(format_arr(&str, &vals.evaluated()?)?.into()),34 Val::Obj(obj) => Val::Str(format_obj(&str, &obj)?.into()),35 o => Val::Str(format_arr(&str, &[o])?.into()),36 })37 },38 )39}4041pub fn std_slice(42 indexable: IndexableVal,43 index: Option<usize>,44 end: Option<usize>,45 step: Option<usize>,46) -> Result<Val> {47 let index = index.unwrap_or(0);48 let end = end.unwrap_or_else(|| match &indexable {49 IndexableVal::Str(_) => usize::MAX,50 IndexableVal::Arr(v) => v.len(),51 });52 let step = step.unwrap_or(1);53 match &indexable {54 IndexableVal::Str(s) => Ok(Val::Str(55 (s.chars()56 .skip(index)57 .take(end - index)58 .step_by(step)59 .collect::<String>())60 .into(),61 )),62 IndexableVal::Arr(arr) => Ok(Val::Arr(63 (arr.iter()64 .skip(index)65 .take(end - index)66 .step_by(step)67 .collect::<Result<Vec<Val>>>()?)68 .into(),69 )),70 }71}7273type Builtin = fn(context: Context, loc: &ExprLocation, args: &ArgsDesc) -> Result<Val>;7475type BuiltinsType = HashMap<Box<str>, Builtin>;7677thread_local! {78 static BUILTINS: BuiltinsType = {79 [80 ("length".into(), builtin_length as Builtin),81 ("type".into(), builtin_type),82 ("makeArray".into(), builtin_make_array),83 ("codepoint".into(), builtin_codepoint),84 ("objectFieldsEx".into(), builtin_object_fields_ex),85 ("objectHasEx".into(), builtin_object_has_ex),86 ("slice".into(), builtin_slice),87 ("substr".into(), builtin_substr),88 ("primitiveEquals".into(), builtin_primitive_equals),89 ("equals".into(), builtin_equals),90 ("modulo".into(), builtin_modulo),91 ("mod".into(), builtin_mod),92 ("floor".into(), builtin_floor),93 ("ceil".into(), builtin_ceil),94 ("log".into(), builtin_log),95 ("pow".into(), builtin_pow),96 ("sqrt".into(), builtin_sqrt),97 ("sin".into(), builtin_sin),98 ("cos".into(), builtin_cos),99 ("tan".into(), builtin_tan),100 ("asin".into(), builtin_asin),101 ("acos".into(), builtin_acos),102 ("atan".into(), builtin_atan),103 ("exp".into(), builtin_exp),104 ("mantissa".into(), builtin_mantissa),105 ("exponent".into(), builtin_exponent),106 ("extVar".into(), builtin_ext_var),107 ("native".into(), builtin_native),108 ("filter".into(), builtin_filter),109 ("map".into(), builtin_map),110 ("flatMap".into(), builtin_flatmap),111 ("foldl".into(), builtin_foldl),112 ("foldr".into(), builtin_foldr),113 ("sortImpl".into(), builtin_sort_impl),114 ("format".into(), builtin_format),115 ("range".into(), builtin_range),116 ("char".into(), builtin_char),117 ("encodeUTF8".into(), builtin_encode_utf8),118 ("decodeUTF8".into(), builtin_decode_utf8),119 ("md5".into(), builtin_md5),120 ("base64".into(), builtin_base64),121 ("base64DecodeBytes".into(), builtin_base64_decode_bytes),122 ("base64Decode".into(), builtin_base64_decode),123 ("trace".into(), builtin_trace),124 ("join".into(), builtin_join),125 ("escapeStringJson".into(), builtin_escape_string_json),126 ("manifestJsonEx".into(), builtin_manifest_json_ex),127 ("manifestYamlDocImpl".into(), builtin_manifest_yaml_doc),128 ("reverse".into(), builtin_reverse),129 ("id".into(), builtin_id),130 ("strReplace".into(), builtin_str_replace),131 ("splitLimit".into(), builtin_splitlimit),132 ("parseJson".into(), builtin_parse_json),133 ("parseYaml".into(), builtin_parse_yaml),134 ("asciiUpper".into(), builtin_ascii_upper),135 ("asciiLower".into(), builtin_ascii_lower),136 ("member".into(), builtin_member),137 ("count".into(), builtin_count),138 ].iter().cloned().collect()139 };140}141142fn builtin_length(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {143 parse_args!(context, "length", args, 1, [144 0, x: ty!((string | object | array));145 ], {146 Ok(match x {147 Val::Str(n) => Val::Num(n.chars().count() as f64),148 Val::Arr(a) => Val::Num(a.len() as f64),149 Val::Obj(o) => Val::Num(150 o.fields_visibility()151 .into_iter()152 .filter(|(_k, v)| *v)153 .count() as f64,154 ),155 _ => unreachable!(),156 })157 })158}159160fn builtin_type(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {161 parse_args!(context, "type", args, 1, [162 0, x: ty!(any);163 ], {164 Ok(Val::Str(x.value_type().name().into()))165 })166}167168fn builtin_make_array(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {169 parse_args!(context, "makeArray", args, 2, [170 0, sz: ty!(BoundedNumber<(Some(0.0)), (None)>) => Val::Num;171 1, func: ty!(function) => Val::Func;172 ], {173 let mut out = Vec::with_capacity(sz as usize);174 for i in 0..sz as usize {175 out.push(LazyVal::new_resolved(func.evaluate_values(176 context.clone(),177 &[Val::Num(i as f64)]178 )?))179 }180 Ok(Val::Arr(out.into()))181 })182}183184fn builtin_codepoint(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {185 parse_args!(context, "codepoint", args, 1, [186 0, str: ty!(char) => Val::Str;187 ], {188 Ok(Val::Num(str.chars().next().unwrap() as u32 as f64))189 })190}191192fn builtin_object_fields_ex(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {193 parse_args!(context, "objectFieldsEx", args, 2, [194 0, obj: ty!(object) => Val::Obj;195 1, inc_hidden: ty!(boolean) => Val::Bool;196 ], {197 let out = obj.fields_ex(inc_hidden);198 Ok(Val::Arr(out.into_iter().map(Val::Str).collect::<Vec<_>>().into()))199 })200}201202fn builtin_object_has_ex(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {203 parse_args!(context, "objectHasEx", args, 3, [204 0, obj: ty!(object) => Val::Obj;205 1, f: ty!(string) => Val::Str;206 2, inc_hidden: ty!(boolean) => Val::Bool;207 ], {208 Ok(Val::Bool(obj.has_field_ex(f, inc_hidden)))209 })210}211212fn builtin_parse_json(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {213 parse_args!(context, "parseJson", args, 1, [214 0, s: ty!(string) => Val::Str;215 ], {216 let value: serde_json::Value = serde_json::from_str(&s).map_err(|e| RuntimeError(format!("failed to parse json: {}", e).into()))?;217 Ok(Val::try_from(&value)?)218 })219}220221fn builtin_parse_yaml(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {222 parse_args!(context, "parseYaml", args, 1, [223 0, s: ty!(string) => Val::Str;224 ], {225 let value = serde_yaml::Deserializer::from_str_with_quirks(&s, DeserializingQuirks { old_octals: true });226 let mut out = vec![];227 for item in value {228 let value = serde_json::Value::deserialize(item)229 .map_err(|e| RuntimeError(format!("failed to parse yaml: {}", e).into()))?;230 let val = Val::try_from(&value)?;231 out.push(val);232 }233 if out.is_empty() {234 Ok(Val::Null)235 } else if out.len() == 1 {236 Ok(out.into_iter().next().unwrap())237 } else {238 Ok(Val::Arr(out.into()))239 }240 })241}242243fn builtin_slice(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {244 parse_args!(context, "slice", args, 4, [245 0, indexable: ty!((string | array));246 1, index: ty!((number | null));247 2, end: ty!((number | null));248 3, step: ty!((number | null));249 ], {250 std_slice(251 indexable.into_indexable()?,252 index.try_cast_nullable_num("index")?.map(|v| v as usize),253 end.try_cast_nullable_num("end")?.map(|v| v as usize),254 step.try_cast_nullable_num("step")?.map(|v| v as usize),255 )256 })257}258259fn builtin_substr(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {260 parse_args!(context, "substr", args, 3, [261 0, str: ty!(string) => Val::Str;262 1, from: ty!(BoundedNumber<(Some(0.0)), (None)>) => Val::Num;263 2, len: ty!(BoundedNumber<(Some(0.0)), (None)>) => Val::Num;264 ], {265 let out: String = str.chars().skip(from as usize).take(len as usize).collect();266 Ok(Val::Str(out.into()))267 })268}269270fn builtin_primitive_equals(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {271 parse_args!(context, "primitiveEquals", args, 2, [272 0, a: ty!(any);273 1, b: ty!(any);274 ], {275 Ok(Val::Bool(primitive_equals(&a, &b)?))276 })277}278279fn builtin_equals(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {280 parse_args!(context, "equals", args, 2, [281 0, a: ty!(any);282 1, b: ty!(any);283 ], {284 Ok(Val::Bool(equals(&a, &b)?))285 })286}287288fn builtin_modulo(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {289 parse_args!(context, "modulo", args, 2, [290 0, a: ty!(number) => Val::Num;291 1, b: ty!(number) => Val::Num;292 ], {293 Ok(Val::Num(a % b))294 })295}296297fn builtin_mod(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {298 parse_args!(context, "mod", args, 2, [299 0, a: ty!((number | string));300 1, b: ty!(any);301 ], {302 evaluate_mod_op(&a, &b)303 })304}305306fn builtin_floor(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {307 parse_args!(context, "floor", args, 1, [308 0, x: ty!(number) => Val::Num;309 ], {310 Ok(Val::Num(x.floor()))311 })312}313314fn builtin_ceil(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {315 parse_args!(context, "ceil", args, 1, [316 0, x: ty!(number) => Val::Num;317 ], {318 Ok(Val::Num(x.ceil()))319 })320}321322fn builtin_log(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {323 parse_args!(context, "log", args, 1, [324 0, n: ty!(number) => Val::Num;325 ], {326 Ok(Val::Num(n.ln()))327 })328}329330fn builtin_pow(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {331 parse_args!(context, "pow", args, 2, [332 0, x: ty!(number) => Val::Num;333 1, n: ty!(number) => Val::Num;334 ], {335 Ok(Val::Num(x.powf(n)))336 })337}338339fn builtin_sqrt(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {340 parse_args!(context, "sqrt", args, 1, [341 0, x: ty!(BoundedNumber<(Some(0.0)), (None)>) => Val::Num;342 ], {343 Ok(Val::Num(x.sqrt()))344 })345}346347fn builtin_sin(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {348 parse_args!(context, "sin", args, 1, [349 0, x: ty!(number) => Val::Num;350 ], {351 Ok(Val::Num(x.sin()))352 })353}354355fn builtin_cos(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {356 parse_args!(context, "cos", args, 1, [357 0, x: ty!(number) => Val::Num;358 ], {359 Ok(Val::Num(x.cos()))360 })361}362363fn builtin_tan(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {364 parse_args!(context, "tan", args, 1, [365 0, x: ty!(number) => Val::Num;366 ], {367 Ok(Val::Num(x.tan()))368 })369}370371fn builtin_asin(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {372 parse_args!(context, "asin", args, 1, [373 0, x: ty!(number) => Val::Num;374 ], {375 Ok(Val::Num(x.asin()))376 })377}378379fn builtin_acos(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {380 parse_args!(context, "acos", args, 1, [381 0, x: ty!(number) => Val::Num;382 ], {383 Ok(Val::Num(x.acos()))384 })385}386387fn builtin_atan(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {388 parse_args!(context, "atan", args, 1, [389 0, x: ty!(number) => Val::Num;390 ], {391 Ok(Val::Num(x.atan()))392 })393}394395fn builtin_exp(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {396 parse_args!(context, "exp", args, 1, [397 0, x: ty!(number) => Val::Num;398 ], {399 Ok(Val::Num(x.exp()))400 })401}402403fn frexp(s: f64) -> (f64, i16) {404 if 0.0 == s {405 (s, 0)406 } else {407 let lg = s.abs().log2();408 let x = (lg - lg.floor() - 1.0).exp2();409 let exp = lg.floor() + 1.0;410 (s.signum() * x, exp as i16)411 }412}413414fn builtin_mantissa(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {415 parse_args!(context, "mantissa", args, 1, [416 0, x: ty!(number) => Val::Num;417 ], {418 Ok(Val::Num(frexp(x).0))419 })420}421422fn builtin_exponent(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {423 parse_args!(context, "exponent", args, 1, [424 0, x: ty!(number) => Val::Num;425 ], {426 Ok(Val::Num(frexp(x).1.into()))427 })428}429430fn builtin_ext_var(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {431 parse_args!(context, "extVar", args, 1, [432 0, x: ty!(string) => Val::Str;433 ], {434 Ok(with_state(|s| s.settings().ext_vars.get(&x).cloned()).ok_or(UndefinedExternalVariable(x))?)435 })436}437438fn builtin_native(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {439 parse_args!(context, "native", args, 1, [440 0, x: ty!(string) => Val::Str;441 ], {442 Ok(with_state(|s| s.settings().ext_natives.get(&x).cloned()).map(|v| Val::Func(Cc::new(FuncVal::NativeExt(x.clone(), v)))).ok_or(UndefinedExternalFunction(x))?)443 })444}445446fn builtin_filter(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {447 parse_args!(context, "filter", args, 2, [448 0, func: ty!(function) => Val::Func;449 1, arr: ty!(array) => Val::Arr;450 ], {451 Ok(Val::Arr(arr.filter(|val| func452 .evaluate_values(context.clone(), &[val.clone()])?453 .try_cast_bool("filter predicate"))?))454 })455}456457fn builtin_map(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {458 parse_args!(context, "map", args, 2, [459 0, func: ty!(function) => Val::Func;460 1, arr: ty!(array) => Val::Arr;461 ], {462 Ok(Val::Arr(arr.map(|val| func463 .evaluate_values(context.clone(), &[val]))?))464 })465}466467fn builtin_flatmap(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {468 parse_args!(context, "flatMap", args, 2, [469 0, func: ty!(function) => Val::Func;470 1, arr: ty!((array | string));471 ], {472 match arr {473 Val::Str(s) => {474 let mut out = String::new();475 for c in s.chars() {476 match func.evaluate_values(context.clone(), &[Val::Str(c.to_string().into())])? {477 Val::Str(o) => out.push_str(&o),478 _ => throw!(RuntimeError("in std.join all items should be strings".into())),479 };480 }481 Ok(Val::Str(out.into()))482 },483 Val::Arr(a) => {484 let mut out = Vec::new();485 for el in a.iter() {486 let el = el?;487 match func.evaluate_values(context.clone(), &[el])? {488 Val::Arr(o) => for oe in o.iter() {489 out.push(oe?)490 },491 _ => throw!(RuntimeError("in std.join all items should be arrays".into())),492 };493 }494 Ok(Val::Arr(out.into()))495 },496 _ => unreachable!(),497 }498 })499}500501fn builtin_foldl(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {502 parse_args!(context, "foldl", args, 3, [503 0, func: ty!(function) => Val::Func;504 1, arr: ty!(array) => Val::Arr;505 2, init: ty!(any);506 ], {507 let mut acc = init;508 for i in arr.iter() {509 acc = func.evaluate_values(context.clone(), &[acc, i?])?;510 }511 Ok(acc)512 })513}514515fn builtin_foldr(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {516 parse_args!(context, "foldr", args, 3, [517 0, func: ty!(function) => Val::Func;518 1, arr: ty!(array) => Val::Arr;519 2, init: ty!(any);520 ], {521 let mut acc = init;522 for i in arr.iter().rev() {523 acc = func.evaluate_values(context.clone(), &[i?, acc])?;524 }525 Ok(acc)526 })527}528529#[allow(non_snake_case)]530fn builtin_sort_impl(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {531 parse_args!(context, "sort", args, 2, [532 0, arr: ty!(array) => Val::Arr;533 1, keyF: ty!(function) => Val::Func;534 ], {535 if arr.len() <= 1 {536 return Ok(Val::Arr(arr))537 }538 Ok(Val::Arr(ArrValue::Eager(sort::sort(context, arr.evaluated()?, &keyF)?)))539 })540}541542fn builtin_format(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {543 parse_args!(context, "format", args, 2, [544 0, str: ty!(string) => Val::Str;545 1, vals: ty!(any)546 ], {547 std_format(str, vals)548 })549}550551fn builtin_range(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {552 parse_args!(context, "range", args, 2, [553 0, from: ty!(number) => Val::Num;554 1, to: ty!(number) => Val::Num;555 ], {556 if to < from {557 return Ok(Val::Arr(ArrValue::new_eager()))558 }559 let mut out = Vec::with_capacity((1+to as usize-from as usize).max(0));560 for i in from as usize..=to as usize {561 out.push(Val::Num(i as f64));562 }563 Ok(Val::Arr(out.into()))564 })565}566567fn builtin_char(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {568 parse_args!(context, "char", args, 1, [569 0, n: ty!(number) => Val::Num;570 ], {571 let mut out = String::new();572 out.push(std::char::from_u32(n as u32).ok_or_else(||573 InvalidUnicodeCodepointGot(n as u32)574 )?);575 Ok(Val::Str(out.into()))576 })577}578579fn builtin_encode_utf8(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {580 parse_args!(context, "encodeUTF8", args, 1, [581 0, str: ty!(string) => Val::Str;582 ], {583 Ok(Val::Arr((str.bytes().map(|b| Val::Num(b as f64)).collect::<Vec<Val>>()).into()))584 })585}586587fn builtin_decode_utf8(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {588 parse_args!(context, "decodeUTF8", args, 1, [589 0, arr: ty!((Array<ubyte>)) => Val::Arr;590 ], {591 let data: Result<Vec<u8>> = arr.iter().map(|v| v.map(|v| match v{592 Val::Num(n) => n as u8,593 _ => unreachable!(),594 })).collect();595 let data = data?;596 Ok(Val::Str(String::from_utf8(data).map_err(|_| RuntimeError("bad utf8".into()))?.into()))597 })598}599600fn builtin_md5(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {601 parse_args!(context, "md5", args, 1, [602 0, str: ty!(string) => Val::Str;603 ], {604 Ok(Val::Str(format!("{:x}", md5::compute(&str.as_bytes())).into()))605 })606}607608fn builtin_trace(context: Context, loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {609 parse_args!(context, "trace", args, 2, [610 0, str: ty!(string) => Val::Str;611 1, rest: ty!(any);612 ], {613 eprint!("TRACE:");614 with_state(|s|{615 let locs = s.map_source_locations(&loc.0, &[loc.1]);616 eprint!(" {}:{}", loc.0.file_name().unwrap().to_str().unwrap(), locs[0].line);617 });618 eprintln!(" {}", str);619 Ok(rest)620 })621}622623fn builtin_base64(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {624 parse_args!(context, "base64", args, 1, [625 0, input: ty!((string | (Array<number>)));626 ], {627 Ok(Val::Str(match input {628 Val::Str(s) => {629 base64::encode(s.bytes().collect::<Vec<_>>()).into()630 },631 Val::Arr(a) => {632 base64::encode(a.iter().map(|v| {633 Ok(v?.unwrap_num()? as u8)634 }).collect::<Result<Vec<_>>>()?).into()635 },636 _ => unreachable!()637 }))638 })639}640641fn builtin_base64_decode_bytes(642 context: Context,643 _loc: &ExprLocation,644 args: &ArgsDesc,645) -> Result<Val> {646 parse_args!(context, "base64DecodeBytes", args, 1, [647 0, input: ty!(string) => Val::Str;648 ], {649 Ok(Val::Arr(650 base64::decode(&input.as_bytes())651 .map_err(|_| RuntimeError("bad base64".into()))?652 .iter()653 .map(|v| Val::Num(*v as f64)).collect::<Vec<_>>().into()654 ))655 })656}657658fn builtin_base64_decode(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {659 parse_args!(context, "base64Decode", args, 1, [660 0, input: ty!(string) => Val::Str;661 ], {662 Ok(Val::Str(663 String::from_utf8(base64::decode(&input.as_bytes())664 .map_err(|_| RuntimeError("bad base64".into()))?)665 .map_err(|_| RuntimeError("bad utf8".into()))?.into()666 ))667 })668}669670fn builtin_join(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {671 parse_args!(context, "join", args, 2, [672 0, sep: ty!((string | array));673 1, arr: ty!(array) => Val::Arr;674 ], {675 Ok(match sep {676 Val::Arr(joiner_items) => {677 let mut out = Vec::new();678679 let mut first = true;680 for item in arr.iter() {681 let item = item?.clone();682 if let Val::Arr(items) = item {683 if !first {684 out.reserve(joiner_items.len());685 // TODO: extend686 for item in joiner_items.iter() {687 out.push(item?);688 }689 }690 first = false;691 out.reserve(items.len());692 // TODO: extend693 for item in items.iter() {694 out.push(item?);695 }696 } else {697 throw!(RuntimeError("in std.join all items should be arrays".into()));698 }699 }700701 Val::Arr(out.into())702 },703 Val::Str(sep) => {704 let mut out = String::new();705706 let mut first = true;707 for item in arr.iter() {708 let item = item?.clone();709 if let Val::Str(item) = item {710 if !first {711 out += &sep;712 }713 first = false;714 out += &item;715 } else {716 throw!(RuntimeError("in std.join all items should be strings".into()));717 }718 }719720 Val::Str(out.into())721 },722 _ => unreachable!()723 })724 })725}726727fn builtin_escape_string_json(728 context: Context,729 _loc: &ExprLocation,730 args: &ArgsDesc,731) -> Result<Val> {732 parse_args!(context, "escapeStringJson", args, 1, [733 0, str_: ty!(string) => Val::Str;734 ], {735 Ok(Val::Str(escape_string_json(&str_).into()))736 })737}738739fn builtin_manifest_json_ex(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {740 parse_args!(context, "manifestJsonEx", args, 2, [741 0, value: ty!(any);742 1, indent: ty!(string) => Val::Str;743 ], {744 Ok(Val::Str(manifest_json_ex(&value, &ManifestJsonOptions {745 padding: &indent,746 mtype: ManifestType::Std,747 })?.into()))748 })749}750751fn builtin_manifest_yaml_doc(752 context: Context,753 _loc: &ExprLocation,754 args: &ArgsDesc,755) -> Result<Val> {756 parse_args!(context, "manifestYamlDoc", args, 3, [757 0, value: ty!(any);758 1, indent_array_in_object: ty!(boolean) => Val::Bool;759 2, quote_keys: ty!(boolean) => Val::Bool;760 ], {761 Ok(Val::Str(manifest_yaml_ex(&value, &ManifestYamlOptions {762 padding: " ",763 arr_element_padding: if indent_array_in_object { " " } else { "" },764 quote_keys,765 })?.into()))766 })767}768769fn builtin_reverse(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {770 parse_args!(context, "reverse", args, 1, [771 0, value: ty!(array) => Val::Arr;772 ], {773 Ok(Val::Arr(value.reversed()))774 })775}776777fn builtin_id(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {778 parse_args!(context, "id", args, 1, [779 0, v: ty!(any);780 ], {781 Ok(v)782 })783}784785fn builtin_str_replace(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {786 parse_args!(context, "strReplace", args, 3, [787 0, str: ty!(string) => Val::Str;788 1, from: ty!(string) => Val::Str;789 2, to: ty!(string) => Val::Str;790 ], {791 Ok(Val::Str(str.replace(&from as &str, &to as &str).into()))792 })793}794795fn builtin_splitlimit(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {796 parse_args!(context, "splitLimit", args, 3, [797 0, str: ty!(string) => Val::Str;798 1, c: ty!(char) => Val::Str;799 2, maxsplits: ty!(number) => Val::Num;800 ], {801 let maxsplits = maxsplits as isize;802 let c = c.chars().next().unwrap();803804 let out: Vec<Val> = if maxsplits == -1 {805 str.split(c).map(|s| Val::Str(s.into())).collect()806 } else {807 str.splitn(maxsplits as usize + 1, c).map(|s| Val::Str(s.into())).collect()808 };809810 Ok(Val::Arr(out.into()))811 })812}813814fn builtin_ascii_upper(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {815 parse_args!(context, "asciiUpper", args, 1, [816 0, str: ty!(string) => Val::Str;817 ], {818 Ok(Val::Str(str.to_ascii_uppercase().into()))819 })820}821822fn builtin_ascii_lower(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {823 parse_args!(context, "asciiLower", args, 1, [824 0, str: ty!(string) => Val::Str;825 ], {826 Ok(Val::Str(str.to_ascii_lowercase().into()))827 })828}829830fn builtin_member(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {831 parse_args!(context, "member", args, 2, [832 0, arr: ty!((array | string));833 1, x: ty!(any);834 ], {835 match arr {836 Val::Str(s) => {837 let x = x.try_cast_str("x should be string")?;838 Ok(Val::Bool(!x.is_empty() && s.contains(&*x)))839 }840 Val::Arr(a) => {841 for item in a.iter() {842 let item = item?;843 if equals(&item, &x)? {844 return Ok(Val::Bool(true));845 }846 }847 Ok(Val::Bool(false))848 }849 _ => unreachable!(),850 }851 })852}853854fn builtin_count(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {855 parse_args!(context, "count", args, 2, [856 0, arr: ty!(array) => Val::Arr;857 1, x: ty!(any);858 ], {859 let mut count = 0;860 for item in arr.iter() {861 let item = item?;862 if equals(&item, &x)? {863 count += 1;864 }865 }866 Ok(Val::Num(count as f64))867 })868}869870pub fn call_builtin(871 context: Context,872 loc: &ExprLocation,873 name: &str,874 args: &ArgsDesc,875) -> Result<Val> {876 BUILTINS877 .with(|builtins| builtins.get(name).copied())878 .ok_or_else(|| IntrinsicNotFound(name.into()))?(context, loc, args)879}crates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/val.rs
+++ b/crates/jrsonnet-evaluator/src/val.rs
@@ -556,6 +556,7 @@
&ManifestYamlOptions {
padding,
arr_element_padding: padding,
+ quote_keys: false,
},
)
.map(|s| s.into())
crates/jrsonnet-stdlib/src/std.jsonnetdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/std.jsonnet
+++ b/crates/jrsonnet-stdlib/src/std.jsonnet
@@ -377,7 +377,7 @@
manifestYamlDocImpl:: $intrinsic(manifestYamlDocImpl),
- manifestYamlDoc(value, indent_array_in_object=false):: std.manifestYamlDocImpl(value, indent_array_in_object),
+ manifestYamlDoc(value, indent_array_in_object=false, quote_keys=true):: std.manifestYamlDocImpl(value, indent_array_in_object, quote_keys),
manifestYamlStream(value, indent_array_in_object=false, c_document_end=true)::
if !std.isArray(value) then