difftreelog
refactor fancier builtin param type
in: master
5 files changed
bindings/jsonnet/src/native.rsdiffbeforeafterboth1use std::{1use std::{2 borrow::Cow,3 ffi::{c_void, CStr},2 ffi::{c_void, CStr},4 os::raw::{c_char, c_int},3 os::raw::{c_char, c_int},5};4};82 let param = CStr::from_ptr(*raw_params)81 let param = CStr::from_ptr(*raw_params)83 .to_str()82 .to_str()84 .expect("param name is not utf-8");83 .expect("param name is not utf-8");85 params.push(Cow::Owned(param.into()));84 params.push(param.into());86 raw_params = raw_params.offset(1);85 raw_params = raw_params.offset(1);87 }86 }8887crates/jrsonnet-evaluator/src/function/builtin.rsdiffbeforeafterboth1use std::{any::Any, borrow::Cow};1use std::{any::Any, borrow::Cow};223use jrsonnet_gcmodule::Trace;3use jrsonnet_gcmodule::Trace;4use jrsonnet_interner::IStr;455use super::{arglike::ArgsLike, parse::parse_builtin_call, CallLocation};6use super::{arglike::ArgsLike, parse::parse_builtin_call, CallLocation};6use crate::{error::Result, gc::TraceBox, tb, Context, Val};7use crate::{error::Result, gc::TraceBox, tb, Context, Val};789/// Can't have str | IStr, because constant BuiltinParam causes10/// E0492: constant functions cannot refer to interior mutable data11#[derive(Clone, Trace)]8pub type BuiltinParamName = Cow<'static, str>;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 match &self.0 {31 Some(s) => s.as_bytes() == other.as_bytes(),32 None => false,33 }34 }35}93610#[derive(Clone, Trace)]37#[derive(Clone, Trace)]11pub struct BuiltinParam {38pub struct BuiltinParam {12 /// Parameter name for named call parsing13 pub name: Option<BuiltinParamName>,39 name: ParamName,14 /// Is implementation allowed to return empty value15 pub has_default: bool,40 has_default: bool,16}41}42impl BuiltinParam {43 pub const fn new(name: ParamName, has_default: bool) -> Self {44 Self { name, has_default }45 }46 /// Parameter name for named call parsing47 pub fn name(&self) -> &ParamName {48 &self.name49 }50 /// Is implementation allowed to return empty value51 pub fn has_default(&self) -> bool {52 self.has_default53 }54}175518/// Description of function defined by native code56/// Description of function defined by native code19///57///44}82}45impl NativeCallback {83impl NativeCallback {46 #[deprecated = "prefer using builtins directly, use this interface only for bindings"]84 #[deprecated = "prefer using builtins directly, use this interface only for bindings"]47 pub fn new(params: Vec<Cow<'static, str>>, handler: impl NativeCallbackHandler) -> Self {85 pub fn new(params: Vec<String>, handler: impl NativeCallbackHandler) -> Self {48 Self {86 Self {49 params: params87 params: params50 .into_iter()88 .into_iter()51 .map(|n| BuiltinParam {89 .map(|n| BuiltinParam {52 name: Some(n),90 name: ParamName::new_dynamic(n.to_string()),53 has_default: false,91 has_default: false,54 })92 })55 .collect(),93 .collect(),crates/jrsonnet-evaluator/src/function/mod.rsdiffbeforeafterboth889use self::{9use self::{10 arglike::OptionalContext,10 arglike::OptionalContext,11 builtin::{Builtin, StaticBuiltin},11 builtin::{Builtin, BuiltinParam, ParamName, StaticBuiltin},12 native::NativeDesc,12 native::NativeDesc,13 parse::{parse_default_function_call, parse_function_call},13 parse::{parse_default_function_call, parse_function_call},14};14};113 }113 }114}114}115116#[allow(clippy::unnecessary_wraps)]117#[builtin]118const fn builtin_id(x: Val) -> Val {119 x120}121static ID: &builtin_id = &builtin_id {};115122116impl FuncVal {123impl FuncVal {117 pub fn builtin(builtin: impl Builtin) -> Self {124 pub fn builtin(builtin: impl Builtin) -> Self {118 Self::Builtin(Cc::new(tb!(builtin)))125 Self::Builtin(Cc::new(tb!(builtin)))119 }126 }127128 pub fn params(&self) -> Vec<BuiltinParam> {129 match self {130 Self::Id => ID.params().to_vec(),131 Self::StaticBuiltin(i) => i.params().to_vec(),132 Self::Builtin(i) => i.params().to_vec(),133 Self::Normal(p) => p134 .params135 .iter()136 .map(|p| {137 BuiltinParam::new(138 p.0.name()139 .as_ref()140 .map(IStr::to_string)141 .map_or(ParamName::ANONYMOUS, ParamName::new_dynamic),142 p.1.is_some(),143 )144 })145 .collect(),146 }147 }120 /// Amount of non-default required arguments148 /// Amount of non-default required arguments121 pub fn params_len(&self) -> usize {149 pub fn params_len(&self) -> usize {122 match self {150 match self {123 Self::Id => 1,151 Self::Id => 1,124 Self::Normal(n) => n.params.iter().filter(|p| p.1.is_none()).count(),152 Self::Normal(n) => n.params.iter().filter(|p| p.1.is_none()).count(),125 Self::StaticBuiltin(i) => i.params().iter().filter(|p| !p.has_default).count(),153 Self::StaticBuiltin(i) => i.params().iter().filter(|p| !p.has_default()).count(),126 Self::Builtin(i) => i.params().iter().filter(|p| !p.has_default).count(),154 Self::Builtin(i) => i.params().iter().filter(|p| !p.has_default()).count(),127 }155 }128 }156 }129 /// Function name, as defined in code.157 /// Function name, as defined in code.146 tailstrict: bool,174 tailstrict: bool,147 ) -> Result<Val> {175 ) -> Result<Val> {148 match self {176 match self {149 Self::Id => {177 Self::Id => ID.call(call_ctx, loc, args),150 #[allow(clippy::unnecessary_wraps)]151 #[builtin]152 const fn builtin_id(x: Val) -> Val {153 x154 }155 static ID: &builtin_id = &builtin_id {};156157 ID.call(call_ctx, loc, args)158 }159 Self::Normal(func) => {178 Self::Normal(func) => {160 let body_ctx = func.call_body_context(call_ctx, args, tailstrict)?;179 let body_ctx = func.call_body_context(call_ctx, args, tailstrict)?;161 evaluate(body_ctx, &func.body)180 evaluate(body_ctx, &func.body)crates/jrsonnet-evaluator/src/function/parse.rsdiffbeforeafterboth163 params.len(),163 params.len(),164 params164 params165 .iter()165 .iter()166 .map(|p| (p.name.as_ref().map(|v| v.as_ref().into()), p.has_default))166 .map(|p| (p.name().as_str().map(IStr::from), p.has_default()))167 .collect()167 .collect()168 ))168 ))169 }169 }180 // FIXME: O(n) for arg existence check180 // FIXME: O(n) for arg existence check181 let id = params181 let id = params182 .iter()182 .iter()183 .position(|p| p.name.as_ref().map_or(false, |v| v as &str == name as &str))183 .position(|p| p.name() == name)184 .ok_or_else(|| UnknownFunctionParameter((name as &str).to_owned()))?;184 .ok_or_else(|| UnknownFunctionParameter((name as &str).to_owned()))?;185 if replace(&mut passed_args[id], Some(arg)).is_some() {185 if replace(&mut passed_args[id], Some(arg)).is_some() {186 throw!(BindingParameterASecondTime(name.clone()));186 throw!(BindingParameterASecondTime(name.clone()));190 })?;190 })?;191191192 if filled_args < params.len() {192 if filled_args < params.len() {193 for (id, _) in params.iter().enumerate().filter(|(_, p)| p.has_default) {193 for (id, _) in params.iter().enumerate().filter(|(_, p)| p.has_default()) {194 if passed_args[id].is_some() {194 if passed_args[id].is_some() {195 continue;195 continue;196 }196 }204 args.named_names(&mut |name| {204 args.named_names(&mut |name| {205 if param205 if param.name() == name {206 .name207 .as_ref()208 .map_or(false, |v| v as &str == name as &str)209 {210 found = true;206 found = true;211 }207 }212 });208 });213 if !found {209 if !found {214 throw!(FunctionParameterNotBoundInCall(210 throw!(FunctionParameterNotBoundInCall(215 param.name.as_ref().map(|v| v.as_ref().into()),211 param.name().as_str().map(IStr::from),216 params212 params217 .iter()213 .iter()218 .map(|p| (p.name.as_ref().map(|p| p.as_ref().into()), p.has_default))214 .map(|p| (p.name().as_str().map(IStr::from), p.has_default()))219 .collect()215 .collect()220 ));216 ));221 }217 }tests/tests/common.rsdiffbeforeafterboth1use std::borrow::Cow;23use jrsonnet_evaluator::{1use jrsonnet_evaluator::{4 error::Result,2 error::Result,66 FuncVal::StaticBuiltin(b) => b64 FuncVal::StaticBuiltin(b) => b67 .params()65 .params()68 .iter()66 .iter()69 .map(|p| {67 .map(|p| p.name().as_str().unwrap_or(&"<unnamed>").to_string())70 p.name71 .as_ref()72 .unwrap_or(&Cow::Borrowed("<unnamed>"))73 .to_string()74 })75 .collect(),68 .collect(),76 FuncVal::Builtin(b) => b69 FuncVal::Builtin(b) => b77 .params()70 .params()78 .iter()71 .iter()79 .map(|p| {72 .map(|p| p.name().as_str().unwrap_or(&"<unnamed>").to_string())80 p.name81 .as_ref()82 .unwrap_or(&Cow::Borrowed("<unnamed>"))83 .to_string()84 })85 .collect(),73 .collect(),86 }74 }87}75}