difftreelog
Merge pull request #124 from pawelbeza/missing-std-features
in: master
10 files changed
Cargo.lockdiffbeforeafterboth--- a/Cargo.lock
+++ b/Cargo.lock
@@ -375,7 +375,9 @@
"serde",
"serde_json",
"serde_yaml_with_quirks",
+ "sha1",
"sha2",
+ "sha3",
"structdump",
]
@@ -388,6 +390,15 @@
]
[[package]]
+name = "keccak"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940"
+dependencies = [
+ "cpufeatures",
+]
+
+[[package]]
name = "libc"
version = "0.2.139"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -676,6 +687,17 @@
]
[[package]]
+name = "sha1"
+version = "0.10.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+]
+
+[[package]]
name = "sha2"
version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -687,6 +709,16 @@
]
[[package]]
+name = "sha3"
+version = "0.10.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60"
+dependencies = [
+ "digest",
+ "keccak",
+]
+
+[[package]]
name = "smallvec"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
crates/jrsonnet-stdlib/Cargo.tomldiffbeforeafterboth--- a/crates/jrsonnet-stdlib/Cargo.toml
+++ b/crates/jrsonnet-stdlib/Cargo.toml
@@ -32,8 +32,12 @@
# std.md5
md5 = "0.7.0"
+# std.sha1
+sha1 = "0.10.5"
# std.sha256, std.sha512
sha2 = "0.10.6"
+# std.sha3
+sha3 = "0.10.8"
# std.base64
base64 = "0.21.0"
# std.parseJson
crates/jrsonnet-stdlib/src/arrays.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/arrays.rs
+++ b/crates/jrsonnet-stdlib/src/arrays.rs
@@ -253,3 +253,26 @@
}
Ok(Val::Num(arr.iter().sum::<f64>() / (arr.len() as f64)))
}
+
+#[builtin]
+pub fn builtin_remove_at(
+ arr: ArrValue,
+ index: usize,
+) -> Result<ArrValue> {
+ let newArrLeft = arr.clone().slice(None, Some(index), None);
+ let newArrRight = arr.clone().slice(Some(index + 1), None, None);
+ return Ok(ArrValue::extended(
+ newArrLeft.unwrap_or(ArrValue::empty()),
+ newArrRight.unwrap_or(ArrValue::empty()))
+ );
+}
+
+#[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)
+ }
+ }
+ Ok(arr)
+}
crates/jrsonnet-stdlib/src/hash.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/hash.rs
+++ b/crates/jrsonnet-stdlib/src/hash.rs
@@ -16,3 +16,15 @@
use sha2::digest::Digest;
format!("{:x}", sha2::Sha512::digest(s.as_bytes()))
}
+
+#[builtin]
+pub fn builtin_sha1(s: IStr) -> String {
+ use sha1::digest::Digest;
+ format!("{:x}", sha1::Sha1::digest(s.as_bytes()))
+}
+
+#[builtin]
+pub fn builtin_sha3(s: IStr) -> String {
+ use sha3::digest::Digest;
+ format!("{:x}", sha3::Sha3_512::digest(s.as_bytes()))
+}
crates/jrsonnet-stdlib/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/lib.rs
+++ b/crates/jrsonnet-stdlib/src/lib.rs
@@ -83,6 +83,8 @@
("contains", builtin_member::INST),
("count", builtin_count::INST),
("avg", builtin_avg::INST),
+ ("removeAt", builtin_remove_at::INST),
+ ("remove", builtin_remove::INST),
// Math
("abs", builtin_abs::INST),
("sign", builtin_sign::INST),
@@ -104,11 +106,17 @@
("exp", builtin_exp::INST),
("mantissa", builtin_mantissa::INST),
("exponent", builtin_exponent::INST),
+ ("round", builtin_round::INST),
+ ("isEven", builtin_is_even::INST),
+ ("isOdd", builtin_is_odd::INST),
+ ("isInteger", builtin_is_integer::INST),
+ ("isDecimal", builtin_is_decimal::INST),
// Operator
("mod", builtin_mod::INST),
("primitiveEquals", builtin_primitive_equals::INST),
("equals", builtin_equals::INST),
("xor", builtin_xor::INST),
+ ("xnor", builtin_xnor::INST),
("format", builtin_format::INST),
// Sort
("sort", builtin_sort::INST),
@@ -118,8 +126,10 @@
("maxArray", builtin_max_array::INST),
// Hash
("md5", builtin_md5::INST),
+ ("sha1", builtin_sha1::INST),
("sha256", builtin_sha256::INST),
("sha512", builtin_sha512::INST),
+ ("sha3", builtin_sha3::INST),
// Encoding
("encodeUTF8", builtin_encode_utf8::INST),
("decodeUTF8", builtin_decode_utf8::INST),
@@ -129,6 +139,7 @@
// Objects
("objectFieldsEx", builtin_object_fields_ex::INST),
("objectHasEx", builtin_object_has_ex::INST),
+ ("objectRemoveKey", builtin_object_remove_key::INST),
// Manifest
("escapeStringJson", builtin_escape_string_json::INST),
("manifestJsonEx", builtin_manifest_json_ex::INST),
@@ -142,6 +153,8 @@
("substr", builtin_substr::INST),
("char", builtin_char::INST),
("strReplace", builtin_str_replace::INST),
+ ("isEmpty", builtin_is_empty::INST),
+ ("equalsIgnoreCase", builtin_equals_ignore_case::INST),
("splitLimit", builtin_splitlimit::INST),
("asciiUpper", builtin_ascii_upper::INST),
("asciiLower", builtin_ascii_lower::INST),
crates/jrsonnet-stdlib/src/math.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/math.rs
+++ b/crates/jrsonnet-stdlib/src/math.rs
@@ -114,3 +114,28 @@
pub fn builtin_exponent(x: f64) -> i16 {
frexp(x).1
}
+
+#[builtin]
+pub fn builtin_round(x: f64) -> f64 {
+ x.round()
+}
+
+#[builtin]
+pub fn builtin_is_even(x: f64) -> bool {
+ builtin_round(x) % 2.0 == 0.0
+}
+
+#[builtin]
+pub fn builtin_is_odd(x: f64) -> bool {
+ builtin_round(x) % 2.0 == 1.0
+}
+
+#[builtin]
+pub fn builtin_is_integer(x: f64) -> bool {
+ builtin_round(x) == x
+}
+
+#[builtin]
+pub fn builtin_is_decimal(x: f64) -> bool {
+ builtin_round(x) != x
+}
crates/jrsonnet-stdlib/src/objects.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/objects.rs
+++ b/crates/jrsonnet-stdlib/src/objects.rs
@@ -1,9 +1,10 @@
use jrsonnet_evaluator::{
function::builtin,
val::{StrValue, Val},
- IStr, ObjValue,
+ IStr, ObjValue, ObjValueBuilder,
};
+
#[builtin]
pub fn builtin_object_fields_ex(
obj: ObjValue,
@@ -27,3 +28,16 @@
pub fn builtin_object_has_ex(obj: ObjValue, fname: IStr, hidden: bool) -> bool {
obj.has_field_ex(fname, hidden)
}
+
+#[builtin]
+pub fn builtin_object_remove_key(obj: ObjValue, key: IStr) -> ObjValue {
+ let mut new_obj = ObjValueBuilder::with_capacity(obj.len() - 1);
+ for (k, v) in obj.iter() {
+ if k == key {
+ continue
+ }
+ new_obj.member(k).value_unchecked(v.unwrap())
+ }
+
+ new_obj.build()
+}
crates/jrsonnet-stdlib/src/operator.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/operator.rs
+++ b/crates/jrsonnet-stdlib/src/operator.rs
@@ -39,6 +39,11 @@
}
#[builtin]
+pub fn builtin_xnor(x: bool, y: bool) -> bool {
+ x == y
+}
+
+#[builtin]
pub fn builtin_format(str: IStr, vals: Val) -> Result<String> {
std_format(&str, vals)
}
crates/jrsonnet-stdlib/src/std.jsonnetdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/std.jsonnet
+++ b/crates/jrsonnet-stdlib/src/std.jsonnet
@@ -274,6 +274,12 @@
objectValuesAll(o)::
[o[k] for k in std.objectFieldsAll(o)],
+ objectKeysValues(o)::
+ [{ key: k, value: o[k] } for k in std.objectFields(o)],
+
+ objectKeysValuesAll(o)::
+ [{ key: k, value: o[k] } for k in std.objectFieldsAll(o)],
+
resolvePath(f, r)::
local arr = std.split(f, '/');
std.join('/', std.makeArray(std.length(arr) - 1, function(i) arr[i]) + [r]),
crates/jrsonnet-stdlib/src/strings.rsdiffbeforeafterboth1use jrsonnet_evaluator::{2 error::{ErrorKind::*, Result},3 function::builtin,4 throw,5 typed::{Either2, M1},6 val::{ArrValue, StrValue},7 Either, IStr, Val,8};910#[builtin]11pub const fn builtin_codepoint(str: char) -> u32 {12 str as u3213}1415#[builtin]16pub fn builtin_substr(str: IStr, from: usize, len: usize) -> String {17 str.chars().skip(from).take(len).collect()18}1920#[builtin]21pub fn builtin_char(n: u32) -> Result<char> {22 Ok(std::char::from_u32(n).ok_or_else(|| InvalidUnicodeCodepointGot(n))?)23}2425#[builtin]26pub fn builtin_str_replace(str: String, from: IStr, to: IStr) -> String {27 str.replace(&from as &str, &to as &str)28}2930#[builtin]31pub fn builtin_splitlimit(str: IStr, c: IStr, maxsplits: Either![usize, M1]) -> ArrValue {32 use Either2::*;33 match maxsplits {34 A(n) => str35 .splitn(n + 1, &c as &str)36 .map(|s| Val::Str(StrValue::Flat(s.into())))37 .collect(),38 B(_) => str39 .split(&c as &str)40 .map(|s| Val::Str(StrValue::Flat(s.into())))41 .collect(),42 }43}4445#[builtin]46pub fn builtin_ascii_upper(str: IStr) -> String {47 str.to_ascii_uppercase()48}4950#[builtin]51pub fn builtin_ascii_lower(str: IStr) -> String {52 str.to_ascii_lowercase()53}5455#[builtin]56pub fn builtin_find_substr(pat: IStr, str: IStr) -> ArrValue {57 if pat.is_empty() || str.is_empty() || pat.len() > str.len() {58 return ArrValue::empty();59 }6061 let str = str.as_str();62 let pat = pat.as_bytes();63 let strb = str.as_bytes();6465 let max_pos = str.len() - pat.len();6667 let mut out: Vec<Val> = Vec::new();68 for (ch_idx, (i, _)) in str69 .char_indices()70 .take_while(|(i, _)| i <= &max_pos)71 .enumerate()72 {73 if &strb[i..i + pat.len()] == pat {74 out.push(Val::Num(ch_idx as f64))75 }76 }77 out.into()78}7980#[builtin]81pub fn builtin_parse_int(str: IStr) -> Result<f64> {82 if let Some(raw) = str.strip_prefix('-') {83 if raw.is_empty() {84 throw!("integer only consists of a minus")85 }8687 parse_nat::<10>(raw).map(|value| -value)88 } else {89 if str.is_empty() {90 throw!("empty integer")91 }9293 parse_nat::<10>(str.as_str())94 }95}9697#[builtin]98pub fn builtin_parse_octal(str: IStr) -> Result<f64> {99 if str.is_empty() {100 throw!("empty octal integer");101 }102103 parse_nat::<8>(str.as_str())104}105106#[builtin]107pub fn builtin_parse_hex(str: IStr) -> Result<f64> {108 if str.is_empty() {109 throw!("empty hexadecimal integer");110 }111112 parse_nat::<16>(str.as_str())113}114115fn parse_nat<const BASE: u32>(raw: &str) -> Result<f64> {116 debug_assert!(117 1 <= BASE && BASE <= 16,118 "integer base should be between 1 and 16"119 );120121 const ZERO_CODE: u32 = '0' as u32;122 const UPPER_A_CODE: u32 = 'A' as u32;123 const LOWER_A_CODE: u32 = 'a' as u32;124125 #[inline]126 fn checked_sub_if(condition: bool, lhs: u32, rhs: u32) -> Option<u32> {127 if condition {128 lhs.checked_sub(rhs)129 } else {130 None131 }132 }133134 let base = BASE as f64;135136 raw.chars().try_fold(0f64, |aggregate, digit| {137 let digit = digit as u32;138 let digit = if let Some(digit) = checked_sub_if(BASE > 10, digit, LOWER_A_CODE) {139 digit + 10140 } else if let Some(digit) = checked_sub_if(BASE > 10, digit, UPPER_A_CODE) {141 digit + 10142 } else {143 digit.checked_sub(ZERO_CODE).unwrap_or(BASE)144 };145146 if digit < BASE {147 Ok(base * aggregate + digit as f64)148 } else {149 throw!("{raw:?} is not a base {BASE} integer",);150 }151 })152}153154#[cfg(feature = "exp-bigint")]155#[builtin]156pub fn builtin_bigint(v: Either![f64, IStr]) -> Result<Val> {157 use Either2::*;158 Ok(match v {159 A(a) => Val::BigInt(Box::new((a as i64).into())),160 B(b) => Val::BigInt(Box::new(161 b.as_str()162 .parse()163 .map_err(|e| RuntimeError(format!("bad bigint: {e}").into()))?,164 )),165 })166}167168#[cfg(test)]169mod tests {170 use super::*;171172 #[test]173 fn parse_nat_base_8() {174 assert_eq!(parse_nat::<8>("0").unwrap(), 0.);175 assert_eq!(parse_nat::<8>("5").unwrap(), 5.);176 assert_eq!(parse_nat::<8>("32").unwrap(), 0o32 as f64);177 assert_eq!(parse_nat::<8>("761").unwrap(), 0o761 as f64);178 }179180 #[test]181 fn parse_nat_base_10() {182 assert_eq!(parse_nat::<10>("0").unwrap(), 0.);183 assert_eq!(parse_nat::<10>("3").unwrap(), 3.);184 assert_eq!(parse_nat::<10>("27").unwrap(), 27.);185 assert_eq!(parse_nat::<10>("123").unwrap(), 123.);186 }187188 #[test]189 fn parse_nat_base_16() {190 assert_eq!(parse_nat::<16>("0").unwrap(), 0.);191 assert_eq!(parse_nat::<16>("A").unwrap(), 10.);192 assert_eq!(parse_nat::<16>("a9").unwrap(), 0xA9 as f64);193 assert_eq!(parse_nat::<16>("BbC").unwrap(), 0xBBC as f64);194 }195}1use jrsonnet_evaluator::{2 error::{ErrorKind::*, Result},3 function::builtin,4 throw,5 typed::{Either2, M1},6 val::{ArrValue, StrValue},7 Either, IStr, Val,8};910#[builtin]11pub const fn builtin_codepoint(str: char) -> u32 {12 str as u3213}1415#[builtin]16pub fn builtin_substr(str: IStr, from: usize, len: usize) -> String {17 str.chars().skip(from).take(len).collect()18}1920#[builtin]21pub fn builtin_char(n: u32) -> Result<char> {22 Ok(std::char::from_u32(n).ok_or_else(|| InvalidUnicodeCodepointGot(n))?)23}2425#[builtin]26pub fn builtin_str_replace(str: String, from: IStr, to: IStr) -> String {27 str.replace(&from as &str, &to as &str)28}2930#[builtin]31pub fn builtin_is_empty(str: String) -> bool {32 str.is_empty()33}3435#[builtin]36pub fn builtin_equals_ignore_case(x: String, y: String) -> bool {37 x.to_ascii_lowercase() == y.to_ascii_lowercase()38}3940#[builtin]41pub fn builtin_splitlimit(str: IStr, c: IStr, maxsplits: Either![usize, M1]) -> ArrValue {42 use Either2::*;43 match maxsplits {44 A(n) => str45 .splitn(n + 1, &c as &str)46 .map(|s| Val::Str(StrValue::Flat(s.into())))47 .collect(),48 B(_) => str49 .split(&c as &str)50 .map(|s| Val::Str(StrValue::Flat(s.into())))51 .collect(),52 }53}5455#[builtin]56pub fn builtin_ascii_upper(str: IStr) -> String {57 str.to_ascii_uppercase()58}5960#[builtin]61pub fn builtin_ascii_lower(str: IStr) -> String {62 str.to_ascii_lowercase()63}6465#[builtin]66pub fn builtin_find_substr(pat: IStr, str: IStr) -> ArrValue {67 if pat.is_empty() || str.is_empty() || pat.len() > str.len() {68 return ArrValue::empty();69 }7071 let str = str.as_str();72 let pat = pat.as_bytes();73 let strb = str.as_bytes();7475 let max_pos = str.len() - pat.len();7677 let mut out: Vec<Val> = Vec::new();78 for (ch_idx, (i, _)) in str79 .char_indices()80 .take_while(|(i, _)| i <= &max_pos)81 .enumerate()82 {83 if &strb[i..i + pat.len()] == pat {84 out.push(Val::Num(ch_idx as f64))85 }86 }87 out.into()88}8990#[builtin]91pub fn builtin_parse_int(str: IStr) -> Result<f64> {92 if let Some(raw) = str.strip_prefix('-') {93 if raw.is_empty() {94 throw!("integer only consists of a minus")95 }9697 parse_nat::<10>(raw).map(|value| -value)98 } else {99 if str.is_empty() {100 throw!("empty integer")101 }102103 parse_nat::<10>(str.as_str())104 }105}106107#[builtin]108pub fn builtin_parse_octal(str: IStr) -> Result<f64> {109 if str.is_empty() {110 throw!("empty octal integer");111 }112113 parse_nat::<8>(str.as_str())114}115116#[builtin]117pub fn builtin_parse_hex(str: IStr) -> Result<f64> {118 if str.is_empty() {119 throw!("empty hexadecimal integer");120 }121122 parse_nat::<16>(str.as_str())123}124125fn parse_nat<const BASE: u32>(raw: &str) -> Result<f64> {126 debug_assert!(127 1 <= BASE && BASE <= 16,128 "integer base should be between 1 and 16"129 );130131 const ZERO_CODE: u32 = '0' as u32;132 const UPPER_A_CODE: u32 = 'A' as u32;133 const LOWER_A_CODE: u32 = 'a' as u32;134135 #[inline]136 fn checked_sub_if(condition: bool, lhs: u32, rhs: u32) -> Option<u32> {137 if condition {138 lhs.checked_sub(rhs)139 } else {140 None141 }142 }143144 let base = BASE as f64;145146 raw.chars().try_fold(0f64, |aggregate, digit| {147 let digit = digit as u32;148 let digit = if let Some(digit) = checked_sub_if(BASE > 10, digit, LOWER_A_CODE) {149 digit + 10150 } else if let Some(digit) = checked_sub_if(BASE > 10, digit, UPPER_A_CODE) {151 digit + 10152 } else {153 digit.checked_sub(ZERO_CODE).unwrap_or(BASE)154 };155156 if digit < BASE {157 Ok(base * aggregate + digit as f64)158 } else {159 throw!("{raw:?} is not a base {BASE} integer",);160 }161 })162}163164#[cfg(feature = "exp-bigint")]165#[builtin]166pub fn builtin_bigint(v: Either![f64, IStr]) -> Result<Val> {167 use Either2::*;168 Ok(match v {169 A(a) => Val::BigInt(Box::new((a as i64).into())),170 B(b) => Val::BigInt(Box::new(171 b.as_str()172 .parse()173 .map_err(|e| RuntimeError(format!("bad bigint: {e}").into()))?,174 )),175 })176}177178#[cfg(test)]179mod tests {180 use super::*;181182 #[test]183 fn parse_nat_base_8() {184 assert_eq!(parse_nat::<8>("0").unwrap(), 0.);185 assert_eq!(parse_nat::<8>("5").unwrap(), 5.);186 assert_eq!(parse_nat::<8>("32").unwrap(), 0o32 as f64);187 assert_eq!(parse_nat::<8>("761").unwrap(), 0o761 as f64);188 }189190 #[test]191 fn parse_nat_base_10() {192 assert_eq!(parse_nat::<10>("0").unwrap(), 0.);193 assert_eq!(parse_nat::<10>("3").unwrap(), 3.);194 assert_eq!(parse_nat::<10>("27").unwrap(), 27.);195 assert_eq!(parse_nat::<10>("123").unwrap(), 123.);196 }197198 #[test]199 fn parse_nat_base_16() {200 assert_eq!(parse_nat::<16>("0").unwrap(), 0.);201 assert_eq!(parse_nat::<16>("A").unwrap(), 10.);202 assert_eq!(parse_nat::<16>("a9").unwrap(), 0xA9 as f64);203 assert_eq!(parse_nat::<16>("BbC").unwrap(), 0xBBC as f64);204 }205}