git.delta.rocks / jrsonnet / refs/commits / 83c939e4fa0b

difftreelog

feat add std.isEmpty

Paweł Bęza2023-07-11parent: #0319252.patch.diff
in: master
Upstream issue: https://github.com/google/go-jsonnet/pull/678

2 files changed

modifiedcrates/jrsonnet-stdlib/src/lib.rsdiffbeforeafterboth
--- a/crates/jrsonnet-stdlib/src/lib.rs
+++ b/crates/jrsonnet-stdlib/src/lib.rs
@@ -148,6 +148,7 @@
 		("substr", builtin_substr::INST),
 		("char", builtin_char::INST),
 		("strReplace", builtin_str_replace::INST),
+		("isEmpty", builtin_is_empty::INST),
 		("splitLimit", builtin_splitlimit::INST),
 		("asciiUpper", builtin_ascii_upper::INST),
 		("asciiLower", builtin_ascii_lower::INST),
modifiedcrates/jrsonnet-stdlib/src/strings.rsdiffbeforeafterboth
before · crates/jrsonnet-stdlib/src/strings.rs
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_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}