1use std::{cell::RefCell, rc::Rc};23use jrsonnet_evaluator::{4 bail,5 error::{ErrorKind::*, Result},6 function::{builtin, ArgLike, CallLocation, FuncVal},7 manifest::JsonFormat,8 typed::{Either2, Either4},9 val::{equals, ArrValue},10 Context, Either, IStr, ObjValue, Thunk, Val,11};1213use crate::{extvar_source, Settings};1415#[builtin]16pub fn builtin_length(x: Either![IStr, ArrValue, ObjValue, FuncVal]) -> usize {17 use Either4::*;18 match x {19 A(x) => x.chars().count(),20 B(x) => x.len(),21 C(x) => x.len(),22 D(f) => f.params_len(),23 }24}2526#[builtin(fields(27 settings: Rc<RefCell<Settings>>,28))]29pub fn builtin_ext_var(this: &builtin_ext_var, ctx: Context, x: IStr) -> Result<Val> {30 let ctx = ctx.state().create_default_context(extvar_source(&x, ""));31 this.settings32 .borrow()33 .ext_vars34 .get(&x)35 .cloned()36 .ok_or_else(|| UndefinedExternalVariable(x))?37 .evaluate_arg(ctx, true)?38 .evaluate()39}4041#[builtin(fields(42 settings: Rc<RefCell<Settings>>,43))]44pub fn builtin_native(this: &builtin_native, x: IStr) -> Val {45 this.settings46 .borrow()47 .ext_natives48 .get(&x)49 .cloned()50 .map_or(Val::Null, Val::Func)51}5253#[builtin(fields(54 settings: Rc<RefCell<Settings>>,55))]56pub fn builtin_trace(57 this: &builtin_trace,58 loc: CallLocation,59 str: Val,60 rest: Option<Thunk<Val>>,61) -> Result<Val> {62 this.settings.borrow().trace_printer.print_trace(63 loc,64 match &str {65 Val::Str(s) => s.clone().into_flat(),66 Val::Func(f) => format!("{f:?}").into(),67 v => v.manifest(JsonFormat::debug())?.into(),68 },69 );70 rest.map_or_else(|| Ok(str), |rest| rest.evaluate())71}7273#[allow(clippy::comparison_chain)]74#[builtin]75pub fn builtin_starts_with(a: Either![IStr, ArrValue], b: Either![IStr, ArrValue]) -> Result<bool> {76 Ok(match (a, b) {77 (Either2::A(a), Either2::A(b)) => a.starts_with(b.as_str()),78 (Either2::B(a), Either2::B(b)) => {79 if b.len() > a.len() {80 return Ok(false);81 } else if b.len() == a.len() {82 return equals(&Val::Arr(a), &Val::Arr(b));83 }84 for (a, b) in a.iter().take(b.len()).zip(b.iter()) {85 let a = a?;86 let b = b?;87 if !equals(&a, &b)? {88 return Ok(false);89 }90 }91 true92 }93 _ => bail!("both arguments should be of the same type"),94 })95}9697#[allow(clippy::comparison_chain)]98#[builtin]99pub fn builtin_ends_with(a: Either![IStr, ArrValue], b: Either![IStr, ArrValue]) -> Result<bool> {100 Ok(match (a, b) {101 (Either2::A(a), Either2::A(b)) => a.ends_with(b.as_str()),102 (Either2::B(a), Either2::B(b)) => {103 if b.len() > a.len() {104 return Ok(false);105 } else if b.len() == a.len() {106 return equals(&Val::Arr(a), &Val::Arr(b));107 }108 let a_len = a.len();109 for (a, b) in a.iter().skip(a_len - b.len()).zip(b.iter()) {110 let a = a?;111 let b = b?;112 if !equals(&a, &b)? {113 return Ok(false);114 }115 }116 true117 }118 _ => bail!("both arguments should be of the same type"),119 })120}