--- 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(""); } - if *has_default { - out.push_str(" = "); + match default { + ParamDefault::None => {} + ParamDefault::Exists => out.push_str(" = "), + 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, bool)>; +type FunctionSignature = Vec<(Option, ParamDefault)>; /// Possible errors #[allow(missing_docs)] --- a/crates/jrsonnet-evaluator/src/function/builtin.rs +++ b/crates/jrsonnet-evaluator/src/function/builtin.rs @@ -33,22 +33,40 @@ } } +#[derive(Clone, Copy, Debug, Trace)] +pub enum ParamDefault { + None, + Exists, + Literal(&'static str), +} +impl ParamDefault { + pub const fn exists(is_exists: bool) -> Self { + if is_exists { + Self::Exists + } else { + Self::None + } + } +} + #[derive(Clone, Trace)] pub struct BuiltinParam { name: ParamName, - has_default: bool, + default: ParamDefault, } impl BuiltinParam { - pub const fn new(name: ParamName, has_default: bool) -> Self { - Self { name, has_default } + pub const fn new(name: ParamName, default: ParamDefault) -> Self { + Self { name, default } } /// Parameter name for named call parsing pub fn name(&self) -> &ParamName { &self.name } - /// Is implementation allowed to return empty value + pub fn default(&self) -> ParamDefault { + self.default + } pub fn has_default(&self) -> bool { - self.has_default + !matches!(self.default, ParamDefault::None) } } @@ -87,7 +105,7 @@ .into_iter() .map(|n| BuiltinParam { name: ParamName::new_dynamic(n), - has_default: false, + default: ParamDefault::Exists, }) .collect(), handler: tb!(handler), --- 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(), --- 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) -> Result { 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()) } --- 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, };