difftreelog
refactor move more stdlib functions to builtins
in: master
16 files changed
crates/jrsonnet-cli/src/manifest.rsdiffbeforeafterboth--- a/crates/jrsonnet-cli/src/manifest.rs
+++ b/crates/jrsonnet-cli/src/manifest.rs
@@ -4,7 +4,7 @@
use jrsonnet_evaluator::manifest::{
JsonFormat, ManifestFormat, StringFormat, ToStringFormat, YamlStreamFormat,
};
-use jrsonnet_stdlib::{TomlFormat, YamlFormat};
+use jrsonnet_stdlib::{TomlFormat, XmlJsonmlFormat, YamlFormat};
#[derive(Clone, Copy, ValueEnum)]
pub enum ManifestFormatName {
@@ -13,6 +13,7 @@
Json,
Yaml,
Toml,
+ XmlJsonml,
}
#[derive(Parser)]
@@ -70,10 +71,11 @@
#[cfg(feature = "exp-preserve-order")]
preserve_order,
)),
+ ManifestFormatName::XmlJsonml => Box::new(XmlJsonmlFormat::cli()),
}
};
if self.yaml_stream {
- Box::new(YamlStreamFormat(format))
+ Box::new(YamlStreamFormat::cli(format))
} else {
format
}
crates/jrsonnet-evaluator/src/arr/mod.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/arr/mod.rs
+++ b/crates/jrsonnet-evaluator/src/arr/mod.rs
@@ -1,4 +1,7 @@
-use std::any::Any;
+use std::{
+ any::Any,
+ num::{NonZeroU32, NonZeroUsize},
+};
use jrsonnet_gcmodule::{Cc, Trace};
use jrsonnet_interner::IBytes;
@@ -99,28 +102,29 @@
Self::new(RangeArray::new_inclusive(a, b))
}
+ /// # Panics
+ /// If step == 0
#[must_use]
- pub fn slice(
- self,
- from: Option<usize>,
- to: Option<usize>,
- step: Option<usize>,
- ) -> Option<Self> {
- let len = self.len();
- let from = from.unwrap_or(0);
- let to = to.unwrap_or(len).min(len);
- let step = step.unwrap_or(1);
+ pub fn slice(self, index: Option<i32>, end: Option<i32>, step: Option<NonZeroU32>) -> Self {
+ let get_idx = |pos: Option<i32>, len: usize, default| match pos {
+ Some(v) if v < 0 => len.saturating_sub((-v) as usize),
+ Some(v) => (v as usize).min(len),
+ None => default,
+ };
+ let index = get_idx(index, self.len(), 0);
+ let end = get_idx(end, self.len(), self.len());
+ let step = step.unwrap_or_else(|| NonZeroU32::new(1).expect("1 != 0"));
- if from >= to || step == 0 {
- return None;
+ if index >= end {
+ return Self::empty();
}
- Some(Self::new(SliceArray {
+ Self::new(SliceArray {
inner: self,
- from: from as u32,
- to: to as u32,
- step: step as u32,
- }))
+ from: index as u32,
+ to: end as u32,
+ step: step.get(),
+ })
}
/// Array length.
crates/jrsonnet-evaluator/src/manifest.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/manifest.rs
+++ b/crates/jrsonnet-evaluator/src/manifest.rs
@@ -340,7 +340,28 @@
}
}
-pub struct YamlStreamFormat<I>(pub I);
+pub struct YamlStreamFormat<I> {
+ inner: I,
+ c_document_end: bool,
+ end_newline: bool,
+}
+impl<I> YamlStreamFormat<I> {
+ pub fn std_yaml_stream(inner: I, c_document_end: bool) -> Self {
+ Self {
+ inner,
+ c_document_end,
+ // Stdlib format always inserts newline at the end
+ end_newline: true,
+ }
+ }
+ pub fn cli(inner: I) -> Self {
+ Self {
+ inner,
+ c_document_end: true,
+ end_newline: false,
+ }
+ }
+}
impl<I: ManifestFormat> ManifestFormat for YamlStreamFormat<I> {
fn manifest_buf(&self, val: Val, out: &mut String) -> Result<()> {
let Val::Arr(arr) = val else {
@@ -353,11 +374,16 @@
for v in arr.iter() {
let v = v?;
out.push_str("---\n");
- self.0.manifest_buf(v, out)?;
+ self.inner.manifest_buf(v, out)?;
out.push('\n');
}
+ }
+ if self.c_document_end {
out.push_str("...");
}
+ if self.end_newline {
+ out.push('\n');
+ }
Ok(())
}
}
crates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/val.rs
+++ b/crates/jrsonnet-evaluator/src/val.rs
@@ -2,6 +2,7 @@
cell::RefCell,
fmt::{self, Debug, Display},
mem::replace,
+ num::{NonZeroU32, NonZeroUsize},
rc::Rc,
};
@@ -277,26 +278,11 @@
.into(),
))
}
- Self::Arr(arr) => {
- let get_idx = |pos: Option<i32>, len: usize, default| match pos {
- Some(v) if v < 0 => len.saturating_sub((-v) as usize),
- Some(v) => (v as usize).min(len),
- None => default,
- };
- let index = get_idx(index, arr.len(), 0);
- let end = get_idx(end, arr.len(), arr.len());
- let step = step.as_deref().copied().unwrap_or(1);
-
- if index >= end {
- return Ok(Self::Arr(ArrValue::empty()));
- }
-
- Ok(Self::Arr(
- arr.clone()
- .slice(Some(index), Some(end), Some(step))
- .expect("arguments checked"),
- ))
- }
+ Self::Arr(arr) => Ok(Self::Arr(arr.clone().slice(
+ index,
+ end,
+ step.map(|v| NonZeroU32::new(v.value() as u32).expect("bounded != 0")),
+ ))),
}
}
}
crates/jrsonnet-macros/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-macros/src/lib.rs
+++ b/crates/jrsonnet-macros/src/lib.rs
@@ -34,6 +34,12 @@
Ok(Some(attr))
}
+fn remove_attr<I>(attrs: &mut Vec<Attribute>, ident: I)
+where
+ Ident: PartialEq<I>,
+{
+ attrs.retain(|a| !a.path().is_ident(&ident));
+}
fn path_is(path: &Path, needed: &str) -> bool {
path.leading_colon.is_none()
@@ -121,10 +127,21 @@
}
}
+enum Optionality {
+ Required,
+ Optional,
+ Default(Expr),
+}
+impl Optionality {
+ fn is_optional(&self) -> bool {
+ !matches!(self, Self::Required)
+ }
+}
+
enum ArgInfo {
Normal {
ty: Box<Type>,
- is_option: bool,
+ optionality: Optionality,
name: Option<String>,
cfg_attrs: Vec<Attribute>,
},
@@ -138,7 +155,7 @@
}
impl ArgInfo {
- fn parse(name: &str, arg: &FnArg) -> Result<Self> {
+ fn parse(name: &str, arg: &mut FnArg) -> Result<Self> {
let FnArg::Typed(arg) = arg else {
unreachable!()
};
@@ -163,7 +180,10 @@
_ => {}
}
- let (is_option, ty) = if let Some(ty) = extract_type_from_option(ty)? {
+ let (optionality, ty) = if let Some(default) = parse_attr::<_, _>(&arg.attrs, "default")? {
+ remove_attr(&mut arg.attrs, "default");
+ (Optionality::Default(default), ty.clone())
+ } else if let Some(ty) = extract_type_from_option(ty)? {
if type_is_path(ty, "Thunk").is_some() {
return Ok(Self::Lazy {
is_option: true,
@@ -171,9 +191,9 @@
});
}
- (true, Box::new(ty.clone()))
+ (Optionality::Optional, Box::new(ty.clone()))
} else {
- (false, ty.clone())
+ (Optionality::Required, ty.clone())
};
let cfg_attrs = arg
@@ -185,7 +205,7 @@
Ok(Self::Normal {
ty,
- is_option,
+ optionality,
name: ident.map(|v| v.to_string()),
cfg_attrs,
})
@@ -201,18 +221,14 @@
let item_fn = item.clone();
let item_fn: ItemFn = parse_macro_input!(item_fn);
- match builtin_inner(attr, item_fn, item.into()) {
+ match builtin_inner(attr, item_fn) {
Ok(v) => v.into(),
Err(e) => e.into_compile_error().into(),
}
}
#[allow(clippy::too_many_lines)]
-fn builtin_inner(
- attr: BuiltinAttrs,
- fun: ItemFn,
- item: proc_macro2::TokenStream,
-) -> syn::Result<TokenStream> {
+fn builtin_inner(attr: BuiltinAttrs, mut fun: ItemFn) -> syn::Result<TokenStream> {
let ReturnType::Type(_, result) = &fun.sig.output else {
return Err(Error::new(
fun.sig.span(),
@@ -224,13 +240,13 @@
let args = fun
.sig
.inputs
- .iter()
+ .iter_mut()
.map(|arg| ArgInfo::parse(&name, arg))
.collect::<Result<Vec<_>>>()?;
let params_desc = args.iter().filter_map(|a| match a {
ArgInfo::Normal {
- is_option,
+ optionality,
name,
cfg_attrs,
..
@@ -238,9 +254,10 @@
let name = name
.as_ref()
.map_or_else(|| quote! {None}, |n| quote! {ParamName::new_static(#n)});
+ let is_optional = optionality.is_optional();
Some(quote! {
#(#cfg_attrs)*
- BuiltinParam::new(#name, #is_option),
+ BuiltinParam::new(#name, #is_optional),
})
}
ArgInfo::Lazy { is_option, name } => {
@@ -270,7 +287,7 @@
.map(|(id, a)| match a {
ArgInfo::Normal {
ty,
- is_option,
+ optionality,
name,
cfg_attrs,
} => {
@@ -279,17 +296,22 @@
|| format!("argument <{}> evaluation", #name),
|| <#ty>::from_untyped(value.evaluate()?),
)?};
- let value = if *is_option {
- quote! {if let Some(value) = &parsed[#id] {
+ let value = match optionality {
+ Optionality::Required => quote! {{
+ let value = parsed[#id].as_ref().expect("args shape is checked");
+ #eval
+ },},
+ Optionality::Optional => quote! {if let Some(value) = &parsed[#id] {
Some(#eval)
} else {
None
- },}
- } else {
- quote! {{
- let value = parsed[#id].as_ref().expect("args shape is checked");
+ },},
+ Optionality::Default(expr) => quote! {if let Some(value) = &parsed[#id] {
#eval
- },}
+ } else {
+ let v: #ty = #expr;
+ v
+ },},
};
quote! {
#(#cfg_attrs)*
@@ -302,7 +324,7 @@
Some(value.clone())
} else {
None
- }}
+ },}
} else {
quote! {
parsed[#id].as_ref().expect("args shape is correct").clone(),
@@ -343,7 +365,7 @@
};
Ok(quote! {
- #item
+ #fun
#[doc(hidden)]
#[allow(non_camel_case_types)]
@@ -373,7 +395,7 @@
fn params(&self) -> &[BuiltinParam] {
PARAMS
}
- #[allow(unused_variable)]
+ #[allow(unused_variables)]
fn call(&self, ctx: Context, location: CallLocation, args: &dyn ArgsLike) -> Result<Val> {
let parsed = parse_builtin_call(ctx.clone(), &PARAMS, args, false)?;
crates/jrsonnet-stdlib/src/arrays.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/arrays.rs
+++ b/crates/jrsonnet-stdlib/src/arrays.rs
@@ -265,21 +265,18 @@
}
#[builtin]
-pub fn builtin_remove_at(arr: ArrValue, at: usize) -> Result<ArrValue> {
+pub fn builtin_remove_at(arr: ArrValue, at: i32) -> Result<ArrValue> {
let newArrLeft = arr.clone().slice(None, Some(at), None);
let newArrRight = arr.slice(Some(at + 1), None, None);
- Ok(ArrValue::extended(
- newArrLeft.unwrap_or_else(ArrValue::empty),
- newArrRight.unwrap_or_else(ArrValue::empty),
- ))
+ Ok(ArrValue::extended(newArrLeft, newArrRight))
}
#[builtin]
pub fn builtin_remove(arr: ArrValue, elem: Val) -> Result<ArrValue> {
for (index, item) in arr.iter().enumerate() {
if equals(&item?, &elem)? {
- return builtin_remove_at(arr.clone(), index);
+ return builtin_remove_at(arr.clone(), index as i32);
}
}
Ok(arr)
@@ -325,7 +322,9 @@
#[builtin]
pub fn builtin_prune(
a: Val,
- #[cfg(feature = "exp-preserve-order")] preserve_order: bool,
+ #[default(false)]
+ #[cfg(feature = "exp-preserve-order")]
+ preserve_order: bool,
) -> Result<Val> {
fn is_content(val: &Val) -> bool {
match val {
crates/jrsonnet-stdlib/src/lib.rsdiffbeforeafterboth1#![allow(clippy::similar_names)]23use std::{4 cell::{Ref, RefCell, RefMut},5 collections::HashMap,6 rc::Rc,7};89pub use arrays::*;10pub use compat::*;11pub use encoding::*;12pub use hash::*;13use jrsonnet_evaluator::{14 error::{ErrorKind::*, Result},15 function::{CallLocation, FuncVal, TlaArg},16 tb,17 trace::PathResolver,18 ContextBuilder, IStr, ObjValue, ObjValueBuilder, State, Thunk, Val,19};20use jrsonnet_gcmodule::Trace;21use jrsonnet_parser::Source;22pub use manifest::*;23pub use math::*;24pub use misc::*;25pub use objects::*;26pub use operator::*;27pub use parse::*;28pub use sets::*;29pub use sort::*;30pub use strings::*;31pub use types::*;3233#[cfg(feature = "exp-regex")]34pub use crate::regex::*;3536mod arrays;37mod compat;38mod encoding;39mod expr;40mod hash;41mod manifest;42mod math;43mod misc;44mod objects;45mod operator;46mod parse;47#[cfg(feature = "exp-regex")]48mod regex;49mod sets;50mod sort;51mod strings;52mod types;5354#[allow(clippy::too_many_lines)]55pub fn stdlib_uncached(settings: Rc<RefCell<Settings>>) -> ObjValue {56 let mut builder = ObjValueBuilder::new();5758 let expr = expr::stdlib_expr();59 let eval = jrsonnet_evaluator::evaluate(ContextBuilder::dangerous_empty_state().build(), &expr)60 .expect("stdlib.jsonnet should have no errors")61 .as_obj()62 .expect("stdlib.jsonnet should evaluate to object");6364 builder.with_super(eval);6566 // FIXME: Use PHF67 for (name, builtin) in [68 // Types69 ("type", builtin_type::INST),70 ("isString", builtin_is_string::INST),71 ("isNumber", builtin_is_number::INST),72 ("isBoolean", builtin_is_boolean::INST),73 ("isObject", builtin_is_object::INST),74 ("isArray", builtin_is_array::INST),75 ("isFunction", builtin_is_function::INST),76 // Arrays77 ("makeArray", builtin_make_array::INST),78 ("repeat", builtin_repeat::INST),79 ("slice", builtin_slice::INST),80 ("map", builtin_map::INST),81 ("flatMap", builtin_flatmap::INST),82 ("filter", builtin_filter::INST),83 ("foldl", builtin_foldl::INST),84 ("foldr", builtin_foldr::INST),85 ("range", builtin_range::INST),86 ("join", builtin_join::INST),87 ("reverse", builtin_reverse::INST),88 ("any", builtin_any::INST),89 ("all", builtin_all::INST),90 ("member", builtin_member::INST),91 ("contains", builtin_contains::INST),92 ("count", builtin_count::INST),93 ("avg", builtin_avg::INST),94 ("removeAt", builtin_remove_at::INST),95 ("remove", builtin_remove::INST),96 ("flattenArrays", builtin_flatten_arrays::INST),97 ("flattenDeepArray", builtin_flatten_deep_array::INST),98 ("prune", builtin_prune::INST),99 ("filterMap", builtin_filter_map::INST),100 // Math101 ("abs", builtin_abs::INST),102 ("sign", builtin_sign::INST),103 ("max", builtin_max::INST),104 ("min", builtin_min::INST),105 ("sum", builtin_sum::INST),106 ("modulo", builtin_modulo::INST),107 ("floor", builtin_floor::INST),108 ("ceil", builtin_ceil::INST),109 ("log", builtin_log::INST),110 ("pow", builtin_pow::INST),111 ("sqrt", builtin_sqrt::INST),112 ("sin", builtin_sin::INST),113 ("cos", builtin_cos::INST),114 ("tan", builtin_tan::INST),115 ("asin", builtin_asin::INST),116 ("acos", builtin_acos::INST),117 ("atan", builtin_atan::INST),118 ("atan2", builtin_atan2::INST),119 ("exp", builtin_exp::INST),120 ("mantissa", builtin_mantissa::INST),121 ("exponent", builtin_exponent::INST),122 ("round", builtin_round::INST),123 ("isEven", builtin_is_even::INST),124 ("isOdd", builtin_is_odd::INST),125 ("isInteger", builtin_is_integer::INST),126 ("isDecimal", builtin_is_decimal::INST),127 // Operator128 ("mod", builtin_mod::INST),129 ("primitiveEquals", builtin_primitive_equals::INST),130 ("equals", builtin_equals::INST),131 ("xor", builtin_xor::INST),132 ("xnor", builtin_xnor::INST),133 ("format", builtin_format::INST),134 // Sort135 ("sort", builtin_sort::INST),136 ("uniq", builtin_uniq::INST),137 ("set", builtin_set::INST),138 ("minArray", builtin_min_array::INST),139 ("maxArray", builtin_max_array::INST),140 // Hash141 ("md5", builtin_md5::INST),142 ("sha1", builtin_sha1::INST),143 ("sha256", builtin_sha256::INST),144 ("sha512", builtin_sha512::INST),145 ("sha3", builtin_sha3::INST),146 // Encoding147 ("encodeUTF8", builtin_encode_utf8::INST),148 ("decodeUTF8", builtin_decode_utf8::INST),149 ("base64", builtin_base64::INST),150 ("base64Decode", builtin_base64_decode::INST),151 ("base64DecodeBytes", builtin_base64_decode_bytes::INST),152 // Objects153 ("objectFieldsEx", builtin_object_fields_ex::INST),154 ("objectFields", builtin_object_fields::INST),155 ("objectFieldsAll", builtin_object_fields_all::INST),156 ("objectValues", builtin_object_values::INST),157 ("objectValuesAll", builtin_object_values_all::INST),158 ("objectKeysValues", builtin_object_keys_values::INST),159 ("objectKeysValuesAll", builtin_object_keys_values_all::INST),160 ("objectHasEx", builtin_object_has_ex::INST),161 ("objectHas", builtin_object_has::INST),162 ("objectHasAll", builtin_object_has_all::INST),163 ("objectRemoveKey", builtin_object_remove_key::INST),164 // Manifest165 ("escapeStringJson", builtin_escape_string_json::INST),166 ("manifestJsonEx", builtin_manifest_json_ex::INST),167 ("manifestYamlDoc", builtin_manifest_yaml_doc::INST),168 ("manifestTomlEx", builtin_manifest_toml_ex::INST),169 ("toString", builtin_to_string::INST),170 // Parsing171 ("parseJson", builtin_parse_json::INST),172 ("parseYaml", builtin_parse_yaml::INST),173 // Strings174 ("codepoint", builtin_codepoint::INST),175 ("substr", builtin_substr::INST),176 ("char", builtin_char::INST),177 ("strReplace", builtin_str_replace::INST),178 ("isEmpty", builtin_is_empty::INST),179 ("equalsIgnoreCase", builtin_equals_ignore_case::INST),180 ("splitLimit", builtin_splitlimit::INST),181 ("splitLimitR", builtin_splitlimitr::INST),182 ("asciiUpper", builtin_ascii_upper::INST),183 ("asciiLower", builtin_ascii_lower::INST),184 ("findSubstr", builtin_find_substr::INST),185 ("parseInt", builtin_parse_int::INST),186 #[cfg(feature = "exp-bigint")]187 ("bigint", builtin_bigint::INST),188 ("parseOctal", builtin_parse_octal::INST),189 ("parseHex", builtin_parse_hex::INST),190 ("stringChars", builtin_string_chars::INST),191 // Misc192 ("length", builtin_length::INST),193 ("startsWith", builtin_starts_with::INST),194 ("endsWith", builtin_ends_with::INST),195 // Sets196 ("setMember", builtin_set_member::INST),197 ("setInter", builtin_set_inter::INST),198 ("setDiff", builtin_set_diff::INST),199 ("setUnion", builtin_set_union::INST),200 // Regex201 #[cfg(feature = "exp-regex")]202 ("regexQuoteMeta", builtin_regex_quote_meta::INST),203 // Compat204 ("__compare", builtin___compare::INST),205 ]206 .iter()207 .copied()208 {209 builder.method(name, builtin);210 }211212 builder.method(213 "extVar",214 builtin_ext_var {215 settings: settings.clone(),216 },217 );218 builder.method(219 "native",220 builtin_native {221 settings: settings.clone(),222 },223 );224 builder.method("trace", builtin_trace { settings });225 builder.method("id", FuncVal::Id);226227 #[cfg(feature = "exp-regex")]228 {229 // Regex230 let regex_cache = RegexCache::default();231 builder.method(232 "regexFullMatch",233 builtin_regex_full_match {234 cache: regex_cache.clone(),235 },236 );237 builder.method(238 "regexPartialMatch",239 builtin_regex_partial_match {240 cache: regex_cache.clone(),241 },242 );243 builder.method(244 "regexReplace",245 builtin_regex_replace {246 cache: regex_cache.clone(),247 },248 );249 builder.method(250 "regexGlobalReplace",251 builtin_regex_global_replace { cache: regex_cache },252 );253 };254255 builder.build()256}257258pub trait TracePrinter {259 fn print_trace(&self, loc: CallLocation, value: IStr);260}261262pub struct StdTracePrinter {263 resolver: PathResolver,264}265impl StdTracePrinter {266 pub fn new(resolver: PathResolver) -> Self {267 Self { resolver }268 }269}270impl TracePrinter for StdTracePrinter {271 fn print_trace(&self, loc: CallLocation, value: IStr) {272 eprint!("TRACE:");273 if let Some(loc) = loc.0 {274 let locs = loc.0.map_source_locations(&[loc.1]);275 eprint!(276 " {}:{}",277 loc.0.source_path().path().map_or_else(278 || loc.0.source_path().to_string(),279 |p| self.resolver.resolve(p)280 ),281 locs[0].line282 );283 }284 eprintln!(" {value}");285 }286}287288pub struct Settings {289 /// Used for `std.extVar`290 pub ext_vars: HashMap<IStr, TlaArg>,291 /// Used for `std.native`292 pub ext_natives: HashMap<IStr, FuncVal>,293 /// Used for `std.trace`294 pub trace_printer: Box<dyn TracePrinter>,295 /// Used for `std.thisFile`296 pub path_resolver: PathResolver,297}298299fn extvar_source(name: &str, code: impl Into<IStr>) -> Source {300 let source_name = format!("<extvar:{name}>");301 Source::new_virtual(source_name.into(), code.into())302}303304#[derive(Trace, Clone)]305pub struct ContextInitializer {306 /// When we don't need to support legacy-this-file, we can reuse same context for all files307 #[cfg(not(feature = "legacy-this-file"))]308 context: jrsonnet_evaluator::Context,309 /// For `populate`310 #[cfg(not(feature = "legacy-this-file"))]311 stdlib_thunk: Thunk<Val>,312 /// Otherwise, we can only keep first stdlib layer, and then stack thisFile on top of it313 #[cfg(feature = "legacy-this-file")]314 stdlib_obj: ObjValue,315 settings: Rc<RefCell<Settings>>,316}317impl ContextInitializer {318 pub fn new(s: State, resolver: PathResolver) -> Self {319 let settings = Settings {320 ext_vars: HashMap::new(),321 ext_natives: HashMap::new(),322 trace_printer: Box::new(StdTracePrinter::new(resolver.clone())),323 path_resolver: resolver,324 };325 let settings = Rc::new(RefCell::new(settings));326 let stdlib_obj = stdlib_uncached(settings.clone());327 #[cfg(not(feature = "legacy-this-file"))]328 let stdlib_thunk = Thunk::evaluated(Val::Obj(stdlib_obj));329 #[cfg(feature = "legacy-this-file")]330 let _ = s;331 Self {332 #[cfg(not(feature = "legacy-this-file"))]333 context: {334 let mut context = ContextBuilder::with_capacity(s, 1);335 context.bind("std", stdlib_thunk.clone());336 context.build()337 },338 #[cfg(not(feature = "legacy-this-file"))]339 stdlib_thunk,340 #[cfg(feature = "legacy-this-file")]341 stdlib_obj,342 settings,343 }344 }345 pub fn settings(&self) -> Ref<Settings> {346 self.settings.borrow()347 }348 pub fn settings_mut(&self) -> RefMut<Settings> {349 self.settings.borrow_mut()350 }351 pub fn add_ext_var(&self, name: IStr, value: Val) {352 self.settings_mut()353 .ext_vars354 .insert(name, TlaArg::Val(value));355 }356 pub fn add_ext_str(&self, name: IStr, value: IStr) {357 self.settings_mut()358 .ext_vars359 .insert(name, TlaArg::String(value));360 }361 pub fn add_ext_code(&self, name: &str, code: impl Into<IStr>) -> Result<()> {362 let code = code.into();363 let source = extvar_source(name, code.clone());364 let parsed = jrsonnet_parser::parse(365 &code,366 &jrsonnet_parser::ParserSettings {367 source: source.clone(),368 },369 )370 .map_err(|e| ImportSyntaxError {371 path: source,372 error: Box::new(e),373 })?;374 // self.data_mut().volatile_files.insert(source_name, code);375 self.settings_mut()376 .ext_vars377 .insert(name.into(), TlaArg::Code(parsed));378 Ok(())379 }380 pub fn add_native(&self, name: impl Into<IStr>, cb: impl Into<FuncVal>) {381 self.settings_mut()382 .ext_natives383 .insert(name.into(), cb.into());384 }385}386impl jrsonnet_evaluator::ContextInitializer for ContextInitializer {387 fn reserve_vars(&self) -> usize {388 1389 }390 #[cfg(not(feature = "legacy-this-file"))]391 fn initialize(&self, _s: State, _source: Source) -> jrsonnet_evaluator::Context {392 self.context.clone()393 }394 #[cfg(not(feature = "legacy-this-file"))]395 fn populate(&self, _for_file: Source, builder: &mut ContextBuilder) {396 builder.bind("std", self.stdlib_thunk.clone());397 }398 #[cfg(feature = "legacy-this-file")]399 fn populate(&self, source: Source, builder: &mut ContextBuilder) {400 let mut std = ObjValueBuilder::new();401 std.with_super(self.stdlib_obj.clone());402 std.field("thisFile").hide().value({403 let source_path = source.source_path();404 source_path.path().map_or_else(405 || source_path.to_string(),406 |p| self.settings().path_resolver.resolve(p),407 )408 });409 let stdlib_with_this_file = std.build();410411 builder.bind("std", Thunk::evaluated(Val::Obj(stdlib_with_this_file)));412 }413 fn as_any(&self) -> &dyn std::any::Any {414 self415 }416}417418pub trait StateExt {419 /// This method was previously implemented in jrsonnet-evaluator itself420 fn with_stdlib(&self);421}422423impl StateExt for State {424 fn with_stdlib(&self) {425 let initializer = ContextInitializer::new(self.clone(), PathResolver::new_cwd_fallback());426 self.settings_mut().context_initializer = tb!(initializer);427 }428}crates/jrsonnet-stdlib/src/manifest/mod.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/manifest/mod.rs
+++ b/crates/jrsonnet-stdlib/src/manifest/mod.rs
@@ -1,13 +1,17 @@
+mod python;
mod toml;
+mod xml;
mod yaml;
use jrsonnet_evaluator::{
function::builtin,
- manifest::{escape_string_json, JsonFormat},
+ manifest::{escape_string_json, JsonFormat, YamlStreamFormat},
IStr, ObjValue, Result, Val,
};
+pub use python::{PythonFormat, PythonVarsFormat};
pub use toml::TomlFormat;
pub use yaml::YamlFormat;
+pub use xml::XmlJsonmlFormat;
#[builtin]
pub fn builtin_escape_string_json(str_: IStr) -> Result<String> {
@@ -17,51 +21,149 @@
#[builtin]
pub fn builtin_manifest_json_ex(
value: Val,
- indent: IStr,
+ indent: String,
newline: Option<IStr>,
key_val_sep: Option<IStr>,
- #[cfg(feature = "exp-preserve-order")] preserve_order: Option<bool>,
+
+ #[default(false)]
+ #[cfg(feature = "exp-preserve-order")]
+ preserve_order: bool,
) -> Result<String> {
let newline = newline.as_deref().unwrap_or("\n");
let key_val_sep = key_val_sep.as_deref().unwrap_or(": ");
value.manifest(JsonFormat::std_to_json(
- indent.to_string(),
+ indent,
newline,
key_val_sep,
#[cfg(feature = "exp-preserve-order")]
- preserve_order.unwrap_or(false),
+ preserve_order,
+ ))
+}
+
+#[builtin]
+pub fn builtin_manifest_json(
+ value: Val,
+
+ #[default(false)]
+ #[cfg(feature = "exp-preserve-order")]
+ preserve_order: bool,
+) -> Result<String> {
+ builtin_manifest_json_ex(
+ value,
+ " ".to_owned(),
+ None,
+ None,
+ #[cfg(feature = "exp-preserve-order")]
+ preserve_order,
+ )
+}
+
+#[builtin]
+pub fn builtin_manifest_json_minified(
+ value: Val,
+
+ #[default(false)]
+ #[cfg(feature = "exp-preserve-order")]
+ preserve_order: bool,
+) -> Result<String> {
+ value.manifest(JsonFormat::minify(
+ #[cfg(feature = "exp-preserve-order")]
+ preserve_order,
))
}
#[builtin]
pub fn builtin_manifest_yaml_doc(
value: Val,
- indent_array_in_object: Option<bool>,
- quote_keys: Option<bool>,
- #[cfg(feature = "exp-preserve-order")] preserve_order: Option<bool>,
+ #[default(false)] indent_array_in_object: bool,
+ #[default(true)] quote_keys: bool,
+
+ #[default(false)]
+ #[cfg(feature = "exp-preserve-order")]
+ preserve_order: bool,
) -> Result<String> {
value.manifest(YamlFormat::std_to_yaml(
- indent_array_in_object.unwrap_or(false),
- quote_keys.unwrap_or(true),
+ indent_array_in_object,
+ quote_keys,
#[cfg(feature = "exp-preserve-order")]
- preserve_order.unwrap_or(false),
+ preserve_order,
+ ))
+}
+
+#[builtin]
+pub fn builtin_manifest_yaml_stream(
+ value: Val,
+ #[default(false)] indent_array_in_object: bool,
+ #[default(true)] c_document_end: bool,
+ #[default(true)] quote_keys: bool,
+
+ #[default(false)]
+ #[cfg(feature = "exp-preserve-order")]
+ preserve_order: bool,
+) -> Result<String> {
+ value.manifest(YamlStreamFormat::std_yaml_stream(
+ YamlFormat::std_to_yaml(
+ indent_array_in_object,
+ quote_keys,
+ #[cfg(feature = "exp-preserve-order")]
+ preserve_order,
+ ),
+ c_document_end,
))
}
#[builtin]
pub fn builtin_manifest_toml_ex(
value: ObjValue,
- indent: IStr,
- #[cfg(feature = "exp-preserve-order")] preserve_order: Option<bool>,
+ indent: String,
+
+ #[default(false)]
+ #[cfg(feature = "exp-preserve-order")]
+ preserve_order: bool,
) -> Result<String> {
Val::Obj(value).manifest(TomlFormat::std_to_toml(
- indent.to_string(),
+ indent,
#[cfg(feature = "exp-preserve-order")]
- preserve_order.unwrap_or(false),
+ preserve_order,
))
}
#[builtin]
+pub fn builtin_manifest_toml(
+ value: ObjValue,
+
+ #[default(false)]
+ #[cfg(feature = "exp-preserve-order")]
+ preserve_order: bool,
+) -> Result<String> {
+ builtin_manifest_toml_ex(
+ value,
+ " ".to_owned(),
+ #[cfg(feature = "exp-preserve-order")]
+ preserve_order,
+ )
+}
+
+#[builtin]
pub fn builtin_to_string(a: Val) -> Result<IStr> {
a.to_string()
}
+
+#[builtin]
+pub fn builtin_manifest_python(v: Val) -> Result<String> {
+ v.manifest(PythonFormat {})
+}
+#[builtin]
+pub fn builtin_manifest_python_vars(v: Val) -> Result<String> {
+ v.manifest(PythonVarsFormat {})
+}
+
+#[builtin]
+pub fn builtin_escape_string_xml(str_: String) -> String {
+ xml::escape_string_xml(str_.as_str())
+}
+
+#[builtin]
+pub fn builtin_manifest_xml_jsonml(value: Val) -> Result<String> {
+ value.manifest(XmlJsonmlFormat::std_to_xml())
+}
crates/jrsonnet-stdlib/src/manifest/python.rsdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-stdlib/src/manifest/python.rs
@@ -0,0 +1,87 @@
+use jrsonnet_evaluator::{
+ bail,
+ manifest::{escape_string_json_buf, ManifestFormat, ToStringFormat},
+ Result, Val,
+};
+
+pub struct PythonFormat {
+ #[cfg(feature = "exp-preserve-order")]
+ preserve_order: bool,
+}
+
+impl ManifestFormat for PythonFormat {
+ fn manifest_buf(&self, val: Val, buf: &mut String) -> Result<()> {
+ match val {
+ Val::Bool(true) => buf.push_str("True"),
+ Val::Bool(false) => buf.push_str("False"),
+ Val::Null => buf.push_str("None"),
+ Val::Str(s) => escape_string_json_buf(&s.to_string(), buf),
+ Val::Num(_) => ToStringFormat.manifest_buf(val, buf)?,
+ Val::Arr(arr) => {
+ buf.push('[');
+ for (i, el) in arr.iter().enumerate() {
+ let el = el?;
+ if i != 0 {
+ buf.push_str(", ");
+ }
+ self.manifest_buf(el, buf)?;
+ }
+ buf.push(']');
+ }
+ Val::Obj(obj) => {
+ obj.run_assertions()?;
+ buf.push('{');
+ let fields = obj.fields(
+ #[cfg(feature = "exp-preserve-order")]
+ self.preserve_order,
+ );
+ for (i, field) in fields.into_iter().enumerate() {
+ if i != 0 {
+ buf.push_str(", ");
+ }
+ escape_string_json_buf(&field, buf);
+ buf.push_str(": ");
+ let value = obj.get(field)?.expect("field exists");
+ self.manifest_buf(value, buf)?;
+ }
+ buf.push('}');
+ }
+ Val::Func(_) => bail!("tried to manifest function"),
+ }
+ Ok(())
+ }
+}
+
+pub struct PythonVarsFormat {
+ #[cfg(feature = "exp-preserve-order")]
+ preserve_order: bool,
+}
+
+impl PythonVarsFormat {}
+
+impl ManifestFormat for PythonVarsFormat {
+ fn manifest_buf(&self, val: Val, buf: &mut String) -> Result<()> {
+ let inner = PythonFormat {
+ #[cfg(feature = "exp-preserve-order")]
+ preserve_order: self.preserve_order,
+ };
+ let Val::Obj(obj) = val else {
+ bail!("python vars root should be object");
+ };
+ obj.run_assertions()?;
+
+ let fields = obj.fields(
+ #[cfg(feature = "exp-preserve-order")]
+ self.preserve_order,
+ );
+
+ for field in fields {
+ // Yep, no escaping
+ buf.push_str(&field);
+ buf.push_str(" = ");
+ inner.manifest_buf(obj.get(field)?.expect("field exists"), buf)?;
+ buf.push('\n');
+ }
+ Ok(())
+ }
+}
crates/jrsonnet-stdlib/src/manifest/xml.rsdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-stdlib/src/manifest/xml.rs
@@ -0,0 +1,173 @@
+use jrsonnet_evaluator::{
+ bail,
+ manifest::{ManifestFormat, ToStringFormat},
+ typed::{ComplexValType, Either4, Typed, ValType},
+ val::{ArrValue, IndexableVal},
+ Either, ObjValue, Result, ResultExt, Val,
+};
+
+pub struct XmlJsonmlFormat {
+ force_closing: bool,
+}
+impl XmlJsonmlFormat {
+ pub fn std_to_xml() -> Self {
+ Self {
+ force_closing: true,
+ }
+ }
+ pub fn cli() -> Self {
+ Self {
+ force_closing: false,
+ }
+ }
+}
+
+enum JSONMLValue {
+ Tag {
+ tag: String,
+ attrs: ObjValue,
+ children: Vec<JSONMLValue>,
+ },
+ String(String),
+}
+impl Typed for JSONMLValue {
+ const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Arr);
+
+ fn into_untyped(_typed: Self) -> Result<Val> {
+ unreachable!("not used, reserved for parseXML?")
+ }
+
+ fn from_untyped(untyped: Val) -> Result<Self> {
+ let Val::Arr(arr) = untyped else {
+ if let Val::Str(s) = untyped {
+ return Ok(Self::String(s.to_string()));
+ };
+ bail!("expected JSONML value (an array or string)");
+ };
+ if arr.len() < 1 {
+ bail!("JSONML value should have tag");
+ };
+ let tag = String::from_untyped(
+ arr.get(0)
+ .with_description(|| "getting JSONML tag")?
+ .expect("length checked"),
+ )?;
+ let (has_attrs, attrs) = if arr.len() >= 2 {
+ let maybe_attrs = arr
+ .get(1)
+ .with_description(|| "getting JSONML attrs")?
+ .expect("length checked");
+ if let Val::Obj(attrs) = maybe_attrs {
+ (true, attrs)
+ } else {
+ (false, ObjValue::new_empty())
+ }
+ } else {
+ (false, ObjValue::new_empty())
+ };
+ Ok(Self::Tag {
+ tag,
+ attrs,
+ children: Typed::from_untyped(Val::Arr(arr.slice(
+ Some(if has_attrs { 2 } else { 1 }),
+ None,
+ None,
+ )))?,
+ })
+ }
+}
+
+impl ManifestFormat for XmlJsonmlFormat {
+ fn manifest_buf(&self, val: Val, buf: &mut String) -> Result<()> {
+ let val = JSONMLValue::from_untyped(val).with_description(|| "parsing JSONML value")?;
+ manifest_jsonml(&val, buf, self)
+ }
+}
+
+fn manifest_jsonml(v: &JSONMLValue, buf: &mut String, opts: &XmlJsonmlFormat) -> Result<()> {
+ match v {
+ JSONMLValue::Tag {
+ tag,
+ attrs,
+ children,
+ } => {
+ let has_children = !children.is_empty();
+ buf.push('<');
+ buf.push_str(&tag);
+ attrs.run_assertions()?;
+ for (key, value) in attrs.iter() {
+ buf.push(' ');
+ buf.push_str(&key);
+ buf.push('=');
+ buf.push('"');
+ let value = value?;
+ let value = if let Val::Str(s) = value {
+ s.to_string()
+ } else {
+ ToStringFormat.manifest(value)?
+ };
+ escape_string_xml_buf(&value, buf);
+ buf.push('"');
+ }
+ if !has_children && !opts.force_closing {
+ buf.push('/');
+ }
+ buf.push('>');
+ for child in children {
+ manifest_jsonml(&child, buf, opts)?;
+ }
+ if has_children || opts.force_closing {
+ buf.push('<');
+ buf.push('/');
+ buf.push_str(&tag);
+ buf.push('>');
+ }
+ Ok(())
+ }
+ JSONMLValue::String(s) => {
+ escape_string_xml_buf(s, buf);
+ Ok(())
+ }
+ }
+}
+
+pub fn escape_string_xml(str: &str) -> String {
+ let mut out = String::new();
+ escape_string_xml_buf(str, &mut out);
+ out
+}
+
+fn escape_string_xml_buf(str: &str, out: &mut String) {
+ if str.is_empty() {
+ return;
+ }
+ let mut remaining = str;
+
+ let mut found = false;
+ while let Some(position) = remaining
+ .bytes()
+ .position(|c| matches!(c, b'<' | b'>' | b'&' | b'"' | b'\''))
+ {
+ found = true;
+
+ let (plain, rem) = remaining.split_at(position);
+ out.push_str(plain);
+
+ out.push_str(match rem.as_bytes()[0] {
+ b'<' => "<",
+ b'>' => ">",
+ b'&' => "&",
+ b'"' => """,
+ b'\'' => "'",
+ _ => unreachable!("position() searches for those matches"),
+ });
+
+ remaining = &rem[1..];
+ }
+ if !found {
+ // No match - no escapes required
+ out.push_str(&str);
+ return;
+ }
+ out.push_str(&remaining);
+}
crates/jrsonnet-stdlib/src/math.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/math.rs
+++ b/crates/jrsonnet-stdlib/src/math.rs
@@ -24,6 +24,12 @@
a.min(b)
}
+#[allow(non_snake_case)]
+#[builtin]
+pub fn builtin_clamp(x: f64, minVal: f64, maxVal: f64) -> f64 {
+ x.clamp(minVal, maxVal)
+}
+
#[builtin]
pub fn builtin_sum(arr: Vec<f64>) -> f64 {
arr.iter().sum()
crates/jrsonnet-stdlib/src/misc.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/misc.rs
+++ b/crates/jrsonnet-stdlib/src/misc.rs
@@ -23,6 +23,30 @@
}
}
+#[builtin]
+pub fn builtin_get(
+ o: ObjValue,
+ f: IStr,
+ default: Option<Thunk<Val>>,
+ #[default(true)]
+ inc_hidden: bool,
+) -> Result<Val> {
+ let do_default = move || {
+ let Some(default) = default else {
+ return Ok(Val::Null);
+ };
+ default.evaluate()
+ };
+ // Happy path for invisible fields
+ if !inc_hidden && !o.has_field_ex(f.clone(), false) {
+ return do_default();
+ }
+ let Some(v) = o.get(f)? else {
+ return do_default();
+ };
+ Ok(v)
+}
+
#[builtin(fields(
settings: Rc<RefCell<Settings>>,
))]
crates/jrsonnet-stdlib/src/objects.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/objects.rs
+++ b/crates/jrsonnet-stdlib/src/objects.rs
@@ -8,10 +8,11 @@
pub fn builtin_object_fields_ex(
obj: ObjValue,
hidden: bool,
- #[cfg(feature = "exp-preserve-order")] preserve_order: Option<bool>,
-) -> Vec<Val> {
+
+ #[default(false)]
#[cfg(feature = "exp-preserve-order")]
- let preserve_order = preserve_order.unwrap_or(false);
+ preserve_order: bool,
+) -> Vec<Val> {
let out = obj.fields_ex(
hidden,
#[cfg(feature = "exp-preserve-order")]
@@ -23,7 +24,10 @@
#[builtin]
pub fn builtin_object_fields(
o: ObjValue,
- #[cfg(feature = "exp-preserve-order")] preserve_order: Option<bool>,
+
+ #[default(false)]
+ #[cfg(feature = "exp-preserve-order")]
+ preserve_order: bool,
) -> Vec<Val> {
builtin_object_fields_ex(
o,
@@ -36,7 +40,10 @@
#[builtin]
pub fn builtin_object_fields_all(
o: ObjValue,
- #[cfg(feature = "exp-preserve-order")] preserve_order: Option<bool>,
+
+ #[default(false)]
+ #[cfg(feature = "exp-preserve-order")]
+ preserve_order: bool,
) -> Vec<Val> {
builtin_object_fields_ex(
o,
@@ -49,10 +56,9 @@
pub fn builtin_object_values_ex(
o: ObjValue,
include_hidden: bool,
- #[cfg(feature = "exp-preserve-order")] preserve_order: Option<bool>,
+
+ #[cfg(feature = "exp-preserve-order")] preserve_order: bool,
) -> ArrValue {
- #[cfg(feature = "exp-preserve-order")]
- let preserve_order = preserve_order.unwrap_or(false);
o.values_ex(
include_hidden,
#[cfg(feature = "exp-preserve-order")]
@@ -62,7 +68,10 @@
#[builtin]
pub fn builtin_object_values(
o: ObjValue,
- #[cfg(feature = "exp-preserve-order")] preserve_order: Option<bool>,
+
+ #[default(false)]
+ #[cfg(feature = "exp-preserve-order")]
+ preserve_order: bool,
) -> ArrValue {
builtin_object_values_ex(
o,
@@ -74,7 +83,10 @@
#[builtin]
pub fn builtin_object_values_all(
o: ObjValue,
- #[cfg(feature = "exp-preserve-order")] preserve_order: Option<bool>,
+
+ #[default(false)]
+ #[cfg(feature = "exp-preserve-order")]
+ preserve_order: bool,
) -> ArrValue {
builtin_object_values_ex(
o,
@@ -87,10 +99,8 @@
pub fn builtin_object_keys_values_ex(
o: ObjValue,
include_hidden: bool,
- #[cfg(feature = "exp-preserve-order")] preserve_order: Option<bool>,
+ #[cfg(feature = "exp-preserve-order")] preserve_order: bool,
) -> ArrValue {
- #[cfg(feature = "exp-preserve-order")]
- let preserve_order = preserve_order.unwrap_or(false);
o.key_values_ex(
include_hidden,
#[cfg(feature = "exp-preserve-order")]
@@ -100,7 +110,10 @@
#[builtin]
pub fn builtin_object_keys_values(
o: ObjValue,
- #[cfg(feature = "exp-preserve-order")] preserve_order: Option<bool>,
+
+ #[default(false)]
+ #[cfg(feature = "exp-preserve-order")]
+ preserve_order: bool,
) -> ArrValue {
builtin_object_keys_values_ex(
o,
@@ -112,7 +125,10 @@
#[builtin]
pub fn builtin_object_keys_values_all(
o: ObjValue,
- #[cfg(feature = "exp-preserve-order")] preserve_order: Option<bool>,
+
+ #[default(false)]
+ #[cfg(feature = "exp-preserve-order")]
+ preserve_order: bool,
) -> ArrValue {
builtin_object_keys_values_ex(
o,
@@ -141,12 +157,13 @@
pub fn builtin_object_remove_key(
obj: ObjValue,
key: IStr,
+
// Standard implementation uses std.objectFields without such argument, we can't
// assume order preservation should always be enabled/disabled
- #[cfg(feature = "exp-preserve-order")] preserve_order: Option<bool>,
+ #[default(false)]
+ #[cfg(feature = "exp-preserve-order")]
+ preserve_order: bool,
) -> ObjValue {
- #[cfg(feature = "exp-preserve-order")]
- let preserve_order = preserve_order.unwrap_or(false);
let mut new_obj = ObjValueBuilder::with_capacity(obj.len() - 1);
for (k, v) in obj.iter(
#[cfg(feature = "exp-preserve-order")]
crates/jrsonnet-stdlib/src/sort.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/sort.rs
+++ b/crates/jrsonnet-stdlib/src/sort.rs
@@ -139,8 +139,12 @@
}
#[builtin]
-pub fn builtin_sort(arr: ArrValue, keyF: Option<FuncVal>) -> Result<ArrValue> {
- super::sort::sort(arr, keyF.unwrap_or_else(FuncVal::identity))
+pub fn builtin_sort(
+ arr: ArrValue,
+
+ #[default(FuncVal::identity())] keyF: FuncVal,
+) -> Result<ArrValue> {
+ super::sort::sort(arr, keyF)
}
fn uniq_identity(arr: Vec<Val>) -> Result<Vec<Val>> {
@@ -174,11 +178,14 @@
#[builtin]
#[allow(non_snake_case)]
-pub fn builtin_uniq(arr: ArrValue, keyF: Option<FuncVal>) -> Result<ArrValue> {
+pub fn builtin_uniq(
+ arr: ArrValue,
+
+ #[default(FuncVal::identity())] keyF: FuncVal,
+) -> Result<ArrValue> {
if arr.len() <= 1 {
return Ok(arr);
}
- let keyF = keyF.unwrap_or(FuncVal::identity());
if keyF.is_identity() {
Ok(ArrValue::eager(uniq_identity(
arr.iter().collect::<Result<Vec<Val>>>()?,
@@ -190,11 +197,14 @@
#[builtin]
#[allow(non_snake_case)]
-pub fn builtin_set(arr: ArrValue, keyF: Option<FuncVal>) -> Result<ArrValue> {
+pub fn builtin_set(
+ arr: ArrValue,
+
+ #[default(FuncVal::identity())] keyF: FuncVal,
+) -> Result<ArrValue> {
if arr.len() <= 1 {
return Ok(arr);
}
- let keyF = keyF.unwrap_or(FuncVal::identity());
if keyF.is_identity() {
let arr = arr.iter().collect::<Result<Vec<Val>>>()?;
let arr = sort_identity(arr)?;
crates/jrsonnet-stdlib/src/std.jsonnetdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/std.jsonnet
+++ b/crates/jrsonnet-stdlib/src/std.jsonnet
@@ -1,6 +1,5 @@
{
local std = self,
- local id = std.id,
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',
@@ -19,8 +18,6 @@
stripChars(str, chars)::
std.lstripChars(std.rstripChars(str, chars), chars),
-
- split(str, c):: std.splitLimit(str, c, -1),
mapWithIndex(func, arr)::
if !std.isFunction(func) then
@@ -55,11 +52,6 @@
else
error 'Assertion failed. ' + a + ' != ' + b,
- clamp(x, minVal, maxVal)::
- if x < minVal then minVal
- else if x > maxVal then maxVal
- else x,
-
manifestIni(ini)::
local body_lines(body) =
std.join([], [
@@ -79,98 +71,7 @@
for k in std.objectFields(ini.sections)
];
std.join('\n', main_body + std.flattenArrays(all_sections) + ['']),
-
- manifestToml(value):: std.manifestTomlEx(value, ' '),
-
- escapeStringPython(str)::
- std.escapeStringJson(str),
-
- escapeStringBash(str_)::
- local str = std.toString(str_);
- local trans(ch) =
- if ch == "'" then
- "'\"'\"'"
- else
- ch;
- "'%s'" % std.join('', [trans(ch) for ch in std.stringChars(str)]),
-
- escapeStringDollars(str_)::
- local str = std.toString(str_);
- local trans(ch) =
- if ch == '$' then
- '$$'
- else
- ch;
- std.foldl(function(a, b) a + trans(b), std.stringChars(str), ''),
-
- local xml_escapes = {
- '<': '<',
- '>': '>',
- '&': '&',
- '"': '"',
- "'": ''',
- },
-
- escapeStringXML(str_)::
- local str = std.toString(str_);
- std.join('', [std.get(xml_escapes, ch, ch) for ch in std.stringChars(str)]),
-
- manifestJson(value):: std.manifestJsonEx(value, ' ') tailstrict,
-
- manifestJsonMinified(value):: std.manifestJsonEx(value, '', '', ':'),
-
- manifestYamlStream(value, indent_array_in_object=false, c_document_end=true, quote_keys=true)::
- if !std.isArray(value) then
- error 'manifestYamlStream only takes arrays, got ' + std.type(value)
- else
- '---\n' + std.join(
- '\n---\n', [std.manifestYamlDoc(e, indent_array_in_object, quote_keys) for e in value]
- ) + if c_document_end then '\n...\n' else '\n',
-
- manifestPython(v)::
- if std.isObject(v) then
- local fields = [
- '%s: %s' % [std.escapeStringPython(k), std.manifestPython(v[k])]
- for k in std.objectFields(v)
- ];
- '{%s}' % [std.join(', ', fields)]
- else if std.isArray(v) then
- '[%s]' % [std.join(', ', [std.manifestPython(v2) for v2 in v])]
- else if std.isString(v) then
- '%s' % [std.escapeStringPython(v)]
- else if std.isFunction(v) then
- error 'cannot manifest function'
- else if std.isNumber(v) then
- std.toString(v)
- else if v == true then
- 'True'
- else if v == false then
- 'False'
- else if v == null then
- 'None',
- manifestPythonVars(conf)::
- local vars = ['%s = %s' % [k, std.manifestPython(conf[k])] for k in std.objectFields(conf)];
- std.join('\n', vars + ['']),
-
- manifestXmlJsonml(value)::
- if !std.isArray(value) then
- error 'Expected a JSONML value (an array), got %s' % std.type(value)
- else
- local aux(v) =
- if std.isString(v) then
- v
- else
- local tag = v[0];
- local has_attrs = std.length(v) > 1 && std.isObject(v[1]);
- local attrs = if has_attrs then v[1] else {};
- local children = if has_attrs then v[2:] else v[1:];
- local attrs_str =
- std.join('', [' %s="%s"' % [k, attrs[k]] for k in std.objectFields(attrs)]);
- std.deepJoin(['<', tag, attrs_str, '>', [aux(x) for x in children], '</', tag, '>']);
-
- aux(value),
-
mergePatch(target, patch)::
if std.isObject(patch) then
local target_object =
@@ -194,9 +95,6 @@
}
else
patch,
-
- get(o, f, default=null, inc_hidden=true)::
- if std.objectHasEx(o, f, inc_hidden) then o[f] else default,
resolvePath(f, r)::
local arr = std.split(f, '/');
crates/jrsonnet-stdlib/src/strings.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/strings.rs
+++ b/crates/jrsonnet-stdlib/src/strings.rs
@@ -28,6 +28,20 @@
}
#[builtin]
+pub fn builtin_escape_string_bash(str: String) -> String {
+ const QUOTE: char = '\'';
+ let mut out = str.replace(QUOTE, "'\"'\"'");
+ out.insert(0, QUOTE);
+ out.push(QUOTE);
+ out
+}
+
+#[builtin]
+pub fn builtin_escape_string_dollars(str: String) -> String {
+ str.replace('$', "$$")
+}
+
+#[builtin]
pub fn builtin_is_empty(str: String) -> bool {
str.is_empty()
}
@@ -66,6 +80,12 @@
}
#[builtin]
+pub fn builtin_split(str: IStr, c: IStr) -> ArrValue {
+ use Either2::*;
+ builtin_splitlimit(str, c, B(M1))
+}
+
+#[builtin]
pub fn builtin_ascii_upper(str: IStr) -> String {
str.to_ascii_uppercase()
}