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(test)]155mod tests {156 use super::*;157158 #[test]159 fn parse_nat_base_8() {160 assert_eq!(parse_nat::<8>("0").unwrap(), 0.);161 assert_eq!(parse_nat::<8>("5").unwrap(), 5.);162 assert_eq!(parse_nat::<8>("32").unwrap(), 0o32 as f64);163 assert_eq!(parse_nat::<8>("761").unwrap(), 0o761 as f64);164 }165166 #[test]167 fn parse_nat_base_10() {168 assert_eq!(parse_nat::<10>("0").unwrap(), 0.);169 assert_eq!(parse_nat::<10>("3").unwrap(), 3.);170 assert_eq!(parse_nat::<10>("27").unwrap(), 27.);171 assert_eq!(parse_nat::<10>("123").unwrap(), 123.);172 }173174 #[test]175 fn parse_nat_base_16() {176 assert_eq!(parse_nat::<16>("0").unwrap(), 0.);177 assert_eq!(parse_nat::<16>("A").unwrap(), 10.);178 assert_eq!(parse_nat::<16>("a9").unwrap(), 0xA9 as f64);179 assert_eq!(parse_nat::<16>("BbC").unwrap(), 0xBBC as f64);180 }181}