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

difftreelog

feat default params in function description

Yaroslav Bolyukin2024-06-18parent: #b5d51b9.patch.diff
in: master

5 files changed

modifiedcrates/jrsonnet-evaluator/src/error.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/error.rs
+++ b/crates/jrsonnet-evaluator/src/error.rs
@@ -10,7 +10,12 @@
 use jrsonnet_types::ValType;
 use thiserror::Error;
 
-use crate::{function::CallLocation, stdlib::format::FormatError, typed::TypeLocError, ObjValue};
+use crate::{
+	function::{builtin::ParamDefault, CallLocation},
+	stdlib::format::FormatError,
+	typed::TypeLocError,
+	ObjValue,
+};
 
 pub(crate) fn format_found(list: &[IStr], what: &str) -> String {
 	if list.is_empty() {
@@ -43,7 +48,7 @@
 	if sig.is_empty() {
 		out.push_str("/*no arguments*/");
 	} else {
-		for (i, (name, has_default)) in sig.iter().enumerate() {
+		for (i, (name, default)) in sig.iter().enumerate() {
 			if i != 0 {
 				out.push_str(", ");
 			}
@@ -52,8 +57,13 @@
 			} else {
 				out.push_str("<unnamed>");
 			}
-			if *has_default {
-				out.push_str(" = <default>");
+			match default {
+				ParamDefault::None => {}
+				ParamDefault::Exists => out.push_str(" = <default>"),
+				ParamDefault::Literal(lit) => {
+					out.push_str(" = ");
+					out.push_str(lit);
+				}
 			}
 		}
 	}
@@ -88,7 +98,7 @@
 	heap.into_iter().map(|v| v.1).collect()
 }
 
-type FunctionSignature = Vec<(Option<IStr>, bool)>;
+type FunctionSignature = Vec<(Option<IStr>, ParamDefault)>;
 
 /// Possible errors
 #[allow(missing_docs)]
modifiedcrates/jrsonnet-evaluator/src/function/builtin.rsdiffbeforeafterboth
before · crates/jrsonnet-evaluator/src/function/builtin.rs
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, Trace)]37pub struct BuiltinParam {38	name: ParamName,39	has_default: bool,40}41impl BuiltinParam {42	pub const fn new(name: ParamName, has_default: bool) -> Self {43		Self { name, has_default }44	}45	/// Parameter name for named call parsing46	pub fn name(&self) -> &ParamName {47		&self.name48	}49	/// Is implementation allowed to return empty value50	pub fn has_default(&self) -> bool {51		self.has_default52	}53}5455/// Description of function defined by native code56///57/// Prefer to use #[builtin] macro, instead of manual implementation of this trait58pub trait Builtin: Trace {59	/// Function name to be used in stack traces60	fn name(&self) -> &str;61	/// Parameter names for named calls62	fn params(&self) -> &[BuiltinParam];63	/// Call the builtin64	fn call(&self, ctx: Context, loc: CallLocation<'_>, args: &dyn ArgsLike) -> Result<Val>;6566	fn as_any(&self) -> &dyn Any;67}6869pub trait StaticBuiltin: Builtin + Send + Sync70where71	Self: 'static,72{73	// In impl, to make it object safe:74	// const INST: &'static Self;75}7677#[derive(Trace)]78pub struct NativeCallback {79	pub(crate) params: Vec<BuiltinParam>,80	handler: TraceBox<dyn NativeCallbackHandler>,81}82impl NativeCallback {83	#[deprecated = "prefer using builtins directly, use this interface only for bindings"]84	pub fn new(params: Vec<String>, handler: impl NativeCallbackHandler) -> Self {85		Self {86			params: params87				.into_iter()88				.map(|n| BuiltinParam {89					name: ParamName::new_dynamic(n),90					has_default: false,91				})92				.collect(),93			handler: tb!(handler),94		}95	}96}9798impl Builtin for NativeCallback {99	fn name(&self) -> &str {100		// TODO: standard natives gets their names from definition101		// But builitins should already have them102		"<native>"103	}104105	fn params(&self) -> &[BuiltinParam] {106		&self.params107	}108109	fn call(&self, ctx: Context, _loc: CallLocation<'_>, args: &dyn ArgsLike) -> Result<Val> {110		let args = parse_builtin_call(ctx, &self.params, args, true)?;111		let args = args112			.into_iter()113			.map(|a| a.expect("legacy natives have no default params"))114			.map(|a| a.evaluate())115			.collect::<Result<Vec<Val>>>()?;116		self.handler.call(&args)117	}118119	fn as_any(&self) -> &dyn Any {120		self121	}122}123124pub trait NativeCallbackHandler: Trace {125	fn call(&self, args: &[Val]) -> Result<Val>;126}
after · crates/jrsonnet-evaluator/src/function/builtin.rs
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}
modifiedcrates/jrsonnet-evaluator/src/function/mod.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/function/mod.rs
+++ b/crates/jrsonnet-evaluator/src/function/mod.rs
@@ -8,7 +8,7 @@
 
 use self::{
 	arglike::OptionalContext,
-	builtin::{Builtin, BuiltinParam, ParamName, StaticBuiltin},
+	builtin::{Builtin, BuiltinParam, ParamDefault, ParamName, StaticBuiltin},
 	native::NativeDesc,
 	parse::{parse_default_function_call, parse_function_call},
 };
@@ -142,7 +142,7 @@
 							.as_ref()
 							.map(IStr::to_string)
 							.map_or(ParamName::ANONYMOUS, ParamName::new_dynamic),
-						p.1.is_some(),
+						ParamDefault::exists(p.1.is_some()),
 					)
 				})
 				.collect(),
modifiedcrates/jrsonnet-evaluator/src/function/parse.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/function/parse.rs
+++ b/crates/jrsonnet-evaluator/src/function/parse.rs
@@ -10,6 +10,7 @@
 	destructure::destruct,
 	error::{ErrorKind::*, Result},
 	evaluate_named,
+	function::builtin::ParamDefault,
 	gc::GcHashMap,
 	val::ThunkValue,
 	Context, Pending, Thunk, Val,
@@ -49,7 +50,10 @@
 	if args.unnamed_len() > params.len() {
 		bail!(TooManyArgsFunctionHas(
 			params.len(),
-			params.iter().map(|p| (p.0.name(), p.1.is_some())).collect()
+			params
+				.iter()
+				.map(|p| (p.0.name(), ParamDefault::exists(p.1.is_some())))
+				.collect()
 		))
 	}
 
@@ -127,7 +131,10 @@
 				if !found {
 					bail!(FunctionParameterNotBoundInCall(
 						param.0.clone().name(),
-						params.iter().map(|p| (p.0.name(), p.1.is_some())).collect()
+						params
+							.iter()
+							.map(|p| (p.0.name(), ParamDefault::exists(p.1.is_some())))
+							.collect()
 					));
 				}
 			}
@@ -163,7 +170,7 @@
 			params.len(),
 			params
 				.iter()
-				.map(|p| (p.name().as_str().map(IStr::from), p.has_default()))
+				.map(|p| (p.name().as_str().map(IStr::from), p.default()))
 				.collect()
 		))
 	}
@@ -211,7 +218,7 @@
 						param.name().as_str().map(IStr::from),
 						params
 							.iter()
-							.map(|p| (p.name().as_str().map(IStr::from), p.has_default()))
+							.map(|p| (p.name().as_str().map(IStr::from), p.default()))
 							.collect()
 					));
 				}
@@ -232,7 +239,10 @@
 		fn get(self: Box<Self>) -> Result<Val> {
 			Err(FunctionParameterNotBoundInCall(
 				Some(self.0.clone()),
-				self.1.iter().map(|p| (p.0.name(), p.1.is_some())).collect(),
+				self.1
+					.iter()
+					.map(|p| (p.0.name(), ParamDefault::exists(p.1.is_some())))
+					.collect(),
 			)
 			.into())
 		}
modifiedcrates/jrsonnet-macros/src/lib.rsdiffbeforeafterboth
--- a/crates/jrsonnet-macros/src/lib.rs
+++ b/crates/jrsonnet-macros/src/lib.rs
@@ -253,10 +253,14 @@
 			let name = name
 				.as_ref()
 				.map_or_else(|| quote! {None}, |n| quote! {ParamName::new_static(#n)});
-			let is_optional = optionality.is_optional();
+			let default = match optionality {
+				Optionality::Required => quote!(ParamDefault::None),
+				Optionality::Optional => quote!(ParamDefault::Exists),
+				Optionality::Default(e) => quote!(ParamDefault::Literal(stringify!(#e))),
+			};
 			Some(quote! {
 				#(#cfg_attrs)*
-				BuiltinParam::new(#name, #is_optional),
+				BuiltinParam::new(#name, #default),
 			})
 		}
 		ArgInfo::Lazy { is_option, name } => {
@@ -264,7 +268,7 @@
 				.as_ref()
 				.map_or_else(|| quote! {None}, |n| quote! {ParamName::new_static(#n)});
 			Some(quote! {
-				BuiltinParam::new(#name, #is_option),
+				BuiltinParam::new(#name, ParamDefault::exists(#is_option)),
 			})
 		}
 		ArgInfo::Context | ArgInfo::Location | ArgInfo::This => None,
@@ -375,7 +379,7 @@
 		const _: () = {
 			use ::jrsonnet_evaluator::{
 				State, Val,
-				function::{builtin::{Builtin, StaticBuiltin, BuiltinParam, ParamName}, CallLocation, ArgsLike, parse::parse_builtin_call},
+				function::{builtin::{Builtin, StaticBuiltin, BuiltinParam, ParamName, ParamDefault}, CallLocation, ArgsLike, parse::parse_builtin_call},
 				Result, Context, typed::Typed,
 				parser::ExprLocation,
 			};