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}