git.delta.rocks / jrsonnet / refs/commits / 2afd5ff0dd7a

difftreelog

source

crates/jrsonnet-stdlib/src/strings.rs4.1 KiBsourcehistory
1use jrsonnet_evaluator::{2	error::{ErrorKind::*, Result},3	function::builtin,4	throw,5	typed::{Either2, VecVal, M1},6	val::{ArrValue, StrValue},7	Either, IStr, Val,8};9use jrsonnet_gcmodule::Cc;1011#[builtin]12pub const fn builtin_codepoint(str: char) -> Result<u32> {13	Ok(str as u32)14}1516#[builtin]17pub fn builtin_substr(str: IStr, from: usize, len: usize) -> Result<String> {18	Ok(str.chars().skip(from).take(len).collect())19}2021#[builtin]22pub fn builtin_char(n: u32) -> Result<char> {23	Ok(std::char::from_u32(n).ok_or_else(|| InvalidUnicodeCodepointGot(n))?)24}2526#[builtin]27pub fn builtin_str_replace(str: String, from: IStr, to: IStr) -> Result<String> {28	Ok(str.replace(&from as &str, &to as &str))29}3031#[builtin]32pub fn builtin_splitlimit(str: IStr, c: IStr, maxsplits: Either![usize, M1]) -> Result<VecVal> {33	use Either2::*;34	Ok(VecVal(Cc::new(match maxsplits {35		A(n) => str36			.splitn(n + 1, &c as &str)37			.map(|s| Val::Str(StrValue::Flat(s.into())))38			.collect(),39		B(_) => str40			.split(&c as &str)41			.map(|s| Val::Str(StrValue::Flat(s.into())))42			.collect(),43	})))44}4546#[builtin]47pub fn builtin_ascii_upper(str: IStr) -> Result<String> {48	Ok(str.to_ascii_uppercase())49}5051#[builtin]52pub fn builtin_ascii_lower(str: IStr) -> Result<String> {53	Ok(str.to_ascii_lowercase())54}5556#[builtin]57pub fn builtin_find_substr(pat: IStr, str: IStr) -> Result<ArrValue> {58	if pat.is_empty() || str.is_empty() || pat.len() > str.len() {59		return Ok(ArrValue::empty());60	}6162	let str = str.as_str();63	let pat = pat.as_bytes();64	let strb = str.as_bytes();6566	let max_pos = str.len() - pat.len();6768	let mut out: Vec<Val> = Vec::new();69	for (ch_idx, (i, _)) in str70		.char_indices()71		.take_while(|(i, _)| i <= &max_pos)72		.enumerate()73	{74		if &strb[i..i + pat.len()] == pat {75			out.push(Val::Num(ch_idx as f64))76		}77	}78	Ok(out.into())79}8081#[builtin]82pub fn builtin_parse_int(str: IStr) -> Result<f64> {83	if let Some(raw) = str.strip_prefix('-') {84		if raw.is_empty() {85			throw!("integer only consists of a minus")86		}8788		parse_nat::<10>(raw).map(|value| -value)89	} else {90		if str.is_empty() {91			throw!("empty integer")92		}9394		parse_nat::<10>(str.as_str())95	}96}9798#[builtin]99pub fn builtin_parse_octal(str: IStr) -> Result<f64> {100	if str.is_empty() {101		throw!("empty octal integer");102	}103104	parse_nat::<8>(str.as_str())105}106107#[builtin]108pub fn builtin_parse_hex(str: IStr) -> Result<f64> {109	if str.is_empty() {110		throw!("empty hexadecimal integer");111	}112113	parse_nat::<16>(str.as_str())114}115116fn parse_nat<const BASE: u32>(raw: &str) -> Result<f64> {117	debug_assert!(118		1 <= BASE && BASE <= 16,119		"integer base should be between 1 and 16"120	);121122	const ZERO_CODE: u32 = '0' as u32;123	const UPPER_A_CODE: u32 = 'A' as u32;124	const LOWER_A_CODE: u32 = 'a' as u32;125126	#[inline]127	fn checked_sub_if(condition: bool, lhs: u32, rhs: u32) -> Option<u32> {128		if condition {129			lhs.checked_sub(rhs)130		} else {131			None132		}133	}134135	let base = BASE as f64;136137	raw.chars().try_fold(0f64, |aggregate, digit| {138		let digit = digit as u32;139		let digit = if let Some(digit) = checked_sub_if(BASE > 10, digit, LOWER_A_CODE) {140			digit + 10141		} else if let Some(digit) = checked_sub_if(BASE > 10, digit, UPPER_A_CODE) {142			digit + 10143		} else {144			digit.checked_sub(ZERO_CODE).unwrap_or(BASE)145		};146147		if digit < BASE {148			Ok(base * aggregate + digit as f64)149		} else {150			throw!("{raw:?} is not a base {BASE} integer",);151		}152	})153}154155#[cfg(test)]156mod tests {157	use super::*;158159	#[test]160	fn parse_nat_base_8() {161		assert_eq!(parse_nat::<8>("0").unwrap(), 0.);162		assert_eq!(parse_nat::<8>("5").unwrap(), 5.);163		assert_eq!(parse_nat::<8>("32").unwrap(), 0o32 as f64);164		assert_eq!(parse_nat::<8>("761").unwrap(), 0o761 as f64);165	}166167	#[test]168	fn parse_nat_base_10() {169		assert_eq!(parse_nat::<10>("0").unwrap(), 0.);170		assert_eq!(parse_nat::<10>("3").unwrap(), 3.);171		assert_eq!(parse_nat::<10>("27").unwrap(), 27.);172		assert_eq!(parse_nat::<10>("123").unwrap(), 123.);173	}174175	#[test]176	fn parse_nat_base_16() {177		assert_eq!(parse_nat::<16>("0").unwrap(), 0.);178		assert_eq!(parse_nat::<16>("A").unwrap(), 10.);179		assert_eq!(parse_nat::<16>("a9").unwrap(), 0xA9 as f64);180		assert_eq!(parse_nat::<16>("BbC").unwrap(), 0xBBC as f64);181	}182}