git.delta.rocks / jrsonnet / refs/commits / 5f0f8de9f52f

difftreelog

source

crates/jrsonnet-stdlib/src/strings.rs4.0 KiBsourcehistory
1use jrsonnet_evaluator::{2	error::{ErrorKind::*, Result},3	function::builtin,4	throw,5	typed::{Either2, VecVal, M1},6	val::ArrValue,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(s.into()))38			.collect(),39		B(_) => str.split(&c as &str).map(|s| Val::Str(s.into())).collect(),40	})))41}4243#[builtin]44pub fn builtin_ascii_upper(str: IStr) -> Result<String> {45	Ok(str.to_ascii_uppercase())46}4748#[builtin]49pub fn builtin_ascii_lower(str: IStr) -> Result<String> {50	Ok(str.to_ascii_lowercase())51}5253#[builtin]54pub fn builtin_find_substr(pat: IStr, str: IStr) -> Result<ArrValue> {55	if pat.is_empty() || str.is_empty() || pat.len() > str.len() {56		return Ok(ArrValue::empty());57	}5859	let str = str.as_str();60	let pat = pat.as_bytes();61	let strb = str.as_bytes();6263	let max_pos = str.len() - pat.len();6465	let mut out: Vec<Val> = Vec::new();66	for (ch_idx, (i, _)) in str67		.char_indices()68		.take_while(|(i, _)| i <= &max_pos)69		.enumerate()70	{71		if &strb[i..i + pat.len()] == pat {72			out.push(Val::Num(ch_idx as f64))73		}74	}75	Ok(out.into())76}7778#[builtin]79pub fn builtin_parse_int(raw: IStr) -> Result<f64> {80	if let Some(raw) = raw.strip_prefix('-') {81		if raw.is_empty() {82			throw!("integer only consists of a minus")83		}8485		parse_nat::<10>(raw).map(|value| -value)86	} else {87		if raw.is_empty() {88			throw!("empty integer")89		}9091		parse_nat::<10>(raw.as_str())92	}93}9495#[builtin]96pub fn builtin_parse_octal(raw: IStr) -> Result<f64> {97	if raw.is_empty() {98		throw!("empty octal integer");99	}100101	parse_nat::<8>(raw.as_str())102}103104#[builtin]105pub fn builtin_parse_hex(raw: IStr) -> Result<f64> {106	if raw.is_empty() {107		throw!("empty hexadecimal integer");108	}109110	parse_nat::<16>(raw.as_str())111}112113fn parse_nat<const BASE: u32>(raw: &str) -> Result<f64> {114	debug_assert!(115		1 <= BASE && BASE <= 16,116		"integer base should be between 1 and 16"117	);118119	const ZERO_CODE: u32 = '0' as u32;120	const UPPER_A_CODE: u32 = 'A' as u32;121	const LOWER_A_CODE: u32 = 'a' as u32;122123	#[inline]124	fn checked_sub_if(condition: bool, lhs: u32, rhs: u32) -> Option<u32> {125		if condition {126			lhs.checked_sub(rhs)127		} else {128			None129		}130	}131132	let base = BASE as f64;133134	raw.chars().try_fold(0f64, |aggregate, digit| {135		let digit = digit as u32;136		let digit = if let Some(digit) = checked_sub_if(BASE > 10, digit, LOWER_A_CODE) {137			digit + 10138		} else if let Some(digit) = checked_sub_if(BASE > 10, digit, UPPER_A_CODE) {139			digit + 10140		} else {141			digit.checked_sub(ZERO_CODE).unwrap_or(BASE)142		};143144		if digit < BASE {145			Ok(base * aggregate + digit as f64)146		} else {147			throw!("{raw:?} is not a base {BASE} integer",);148		}149	})150}151152#[cfg(test)]153mod tests {154	use super::*;155156	#[test]157	fn parse_nat_base_8() {158		assert_eq!(parse_nat::<8>("0").unwrap(), 0.);159		assert_eq!(parse_nat::<8>("5").unwrap(), 5.);160		assert_eq!(parse_nat::<8>("32").unwrap(), 0o32 as f64);161		assert_eq!(parse_nat::<8>("761").unwrap(), 0o761 as f64);162	}163164	#[test]165	fn parse_nat_base_10() {166		assert_eq!(parse_nat::<10>("0").unwrap(), 0.);167		assert_eq!(parse_nat::<10>("3").unwrap(), 3.);168		assert_eq!(parse_nat::<10>("27").unwrap(), 27.);169		assert_eq!(parse_nat::<10>("123").unwrap(), 123.);170	}171172	#[test]173	fn parse_nat_base_16() {174		assert_eq!(parse_nat::<16>("0").unwrap(), 0.);175		assert_eq!(parse_nat::<16>("A").unwrap(), 10.);176		assert_eq!(parse_nat::<16>("a9").unwrap(), 0xA9 as f64);177		assert_eq!(parse_nat::<16>("BbC").unwrap(), 0xBBC as f64);178	}179}