git.delta.rocks / jrsonnet / refs/commits / 6bf55d21bf51

difftreelog

source

crates/jrsonnet-evaluator/src/function/builtin.rs3.4 KiBsourcehistory
1use std::{any::Any, borrow::Cow};23use jrsonnet_gcmodule::Trace;4use jrsonnet_interner::IStr;56use super::{arglike::ArgsLike, parse::parse_builtin_call, CallLocation};7use crate::{gc::TraceBox, tb, Context, Result, Val};89/// Can't have `str` | `IStr`, because constant `BuiltinParam` causes10/// `E0492: constant functions cannot refer to interior mutable data`11#[derive(Clone, Trace)]12pub struct ParamName(Option<Cow<'static, str>>);13impl ParamName {14	pub const ANONYMOUS: Self = Self(None);15	pub const fn new_static(name: &'static str) -> Self {16		Self(Some(Cow::Borrowed(name)))17	}18	pub fn new_dynamic(name: String) -> Self {19		Self(Some(Cow::Owned(name)))20	}21	pub fn as_str(&self) -> Option<&str> {22		self.0.as_deref()23	}24	pub fn is_anonymous(&self) -> bool {25		self.0.is_none()26	}27}28impl PartialEq<IStr> for ParamName {29	fn eq(&self, other: &IStr) -> bool {30		self.031			.as_ref()32			.map_or(false, |s| s.as_bytes() == other.as_bytes())33	}34}3536#[derive(Clone, Copy, Debug, Trace)]37pub enum ParamDefault {38	None,39	Exists,40	Literal(&'static str),41}42impl ParamDefault {43	pub const fn exists(is_exists: bool) -> Self {44		if is_exists {45			Self::Exists46		} else {47			Self::None48		}49	}50}5152#[derive(Clone, Trace)]53pub struct BuiltinParam {54	name: ParamName,55	default: ParamDefault,56}57impl BuiltinParam {58	pub const fn new(name: ParamName, default: ParamDefault) -> Self {59		Self { name, default }60	}61	/// Parameter name for named call parsing62	pub fn name(&self) -> &ParamName {63		&self.name64	}65	pub fn default(&self) -> ParamDefault {66		self.default67	}68	pub fn has_default(&self) -> bool {69		!matches!(self.default, ParamDefault::None)70	}71}7273/// Description of function defined by native code74///75/// Prefer to use #[builtin] macro, instead of manual implementation of this trait76pub trait Builtin: Trace {77	/// Function name to be used in stack traces78	fn name(&self) -> &str;79	/// Parameter names for named calls80	fn params(&self) -> &[BuiltinParam];81	/// Call the builtin82	fn call(&self, ctx: Context, loc: CallLocation<'_>, args: &dyn ArgsLike) -> Result<Val>;8384	fn as_any(&self) -> &dyn Any;85}8687pub trait StaticBuiltin: Builtin + Send + Sync88where89	Self: 'static,90{91	// In impl, to make it object safe:92	// const INST: &'static Self;93}9495#[derive(Trace)]96pub struct NativeCallback {97	pub(crate) params: Vec<BuiltinParam>,98	handler: TraceBox<dyn NativeCallbackHandler>,99}100impl NativeCallback {101	#[deprecated = "prefer using builtins directly, use this interface only for bindings"]102	pub fn new(params: Vec<String>, handler: impl NativeCallbackHandler) -> Self {103		Self {104			params: params105				.into_iter()106				.map(|n| BuiltinParam {107					name: ParamName::new_dynamic(n),108					default: ParamDefault::Exists,109				})110				.collect(),111			handler: tb!(handler),112		}113	}114}115116impl Builtin for NativeCallback {117	fn name(&self) -> &str {118		// TODO: standard natives gets their names from definition119		// But builitins should already have them120		"<native>"121	}122123	fn params(&self) -> &[BuiltinParam] {124		&self.params125	}126127	fn call(&self, ctx: Context, _loc: CallLocation<'_>, args: &dyn ArgsLike) -> Result<Val> {128		let args = parse_builtin_call(ctx, &self.params, args, true)?;129		let args = args130			.into_iter()131			.map(|a| a.expect("legacy natives have no default params"))132			.map(|a| a.evaluate())133			.collect::<Result<Vec<Val>>>()?;134		self.handler.call(&args)135	}136137	fn as_any(&self) -> &dyn Any {138		self139	}140}141142pub trait NativeCallbackHandler: Trace {143	fn call(&self, args: &[Val]) -> Result<Val>;144}