1use std::any::Any;23use jrsonnet_gcmodule::{cc_dyn, Acyclic, Trace, TraceBox};4use jrsonnet_interner::IStr;56use super::{arglike::ArgsLike, parse::parse_builtin_call, CallLocation};7use crate::{Context, Result, Val};89#[derive(Clone, Acyclic)]10pub struct ParamName(Option<IStr>);11impl ParamName {12 pub const ANONYMOUS: Self = Self(None);13 pub fn new(name: IStr) -> Self {14 Self(Some(name))15 }16 pub fn as_str(&self) -> Option<&str> {17 self.0.as_deref()18 }19 pub fn is_anonymous(&self) -> bool {20 self.0.is_none()21 }22}23impl PartialEq<IStr> for ParamName {24 fn eq(&self, other: &IStr) -> bool {25 self.026 .as_ref()27 .map_or(false, |s| s.as_bytes() == other.as_bytes())28 }29}3031#[derive(Clone, Copy, Debug, Acyclic)]32pub enum ParamDefault {33 None,34 Exists,35 Literal(&'static str),36}37impl ParamDefault {38 pub const fn exists(is_exists: bool) -> Self {39 if is_exists {40 Self::Exists41 } else {42 Self::None43 }44 }45}4647#[macro_export]48macro_rules! params {49 (@name unnamed) => { ParamName::ANONYMOUS };50 (@name named $name:literal) => { ParamName::new($crate::IStr::from($name)) };51 ($($(#[$meta:meta])* [$kind:ident $(($lit:literal))? => $default:expr]),* $(,)?) => {52 thread_local! {53 static PARAMS: [ParamParse; { const N: usize = <[u8]>::len(&[$($(#[$meta])* 0u8),*]); N }] = [54 $($(#[$meta])* ParamParse::new(params!(@name $kind $($lit)?), $default)),*55 ];56 }57 };58}5960#[derive(Clone, Acyclic)]61pub struct ParamParse {62 name: ParamName,63 default: ParamDefault,64}65impl ParamParse {66 pub fn new(name: ParamName, default: ParamDefault) -> Self {67 Self { name, default }68 }69 70 pub fn name(&self) -> &ParamName {71 &self.name72 }73 pub fn default(&self) -> ParamDefault {74 self.default75 }76 pub fn has_default(&self) -> bool {77 !matches!(self.default, ParamDefault::None)78 }79}8081cc_dyn!(82 #[derive(Clone)]83 BuiltinFunc,84 Builtin,85 pub(crate) fn new() {...}86);87impl Builtin for BuiltinFunc {88 fn name(&self) -> &str {89 self.0.name()90 }9192 fn params(&self) -> &[ParamParse] {93 self.0.params()94 }9596 fn call(&self, ctx: Context, loc: CallLocation<'_>, args: &dyn ArgsLike) -> Result<Val> {97 self.0.call(ctx, loc, args)98 }99100 fn as_any(&self) -> &dyn Any {101 self.0.as_any()102 }103}104105106107108pub trait Builtin: Trace {109 110 fn name(&self) -> &str;111 112 fn params(&self) -> &[ParamParse];113 114 fn call(&self, ctx: Context, loc: CallLocation<'_>, args: &dyn ArgsLike) -> Result<Val>;115116 fn as_any(&self) -> &dyn Any;117}118119pub trait StaticBuiltin: Builtin + Send + Sync120where121 Self: 'static,122{123 124 125}126127#[derive(Trace)]128pub struct NativeCallback {129 pub(crate) params: Vec<ParamParse>,130 handler: TraceBox<dyn NativeCallbackHandler>,131}132impl NativeCallback {133 #[deprecated = "prefer using builtins directly, use this interface only for bindings"]134 pub fn new(params: Vec<String>, handler: impl NativeCallbackHandler) -> Self {135 Self {136 params: params137 .into_iter()138 .map(|n| ParamParse {139 name: ParamName::new(n.into()),140 default: ParamDefault::None,141 })142 .collect(),143 handler: TraceBox(Box::new(handler)),144 }145 }146}147148impl Builtin for NativeCallback {149 fn name(&self) -> &str {150 151 152 "<native>"153 }154155 fn params(&self) -> &[ParamParse] {156 &self.params157 }158159 fn call(&self, ctx: Context, _loc: CallLocation<'_>, args: &dyn ArgsLike) -> Result<Val> {160 let args = parse_builtin_call(ctx, &self.params, args, true)?;161 let args = args162 .into_iter()163 .map(|a| a.expect("legacy natives have no default params"))164 .map(|a| a.evaluate())165 .collect::<Result<Vec<Val>>>()?;166 self.handler.call(&args)167 }168169 fn as_any(&self) -> &dyn Any {170 self171 }172}173174pub trait NativeCallbackHandler: Trace {175 fn call(&self, args: &[Val]) -> Result<Val>;176}