difftreelog
feat default params in function description
in: master
5 files changed
crates/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)]
crates/jrsonnet-evaluator/src/function/builtin.rsdiffbeforeafterboth1use 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}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}crates/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(),
crates/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())
}
crates/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,
};