difftreelog
test basic interop checks
in: master
7 files changed
crates/jrsonnet-evaluator/src/error.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/error.rs
+++ b/crates/jrsonnet-evaluator/src/error.rs
@@ -1,4 +1,5 @@
use std::{
+ fmt::Debug,
path::{Path, PathBuf},
rc::Rc,
};
@@ -166,7 +167,7 @@
#[derive(Debug, Clone, Trace)]
pub struct StackTrace(pub Vec<StackTraceElement>);
-#[derive(Debug, Clone, Trace)]
+#[derive(Clone, Trace)]
pub struct LocError(Box<(Error, StackTrace)>);
impl LocError {
pub fn new(e: Error) -> Self {
@@ -186,6 +187,15 @@
&mut (self.0).1
}
}
+impl Debug for LocError {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ writeln!(f, "{}", self.0 .0)?;
+ for el in self.0 .1 .0.iter() {
+ writeln!(f, "\t{:?}", el)?;
+ }
+ Ok(())
+ }
+}
pub type Result<V, E = LocError> = std::result::Result<V, E>;
crates/jrsonnet-evaluator/src/function.rsdiffbeforeafterboth1use std::{borrow::Cow, collections::HashMap};23use gcmodule::Trace;4use jrsonnet_interner::IStr;5pub use jrsonnet_macros::builtin;6use jrsonnet_parser::{ArgsDesc, ExprLocation, LocExpr, ParamsDesc};78use crate::{9 error::Error::*, evaluate, evaluate_named, gc::TraceBox, throw, typed::Typed,10 val::LazyValValue, Context, FutureWrapper, GcHashMap, LazyVal, Result, State, Val,11};1213#[derive(Clone, Copy)]14pub struct CallLocation<'l>(pub Option<&'l ExprLocation>);15impl<'l> CallLocation<'l> {16 pub const fn new(loc: &'l ExprLocation) -> Self {17 Self(Some(loc))18 }19}20impl CallLocation<'static> {21 pub const fn native() -> Self {22 Self(None)23 }24}2526#[derive(Trace)]27struct EvaluateLazyVal {28 ctx: Context,29 expr: LocExpr,30}31impl LazyValValue for EvaluateLazyVal {32 fn get(self: Box<Self>, s: State) -> Result<Val> {33 evaluate(s, self.ctx, &self.expr)34 }35}3637#[derive(Trace)]38struct EvaluateNamedLazyVal {39 ctx: FutureWrapper<Context>,40 name: IStr,41 value: LocExpr,42}43impl LazyValValue for EvaluateNamedLazyVal {44 fn get(self: Box<Self>, s: State) -> Result<Val> {45 evaluate_named(s, self.ctx.unwrap(), &self.value, self.name)46 }47}4849pub trait ArgLike {50 fn evaluate_arg(&self, s: State, ctx: Context, tailstrict: bool) -> Result<LazyVal>;51}52impl ArgLike for &LocExpr {53 fn evaluate_arg(&self, s: State, ctx: Context, tailstrict: bool) -> Result<LazyVal> {54 Ok(if tailstrict {55 LazyVal::new_resolved(evaluate(s, ctx, self)?)56 } else {57 LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {58 ctx,59 expr: (*self).clone(),60 })))61 })62 }63}64impl<T> ArgLike for T65where66 T: Typed + Clone,67{68 fn evaluate_arg(&self, s: State, _ctx: Context, _tailstrict: bool) -> Result<LazyVal> {69 let val = T::into_untyped(self.clone(), s)?;70 Ok(LazyVal::new_resolved(val))71 }72}73pub enum TlaArg {74 String(IStr),75 Code(LocExpr),76 Val(Val),77}78impl ArgLike for TlaArg {79 fn evaluate_arg(&self, s: State, ctx: Context, tailstrict: bool) -> Result<LazyVal> {80 match self {81 TlaArg::String(s) => Ok(LazyVal::new_resolved(Val::Str(s.clone()))),82 TlaArg::Code(code) => Ok(if tailstrict {83 LazyVal::new_resolved(evaluate(s, ctx, code)?)84 } else {85 LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {86 ctx,87 expr: code.clone(),88 })))89 }),90 TlaArg::Val(val) => Ok(LazyVal::new_resolved(val.clone())),91 }92 }93}9495pub trait ArgsLike {96 fn unnamed_len(&self) -> usize;97 fn unnamed_iter(98 &self,99 s: State,100 ctx: Context,101 tailstrict: bool,102 handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,103 ) -> Result<()>;104 fn named_iter(105 &self,106 s: State,107 ctx: Context,108 tailstrict: bool,109 handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,110 ) -> Result<()>;111 fn named_names(&self, handler: &mut dyn FnMut(&IStr));112}113114impl ArgsLike for ArgsDesc {115 fn unnamed_len(&self) -> usize {116 self.unnamed.len()117 }118119 fn unnamed_iter(120 &self,121 s: State,122 ctx: Context,123 tailstrict: bool,124 handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,125 ) -> Result<()> {126 for (id, arg) in self.unnamed.iter().enumerate() {127 handler(128 id,129 if tailstrict {130 LazyVal::new_resolved(evaluate(s.clone(), ctx.clone(), arg)?)131 } else {132 LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {133 ctx: ctx.clone(),134 expr: arg.clone(),135 })))136 },137 )?;138 }139 Ok(())140 }141142 fn named_iter(143 &self,144 s: State,145 ctx: Context,146 tailstrict: bool,147 handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,148 ) -> Result<()> {149 for (name, arg) in self.named.iter() {150 handler(151 name,152 if tailstrict {153 LazyVal::new_resolved(evaluate(s.clone(), ctx.clone(), arg)?)154 } else {155 LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {156 ctx: ctx.clone(),157 expr: arg.clone(),158 })))159 },160 )?;161 }162 Ok(())163 }164165 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {166 for (name, _) in self.named.iter() {167 handler(name)168 }169 }170}171172impl<A: ArgLike> ArgsLike for [(IStr, A)] {173 fn unnamed_len(&self) -> usize {174 0175 }176177 fn unnamed_iter(178 &self,179 _s: State,180 _ctx: Context,181 _tailstrict: bool,182 _handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,183 ) -> Result<()> {184 Ok(())185 }186187 fn named_iter(188 &self,189 s: State,190 ctx: Context,191 tailstrict: bool,192 handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,193 ) -> Result<()> {194 for (name, val) in self.iter() {195 handler(name, val.evaluate_arg(s.clone(), ctx.clone(), tailstrict)?)?;196 }197 Ok(())198 }199200 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {201 for (name, _) in self.iter() {202 handler(name);203 }204 }205}206207impl<A: ArgLike> ArgsLike for HashMap<IStr, A> {208 fn unnamed_len(&self) -> usize {209 0210 }211212 fn unnamed_iter(213 &self,214 _s: State,215 _ctx: Context,216 _tailstrict: bool,217 _handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,218 ) -> Result<()> {219 Ok(())220 }221222 fn named_iter(223 &self,224 s: State,225 ctx: Context,226 tailstrict: bool,227 handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,228 ) -> Result<()> {229 for (name, value) in self.iter() {230 handler(231 name,232 value.evaluate_arg(s.clone(), ctx.clone(), tailstrict)?,233 )?;234 }235 Ok(())236 }237238 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {239 for (name, _) in self.iter() {240 handler(name);241 }242 }243}244245impl<A: ArgLike> ArgsLike for [A] {246 fn unnamed_len(&self) -> usize {247 self.len()248 }249250 fn unnamed_iter(251 &self,252 s: State,253 ctx: Context,254 tailstrict: bool,255 handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,256 ) -> Result<()> {257 for (i, arg) in self.iter().enumerate() {258 handler(i, arg.evaluate_arg(s.clone(), ctx.clone(), tailstrict)?)?;259 }260 Ok(())261 }262263 fn named_iter(264 &self,265 _s: State,266 _ctx: Context,267 _tailstrict: bool,268 _handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,269 ) -> Result<()> {270 Ok(())271 }272273 fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}274}275impl<A: ArgLike> ArgsLike for &[A] {276 fn unnamed_len(&self) -> usize {277 (*self).unnamed_len()278 }279280 fn unnamed_iter(281 &self,282 s: State,283 ctx: Context,284 tailstrict: bool,285 handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,286 ) -> Result<()> {287 (*self).unnamed_iter(s, ctx, tailstrict, handler)288 }289290 fn named_iter(291 &self,292 s: State,293 ctx: Context,294 tailstrict: bool,295 handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,296 ) -> Result<()> {297 (*self).named_iter(s, ctx, tailstrict, handler)298 }299300 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {301 (*self).named_names(handler)302 }303}304305/// Creates correct [context](Context) for function body evaluation returning error on invalid call.306///307/// ## Parameters308/// * `ctx`: used for passed argument expressions' execution and for body execution (if `body_ctx` is not set)309/// * `body_ctx`: used for default parameter values' execution and for body execution (if set)310/// * `params`: function parameters' definition311/// * `args`: passed function arguments312/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily313pub fn parse_function_call(314 s: State,315 ctx: Context,316 body_ctx: Context,317 params: &ParamsDesc,318 args: &dyn ArgsLike,319 tailstrict: bool,320) -> Result<Context> {321 let mut passed_args = GcHashMap::with_capacity(params.len());322 if args.unnamed_len() > params.len() {323 throw!(TooManyArgsFunctionHas(params.len()))324 }325326 let mut filled_args = 0;327328 args.unnamed_iter(s.clone(), ctx.clone(), tailstrict, &mut |id, arg| {329 let name = params[id].0.clone();330 passed_args.insert(name, arg);331 filled_args += 1;332 Ok(())333 })?;334335 args.named_iter(s, ctx, tailstrict, &mut |name, value| {336 // FIXME: O(n) for arg existence check337 if !params.iter().any(|p| &p.0 == name) {338 throw!(UnknownFunctionParameter((name as &str).to_owned()));339 }340 if passed_args.insert(name.clone(), value).is_some() {341 throw!(BindingParameterASecondTime(name.clone()));342 }343 filled_args += 1;344 Ok(())345 })?;346347 if filled_args < params.len() {348 // Some args are unset, but maybe we have defaults for them349 // Default values should be created in newly created context350 let fctx = Context::new_future();351 let mut defaults = GcHashMap::with_capacity(params.len() - filled_args);352353 for param in params.iter().filter(|p| p.1.is_some()) {354 if passed_args.contains_key(¶m.0.clone()) {355 continue;356 }357 LazyVal::new(TraceBox(Box::new(EvaluateNamedLazyVal {358 ctx: fctx.clone(),359 name: param.0.clone(),360 value: param.1.clone().unwrap(),361 })));362363 defaults.insert(364 param.0.clone(),365 LazyVal::new(TraceBox(Box::new(EvaluateNamedLazyVal {366 ctx: fctx.clone(),367 name: param.0.clone(),368 value: param.1.clone().unwrap(),369 }))),370 );371 filled_args += 1;372 }373374 // Some args still wasn't filled375 if filled_args != params.len() {376 for param in params.iter().skip(args.unnamed_len()) {377 let mut found = false;378 args.named_names(&mut |name| {379 if name == ¶m.0 {380 found = true;381 }382 });383 if !found {384 throw!(FunctionParameterNotBoundInCall(param.0.clone()));385 }386 }387 unreachable!();388 }389390 Ok(body_ctx391 .extend(passed_args, None, None, None)392 .extend_bound(defaults)393 .into_future(fctx))394 } else {395 let body_ctx = body_ctx.extend(passed_args, None, None, None);396 Ok(body_ctx)397 }398}399400type BuiltinParamName = Cow<'static, str>;401402#[derive(Clone, Trace)]403pub struct BuiltinParam {404 pub name: BuiltinParamName,405 pub has_default: bool,406}407408/// Do not implement it directly, instead use #[builtin] macro409pub trait Builtin: Trace {410 fn name(&self) -> &str;411 fn params(&self) -> &[BuiltinParam];412 fn call(&self, s: State, ctx: Context, loc: CallLocation, args: &dyn ArgsLike) -> Result<Val>;413}414415pub trait StaticBuiltin: Builtin + Send + Sync416where417 Self: 'static,418{419 // In impl, to make it object safe:420 // const INST: &'static Self;421}422423/// You shouldn't probally use this function, use jrsonnet_macros::builtin instead424///425/// ## Parameters426/// * `ctx`: used for passed argument expressions' execution and for body execution (if `body_ctx` is not set)427/// * `params`: function parameters' definition428/// * `args`: passed function arguments429/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily430pub fn parse_builtin_call(431 s: State,432 ctx: Context,433 params: &[BuiltinParam],434 args: &dyn ArgsLike,435 tailstrict: bool,436) -> Result<GcHashMap<BuiltinParamName, LazyVal>> {437 let mut passed_args = GcHashMap::with_capacity(params.len());438 if args.unnamed_len() > params.len() {439 throw!(TooManyArgsFunctionHas(params.len()))440 }441442 let mut filled_args = 0;443444 args.unnamed_iter(s.clone(), ctx.clone(), tailstrict, &mut |id, arg| {445 let name = params[id].name.clone();446 passed_args.insert(name, arg);447 filled_args += 1;448 Ok(())449 })?;450451 args.named_iter(s, ctx, tailstrict, &mut |name, arg| {452 // FIXME: O(n) for arg existence check453 let p = params454 .iter()455 .find(|p| p.name == name as &str)456 .ok_or_else(|| UnknownFunctionParameter((name as &str).to_owned()))?;457 if passed_args.insert(p.name.clone(), arg).is_some() {458 throw!(BindingParameterASecondTime(name.clone()));459 }460 filled_args += 1;461 Ok(())462 })?;463464 if filled_args < params.len() {465 for param in params.iter().filter(|p| p.has_default) {466 if passed_args.contains_key(¶m.name) {467 continue;468 }469 filled_args += 1;470 }471472 // Some args still wasn't filled473 if filled_args != params.len() {474 for param in params.iter().skip(args.unnamed_len()) {475 let mut found = false;476 args.named_names(&mut |name| {477 if name as &str == ¶m.name as &str {478 found = true;479 }480 });481 if !found {482 throw!(FunctionParameterNotBoundInCall(param.name.clone().into()));483 }484 }485 unreachable!();486 }487 }488 Ok(passed_args)489}490491/// Creates Context, which has all argument default values applied492/// and with unbound values causing error to be returned493pub fn parse_default_function_call(body_ctx: Context, params: &ParamsDesc) -> Context {494 let fctx = Context::new_future();495496 let mut bindings = GcHashMap::new();497498 #[derive(Trace)]499 struct DependsOnUnbound(IStr);500 impl LazyValValue for DependsOnUnbound {501 fn get(self: Box<Self>, _: State) -> Result<Val> {502 Err(FunctionParameterNotBoundInCall(self.0.clone()).into())503 }504 }505506 for param in params.iter() {507 if let Some(v) = ¶m.1 {508 bindings.insert(509 param.0.clone(),510 LazyVal::new(TraceBox(Box::new(EvaluateNamedLazyVal {511 ctx: fctx.clone(),512 name: param.0.clone(),513 value: v.clone(),514 }))),515 );516 } else {517 bindings.insert(518 param.0.clone(),519 LazyVal::new(TraceBox(Box::new(DependsOnUnbound(param.0.clone())))),520 );521 }522 }523524 body_ctx525 .extend(bindings, None, None, None)526 .into_future(fctx)527}1use std::{borrow::Cow, collections::HashMap};23use gcmodule::Trace;4use jrsonnet_interner::IStr;5pub use jrsonnet_macros::builtin;6use jrsonnet_parser::{ArgsDesc, ExprLocation, LocExpr, ParamsDesc};78use crate::{9 error::Error::*, evaluate, evaluate_named, gc::TraceBox, throw, typed::Typed,10 val::LazyValValue, Context, FutureWrapper, GcHashMap, LazyVal, Result, State, Val,11};1213#[derive(Clone, Copy)]14pub struct CallLocation<'l>(pub Option<&'l ExprLocation>);15impl<'l> CallLocation<'l> {16 pub const fn new(loc: &'l ExprLocation) -> Self {17 Self(Some(loc))18 }19}20impl CallLocation<'static> {21 pub const fn native() -> Self {22 Self(None)23 }24}2526#[derive(Trace)]27struct EvaluateLazyVal {28 ctx: Context,29 expr: LocExpr,30}31impl LazyValValue for EvaluateLazyVal {32 fn get(self: Box<Self>, s: State) -> Result<Val> {33 evaluate(s, self.ctx, &self.expr)34 }35}3637#[derive(Trace)]38struct EvaluateNamedLazyVal {39 ctx: FutureWrapper<Context>,40 name: IStr,41 value: LocExpr,42}43impl LazyValValue for EvaluateNamedLazyVal {44 fn get(self: Box<Self>, s: State) -> Result<Val> {45 evaluate_named(s, self.ctx.unwrap(), &self.value, self.name)46 }47}48pub trait ArgLike {49 fn evaluate_arg(&self, s: State, ctx: Context, tailstrict: bool) -> Result<LazyVal>;50}51impl ArgLike for &LocExpr {52 fn evaluate_arg(&self, s: State, ctx: Context, tailstrict: bool) -> Result<LazyVal> {53 Ok(if tailstrict {54 LazyVal::new_resolved(evaluate(s, ctx, self)?)55 } else {56 LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {57 ctx,58 expr: (*self).clone(),59 })))60 })61 }62}63impl<T> ArgLike for T64where65 T: Typed + Clone,66{67 fn evaluate_arg(&self, s: State, _ctx: Context, _tailstrict: bool) -> Result<LazyVal> {68 let val = T::into_untyped(self.clone(), s)?;69 Ok(LazyVal::new_resolved(val))70 }71}72pub enum TlaArg {73 String(IStr),74 Code(LocExpr),75 Val(Val),76}77impl ArgLike for TlaArg {78 fn evaluate_arg(&self, s: State, ctx: Context, tailstrict: bool) -> Result<LazyVal> {79 match self {80 TlaArg::String(s) => Ok(LazyVal::new_resolved(Val::Str(s.clone()))),81 TlaArg::Code(code) => Ok(if tailstrict {82 LazyVal::new_resolved(evaluate(s, ctx, code)?)83 } else {84 LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {85 ctx,86 expr: code.clone(),87 })))88 }),89 TlaArg::Val(val) => Ok(LazyVal::new_resolved(val.clone())),90 }91 }92}9394pub trait ArgsLike {95 fn unnamed_len(&self) -> usize;96 fn unnamed_iter(97 &self,98 s: State,99 ctx: Context,100 tailstrict: bool,101 handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,102 ) -> Result<()>;103 fn named_iter(104 &self,105 s: State,106 ctx: Context,107 tailstrict: bool,108 handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,109 ) -> Result<()>;110 fn named_names(&self, handler: &mut dyn FnMut(&IStr));111}112113impl ArgsLike for ArgsDesc {114 fn unnamed_len(&self) -> usize {115 self.unnamed.len()116 }117118 fn unnamed_iter(119 &self,120 s: State,121 ctx: Context,122 tailstrict: bool,123 handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,124 ) -> Result<()> {125 for (id, arg) in self.unnamed.iter().enumerate() {126 handler(127 id,128 if tailstrict {129 LazyVal::new_resolved(evaluate(s.clone(), ctx.clone(), arg)?)130 } else {131 LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {132 ctx: ctx.clone(),133 expr: arg.clone(),134 })))135 },136 )?;137 }138 Ok(())139 }140141 fn named_iter(142 &self,143 s: State,144 ctx: Context,145 tailstrict: bool,146 handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,147 ) -> Result<()> {148 for (name, arg) in self.named.iter() {149 handler(150 name,151 if tailstrict {152 LazyVal::new_resolved(evaluate(s.clone(), ctx.clone(), arg)?)153 } else {154 LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {155 ctx: ctx.clone(),156 expr: arg.clone(),157 })))158 },159 )?;160 }161 Ok(())162 }163164 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {165 for (name, _) in self.named.iter() {166 handler(name)167 }168 }169}170171impl ArgsLike for [(); 0] {172 fn unnamed_len(&self) -> usize {173 0174 }175176 fn unnamed_iter(177 &self,178 _s: State,179 _ctx: Context,180 _tailstrict: bool,181 _handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,182 ) -> Result<()> {183 Ok(())184 }185186 fn named_iter(187 &self,188 _s: State,189 _ctx: Context,190 _tailstrict: bool,191 _handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,192 ) -> Result<()> {193 Ok(())194 }195196 fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}197}198199impl<A: ArgLike> ArgsLike for [(IStr, A)] {200 fn unnamed_len(&self) -> usize {201 0202 }203204 fn unnamed_iter(205 &self,206 _s: State,207 _ctx: Context,208 _tailstrict: bool,209 _handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,210 ) -> Result<()> {211 Ok(())212 }213214 fn named_iter(215 &self,216 s: State,217 ctx: Context,218 tailstrict: bool,219 handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,220 ) -> Result<()> {221 for (name, val) in self.iter() {222 handler(name, val.evaluate_arg(s.clone(), ctx.clone(), tailstrict)?)?;223 }224 Ok(())225 }226227 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {228 for (name, _) in self.iter() {229 handler(name);230 }231 }232}233234impl<A: ArgLike> ArgsLike for HashMap<IStr, A> {235 fn unnamed_len(&self) -> usize {236 0237 }238239 fn unnamed_iter(240 &self,241 _s: State,242 _ctx: Context,243 _tailstrict: bool,244 _handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,245 ) -> Result<()> {246 Ok(())247 }248249 fn named_iter(250 &self,251 s: State,252 ctx: Context,253 tailstrict: bool,254 handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,255 ) -> Result<()> {256 for (name, value) in self.iter() {257 handler(258 name,259 value.evaluate_arg(s.clone(), ctx.clone(), tailstrict)?,260 )?;261 }262 Ok(())263 }264265 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {266 for (name, _) in self.iter() {267 handler(name);268 }269 }270}271272impl<A: ArgLike> ArgsLike for [A] {273 fn unnamed_len(&self) -> usize {274 self.len()275 }276277 fn unnamed_iter(278 &self,279 s: State,280 ctx: Context,281 tailstrict: bool,282 handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,283 ) -> Result<()> {284 for (i, arg) in self.iter().enumerate() {285 handler(i, arg.evaluate_arg(s.clone(), ctx.clone(), tailstrict)?)?;286 }287 Ok(())288 }289290 fn named_iter(291 &self,292 _s: State,293 _ctx: Context,294 _tailstrict: bool,295 _handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,296 ) -> Result<()> {297 Ok(())298 }299300 fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}301}302impl<A: ArgLike> ArgsLike for &[A] {303 fn unnamed_len(&self) -> usize {304 (*self).unnamed_len()305 }306307 fn unnamed_iter(308 &self,309 s: State,310 ctx: Context,311 tailstrict: bool,312 handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,313 ) -> Result<()> {314 (*self).unnamed_iter(s, ctx, tailstrict, handler)315 }316317 fn named_iter(318 &self,319 s: State,320 ctx: Context,321 tailstrict: bool,322 handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,323 ) -> Result<()> {324 (*self).named_iter(s, ctx, tailstrict, handler)325 }326327 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {328 (*self).named_names(handler)329 }330}331332/// Creates correct [context](Context) for function body evaluation returning error on invalid call.333///334/// ## Parameters335/// * `ctx`: used for passed argument expressions' execution and for body execution (if `body_ctx` is not set)336/// * `body_ctx`: used for default parameter values' execution and for body execution (if set)337/// * `params`: function parameters' definition338/// * `args`: passed function arguments339/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily340pub fn parse_function_call(341 s: State,342 ctx: Context,343 body_ctx: Context,344 params: &ParamsDesc,345 args: &dyn ArgsLike,346 tailstrict: bool,347) -> Result<Context> {348 let mut passed_args = GcHashMap::with_capacity(params.len());349 if args.unnamed_len() > params.len() {350 throw!(TooManyArgsFunctionHas(params.len()))351 }352353 let mut filled_args = 0;354355 args.unnamed_iter(s.clone(), ctx.clone(), tailstrict, &mut |id, arg| {356 let name = params[id].0.clone();357 passed_args.insert(name, arg);358 filled_args += 1;359 Ok(())360 })?;361362 args.named_iter(s, ctx, tailstrict, &mut |name, value| {363 // FIXME: O(n) for arg existence check364 if !params.iter().any(|p| &p.0 == name) {365 throw!(UnknownFunctionParameter((name as &str).to_owned()));366 }367 if passed_args.insert(name.clone(), value).is_some() {368 throw!(BindingParameterASecondTime(name.clone()));369 }370 filled_args += 1;371 Ok(())372 })?;373374 if filled_args < params.len() {375 // Some args are unset, but maybe we have defaults for them376 // Default values should be created in newly created context377 let fctx = Context::new_future();378 let mut defaults = GcHashMap::with_capacity(params.len() - filled_args);379380 for param in params.iter().filter(|p| p.1.is_some()) {381 if passed_args.contains_key(¶m.0.clone()) {382 continue;383 }384 LazyVal::new(TraceBox(Box::new(EvaluateNamedLazyVal {385 ctx: fctx.clone(),386 name: param.0.clone(),387 value: param.1.clone().unwrap(),388 })));389390 defaults.insert(391 param.0.clone(),392 LazyVal::new(TraceBox(Box::new(EvaluateNamedLazyVal {393 ctx: fctx.clone(),394 name: param.0.clone(),395 value: param.1.clone().unwrap(),396 }))),397 );398 filled_args += 1;399 }400401 // Some args still wasn't filled402 if filled_args != params.len() {403 for param in params.iter().skip(args.unnamed_len()) {404 let mut found = false;405 args.named_names(&mut |name| {406 if name == ¶m.0 {407 found = true;408 }409 });410 if !found {411 throw!(FunctionParameterNotBoundInCall(param.0.clone()));412 }413 }414 unreachable!();415 }416417 Ok(body_ctx418 .extend(passed_args, None, None, None)419 .extend_bound(defaults)420 .into_future(fctx))421 } else {422 let body_ctx = body_ctx.extend(passed_args, None, None, None);423 Ok(body_ctx)424 }425}426427type BuiltinParamName = Cow<'static, str>;428429#[derive(Clone, Trace)]430pub struct BuiltinParam {431 pub name: BuiltinParamName,432 pub has_default: bool,433}434435/// Do not implement it directly, instead use #[builtin] macro436pub trait Builtin: Trace {437 fn name(&self) -> &str;438 fn params(&self) -> &[BuiltinParam];439 fn call(&self, s: State, ctx: Context, loc: CallLocation, args: &dyn ArgsLike) -> Result<Val>;440}441442pub trait StaticBuiltin: Builtin + Send + Sync443where444 Self: 'static,445{446 // In impl, to make it object safe:447 // const INST: &'static Self;448}449450/// You shouldn't probally use this function, use jrsonnet_macros::builtin instead451///452/// ## Parameters453/// * `ctx`: used for passed argument expressions' execution and for body execution (if `body_ctx` is not set)454/// * `params`: function parameters' definition455/// * `args`: passed function arguments456/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily457pub fn parse_builtin_call(458 s: State,459 ctx: Context,460 params: &[BuiltinParam],461 args: &dyn ArgsLike,462 tailstrict: bool,463) -> Result<GcHashMap<BuiltinParamName, LazyVal>> {464 let mut passed_args = GcHashMap::with_capacity(params.len());465 if args.unnamed_len() > params.len() {466 throw!(TooManyArgsFunctionHas(params.len()))467 }468469 let mut filled_args = 0;470471 args.unnamed_iter(s.clone(), ctx.clone(), tailstrict, &mut |id, arg| {472 let name = params[id].name.clone();473 passed_args.insert(name, arg);474 filled_args += 1;475 Ok(())476 })?;477478 args.named_iter(s, ctx, tailstrict, &mut |name, arg| {479 // FIXME: O(n) for arg existence check480 let p = params481 .iter()482 .find(|p| p.name == name as &str)483 .ok_or_else(|| UnknownFunctionParameter((name as &str).to_owned()))?;484 if passed_args.insert(p.name.clone(), arg).is_some() {485 throw!(BindingParameterASecondTime(name.clone()));486 }487 filled_args += 1;488 Ok(())489 })?;490491 if filled_args < params.len() {492 for param in params.iter().filter(|p| p.has_default) {493 if passed_args.contains_key(¶m.name) {494 continue;495 }496 filled_args += 1;497 }498499 // Some args still wasn't filled500 if filled_args != params.len() {501 for param in params.iter().skip(args.unnamed_len()) {502 let mut found = false;503 args.named_names(&mut |name| {504 if name as &str == ¶m.name as &str {505 found = true;506 }507 });508 if !found {509 throw!(FunctionParameterNotBoundInCall(param.name.clone().into()));510 }511 }512 unreachable!();513 }514 }515 Ok(passed_args)516}517518/// Creates Context, which has all argument default values applied519/// and with unbound values causing error to be returned520pub fn parse_default_function_call(body_ctx: Context, params: &ParamsDesc) -> Context {521 let fctx = Context::new_future();522523 let mut bindings = GcHashMap::new();524525 #[derive(Trace)]526 struct DependsOnUnbound(IStr);527 impl LazyValValue for DependsOnUnbound {528 fn get(self: Box<Self>, _: State) -> Result<Val> {529 Err(FunctionParameterNotBoundInCall(self.0.clone()).into())530 }531 }532533 for param in params.iter() {534 if let Some(v) = ¶m.1 {535 bindings.insert(536 param.0.clone(),537 LazyVal::new(TraceBox(Box::new(EvaluateNamedLazyVal {538 ctx: fctx.clone(),539 name: param.0.clone(),540 value: v.clone(),541 }))),542 );543 } else {544 bindings.insert(545 param.0.clone(),546 LazyVal::new(TraceBox(Box::new(DependsOnUnbound(param.0.clone())))),547 );548 }549 }550551 body_ctx552 .extend(bindings, None, None, None)553 .into_future(fctx)554}crates/jrsonnet-evaluator/src/typed/conversions.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/typed/conversions.rs
+++ b/crates/jrsonnet-evaluator/src/typed/conversions.rs
@@ -14,11 +14,11 @@
};
pub trait TypedObj: Typed {
- fn serialize(self, out: &mut ObjValueBuilder) -> Result<()>;
- fn parse(obj: &ObjValue) -> Result<Self>;
- fn into_object(self) -> Result<ObjValue> {
+ fn serialize(self, s: State, out: &mut ObjValueBuilder) -> Result<()>;
+ fn parse(obj: &ObjValue, s: State) -> Result<Self>;
+ fn into_object(self, s: State) -> Result<ObjValue> {
let mut builder = ObjValueBuilder::new();
- self.serialize(&mut builder)?;
+ self.serialize(s, &mut builder)?;
Ok(builder.build())
}
}
crates/jrsonnet-evaluator/tests/builtin.rsdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-evaluator/tests/builtin.rs
@@ -0,0 +1,105 @@
+mod common;
+
+use std::path::PathBuf;
+
+use gcmodule::Cc;
+use jrsonnet_evaluator::{
+ error::Result,
+ function::{builtin, Builtin, CallLocation},
+ gc::TraceBox,
+ typed::Typed,
+ val::FuncVal,
+ State, Val,
+};
+
+#[builtin]
+fn a() -> Result<u32> {
+ Ok(1)
+}
+
+#[test]
+fn basic_function() -> Result<()> {
+ let s = State::default();
+ let a: a = a {};
+ let v = u32::from_untyped(
+ a.call(
+ s.clone(),
+ s.create_default_context(),
+ CallLocation::native(),
+ &[],
+ )?,
+ s.clone(),
+ )?;
+
+ ensure_eq!(v, 1);
+ Ok(())
+}
+
+#[builtin]
+fn native_add(a: u32, b: u32) -> Result<u32> {
+ Ok(a + b)
+}
+
+#[test]
+fn call_from_code() -> Result<()> {
+ let s = State::default();
+ s.with_stdlib();
+ s.settings_mut().globals.insert(
+ "nativeAdd".into(),
+ Val::Func(FuncVal::StaticBuiltin(native_add::INST)),
+ );
+
+ let v = s.evaluate_snippet_raw(
+ PathBuf::new().into(),
+ "
+ assert nativeAdd(1, 2) == 3;
+ assert nativeAdd(100, 200) == 300;
+ null
+ "
+ .into(),
+ )?;
+ ensure_val_eq!(s.clone(), v, Val::Null);
+ Ok(())
+}
+
+#[builtin(fields(
+ a: u32
+))]
+fn curried_add(this: &curried_add, b: u32) -> Result<u32> {
+ Ok(this.a + b)
+}
+
+#[builtin]
+fn curry_add(a: u32) -> Result<FuncVal> {
+ Ok(FuncVal::Builtin(Cc::new(TraceBox(Box::new(curried_add {
+ a,
+ })))))
+}
+
+#[test]
+fn nonstatic_builtin() -> Result<()> {
+ let s = State::default();
+ s.with_stdlib();
+ s.settings_mut().globals.insert(
+ "curryAdd".into(),
+ Val::Func(FuncVal::StaticBuiltin(curry_add::INST)),
+ );
+
+ let v = s.evaluate_snippet_raw(
+ PathBuf::new().into(),
+ "
+ local a = curryAdd(1);
+ local b = curryAdd(4);
+
+ assert a(2) == 3;
+ assert a(200) == 201;
+
+ assert b(2) == 6;
+ assert b(200) == 204;
+ null
+ "
+ .into(),
+ )?;
+ ensure_val_eq!(s.clone(), v, Val::Null);
+ Ok(())
+}
crates/jrsonnet-evaluator/tests/common.rsdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-evaluator/tests/common.rs
@@ -0,0 +1,25 @@
+#[macro_export]
+macro_rules! ensure_eq {
+ ($a:expr, $b:expr $(,)?) => {{
+ if $a != $b {
+ ::jrsonnet_evaluator::throw_runtime!(
+ "assertion failed: a != b\na={:#?}\nb={:#?}",
+ $a,
+ $b,
+ )
+ }
+ }};
+}
+
+#[macro_export]
+macro_rules! ensure_val_eq {
+ ($s:expr, $a:expr, $b:expr) => {{
+ if !::jrsonnet_evaluator::val::equals($s.clone(), &$a.clone(), &$b.clone())? {
+ ::jrsonnet_evaluator::throw_runtime!(
+ "assertion failed: a != b\na={:#?}\nb={:#?}",
+ $a.to_json($s.clone(), 2)?,
+ $b.to_json($s.clone(), 2)?,
+ )
+ }
+ }};
+}
crates/jrsonnet-evaluator/tests/typed_obj.rsdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-evaluator/tests/typed_obj.rs
@@ -0,0 +1,194 @@
+mod common;
+
+use std::{fmt::Debug, path::PathBuf};
+
+use jrsonnet_evaluator::{error::Result, typed::Typed, State};
+
+#[derive(Clone, Typed, PartialEq, Debug)]
+struct A {
+ a: u32,
+ b: u16,
+}
+
+fn test_roundtrip<T: Typed + PartialEq + Debug + Clone>(value: T, s: State) -> Result<()> {
+ let untyped = T::into_untyped(value.clone(), s.clone())?;
+ let value2 = T::from_untyped(untyped.clone(), s.clone())?;
+ ensure_eq!(value, value2);
+ let untyped2 = T::into_untyped(value2, s.clone())?;
+ ensure_val_eq!(s, untyped, untyped2);
+
+ Ok(())
+}
+
+#[test]
+fn simple_object() -> Result<()> {
+ let s = State::default();
+ s.with_stdlib();
+ let a = A::from_untyped(
+ s.evaluate_snippet_raw(PathBuf::new().into(), "{a: 1, b: 2}".into())?,
+ s.clone(),
+ )?;
+ ensure_eq!(a, A { a: 1, b: 2 });
+ test_roundtrip(a.clone(), s.clone())?;
+ Ok(())
+}
+
+#[derive(Clone, Typed, PartialEq, Debug)]
+struct B {
+ a: u32,
+ #[typed(rename = "c")]
+ b: u16,
+}
+
+#[test]
+fn renamed_field() -> Result<()> {
+ let s = State::default();
+ s.with_stdlib();
+ let b = B::from_untyped(
+ s.evaluate_snippet_raw(PathBuf::new().into(), "{a: 1, c: 2}".into())?,
+ s.clone(),
+ )?;
+ ensure_eq!(b, B { a: 1, b: 2 });
+ ensure_eq!(
+ &B::into_untyped(b.clone(), s.clone())?.to_string(s.clone())? as &str,
+ "{a: 1, c: 2}",
+ );
+ test_roundtrip(b.clone(), s.clone())?;
+ Ok(())
+}
+
+#[derive(Clone, Typed, PartialEq, Debug)]
+struct ObjectKind {
+ #[typed(rename = "apiVersion")]
+ api_version: String,
+ #[typed(rename = "kind")]
+ kind: String,
+}
+
+#[derive(Clone, Typed, PartialEq, Debug)]
+struct Object {
+ #[typed(flatten)]
+ kind: ObjectKind,
+ b: u16,
+}
+
+#[test]
+fn flattened_object() -> Result<()> {
+ let s = State::default();
+ s.with_stdlib();
+ let obj = Object::from_untyped(
+ s.evaluate_snippet_raw(
+ PathBuf::new().into(),
+ "{apiVersion: 'ver', kind: 'kind', b: 2}".into(),
+ )?,
+ s.clone(),
+ )?;
+ ensure_eq!(
+ obj,
+ Object {
+ kind: ObjectKind {
+ api_version: "ver".into(),
+ kind: "kind".into(),
+ },
+ b: 2
+ }
+ );
+ ensure_eq!(
+ &Object::into_untyped(obj.clone(), s.clone())?.to_string(s.clone())? as &str,
+ r#"{"apiVersion": "ver", "b": 2, "kind": "kind"}"#,
+ );
+ test_roundtrip(obj.clone(), s.clone())?;
+ Ok(())
+}
+
+#[derive(Clone, Typed, PartialEq, Debug)]
+struct C {
+ a: Option<u32>,
+ b: u16,
+}
+
+#[test]
+fn optional_field_some() -> Result<()> {
+ let s = State::default();
+ s.with_stdlib();
+ let c = C::from_untyped(
+ s.evaluate_snippet_raw(PathBuf::new().into(), "{a: 1, b: 2}".into())?,
+ s.clone(),
+ )?;
+ ensure_eq!(c, C { a: Some(1), b: 2 });
+ ensure_eq!(
+ &C::into_untyped(c.clone(), s.clone())?.to_string(s.clone())? as &str,
+ r#"{"a": 1, "b": 2}"#,
+ );
+ test_roundtrip(c.clone(), s.clone())?;
+ Ok(())
+}
+
+#[test]
+fn optional_field_none() -> Result<()> {
+ let s = State::default();
+ s.with_stdlib();
+ let c = C::from_untyped(
+ s.evaluate_snippet_raw(PathBuf::new().into(), "{b: 2}".into())?,
+ s.clone(),
+ )?;
+ ensure_eq!(c, C { a: None, b: 2 });
+ ensure_eq!(
+ &C::into_untyped(c.clone(), s.clone())?.to_string(s.clone())? as &str,
+ r#"{"b": 2}"#,
+ );
+ test_roundtrip(c.clone(), s.clone())?;
+ Ok(())
+}
+
+#[derive(Clone, Typed, PartialEq, Debug)]
+struct D {
+ #[typed(flatten(ok))]
+ e: Option<E>,
+ b: u16,
+}
+
+#[derive(Clone, Typed, PartialEq, Debug)]
+struct E {
+ v: u32,
+}
+
+#[test]
+fn flatten_optional_some() -> Result<()> {
+ let s = State::default();
+ s.with_stdlib();
+ let d = D::from_untyped(
+ s.evaluate_snippet_raw(PathBuf::new().into(), "{b: 2, v:1}".into())?,
+ s.clone(),
+ )?;
+ ensure_eq!(
+ d,
+ D {
+ e: Some(E { v: 1 }),
+ b: 2
+ }
+ );
+ ensure_eq!(
+ &D::into_untyped(d.clone(), s.clone())?.to_string(s.clone())? as &str,
+ r#"{"b": 2, "v": 1}"#,
+ );
+ test_roundtrip(d.clone(), s.clone())?;
+ Ok(())
+}
+
+#[test]
+fn flatten_optional_none() -> Result<()> {
+ let s = State::default();
+ s.with_stdlib();
+ let d = D::from_untyped(
+ s.evaluate_snippet_raw(PathBuf::new().into(), "{b: 2, v: '1'}".into())?,
+ s.clone(),
+ )?;
+ ensure_eq!(d, D { e: None, b: 2 });
+ ensure_eq!(
+ &D::into_untyped(d.clone(), s.clone())?.to_string(s.clone())? as &str,
+ r#"{"b": 2}"#,
+ );
+ test_roundtrip(d.clone(), s.clone())?;
+ Ok(())
+}
crates/jrsonnet-macros/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-macros/src/lib.rs
+++ b/crates/jrsonnet-macros/src/lib.rs
@@ -6,7 +6,7 @@
parse_macro_input,
punctuated::Punctuated,
spanned::Spanned,
- token::Comma,
+ token::{self, Comma},
Attribute, DeriveInput, Error, FnArg, GenericArgument, Ident, ItemFn, LitStr, Pat, Path,
PathArguments, Result, ReturnType, Token, Type,
};
@@ -90,6 +90,7 @@
syn::custom_keyword!(fields);
syn::custom_keyword!(rename);
syn::custom_keyword!(flatten);
+ syn::custom_keyword!(ok);
}
struct EmptyAttr;
@@ -135,7 +136,7 @@
}
impl ArgInfo {
- fn parse(arg: &FnArg) -> Result<Self> {
+ fn parse(name: &str, arg: &FnArg) -> Result<Self> {
let arg = match arg {
FnArg::Receiver(_) => unreachable!(),
FnArg::Typed(a) => a,
@@ -149,8 +150,6 @@
return Ok(Self::State);
} else if type_is_path(ty, "CallLocation").is_some() {
return Ok(Self::Location);
- } else if type_is_path(ty, "Self").is_some() {
- return Ok(Self::This);
} else if type_is_path(ty, "LazyVal").is_some() {
return Ok(Self::Lazy {
is_option: false,
@@ -158,6 +157,11 @@
});
}
+ match &ty as &Type {
+ Type::Reference(r) if type_is_path(&r.elem, &name).is_some() => return Ok(Self::This),
+ _ => {}
+ }
+
let (is_option, ty) = if let Some(ty) = extract_type_from_option(ty)? {
if type_is_path(ty, "LazyVal").is_some() {
return Ok(Self::Lazy {
@@ -230,11 +234,12 @@
return Err(Error::new(result.span(), "return value should be result"));
};
+ let name = fun.sig.ident.to_string();
let args = fun
.sig
.inputs
.iter()
- .map(ArgInfo::parse)
+ .map(|arg| ArgInfo::parse(&name, arg))
.collect::<Result<Vec<_>>>()?;
let params_desc = args.iter().flat_map(|a| match a {
@@ -343,9 +348,9 @@
}
const _: () = {
use ::jrsonnet_evaluator::{
- State,
+ State, Val,
function::{Builtin, CallLocation, StaticBuiltin, BuiltinParam, ArgsLike, parse_builtin_call},
- error::Result, Context,
+ error::Result, Context, typed::Typed,
parser::ExprLocation,
};
const PARAMS: &'static [BuiltinParam] = &[
@@ -379,6 +384,9 @@
struct TypedAttr {
rename: Option<String>,
flatten: bool,
+ /// flatten(ok) strategy for flattened optionals
+ /// field would be None in case of any parsing error (as in serde)
+ flatten_ok: bool,
}
impl Parse for TypedAttr {
fn parse(input: ParseStream) -> syn::Result<Self> {
@@ -399,6 +407,17 @@
} else if lookahead.peek(kw::flatten) {
input.parse::<kw::flatten>()?;
out.flatten = true;
+ if input.peek(token::Paren) {
+ let content;
+ parenthesized!(content in input);
+ let lookahead = content.lookahead1();
+ if lookahead.peek(kw::ok) {
+ content.parse::<kw::ok>()?;
+ out.flatten_ok = true;
+ } else {
+ return Err(lookahead.error());
+ }
+ }
} else if input.is_empty() {
break;
} else {
@@ -417,75 +436,101 @@
}
}
-struct TypedField<'f>(&'f syn::Field, TypedAttr);
-impl<'f> TypedField<'f> {
- fn try_new(field: &'f syn::Field) -> Result<Self> {
+struct TypedField {
+ attr: TypedAttr,
+ ident: Ident,
+ ty: Type,
+ is_option: bool,
+}
+impl TypedField {
+ fn parse(field: &syn::Field) -> Result<Self> {
let attr = parse_attr::<TypedAttr, _>(&field.attrs, "typed")?.unwrap_or_default();
- if field.ident.is_none() {
+ let ident = if let Some(ident) = field.ident.clone() {
+ ident
+ } else {
return Err(Error::new(
field.span(),
"this field should appear in output object, but it has no visible name",
));
+ };
+ let (is_option, ty) = if let Some(ty) = extract_type_from_option(&field.ty)? {
+ (true, ty.clone())
+ } else {
+ (false, field.ty.clone())
+ };
+ if is_option && attr.flatten {
+ if !attr.flatten_ok {
+ return Err(Error::new(
+ field.span(),
+ "strategy should be set when flattening Option",
+ ));
+ }
+ } else {
+ if attr.flatten_ok {
+ return Err(Error::new(
+ field.span(),
+ "flatten(ok) is only useable on optional fields",
+ ));
+ }
}
- Ok(Self(field, attr))
- }
- fn ident(&self) -> Ident {
- self.0
- .ident
- .clone()
- .expect("constructor disallows fields without name")
+ Ok(Self {
+ attr,
+ ident,
+ ty,
+ is_option,
+ })
}
/// None if this field is flattened in jsonnet output
fn name(&self) -> Option<String> {
- if self.1.flatten {
+ if self.attr.flatten {
return None;
}
Some(
- self.1
+ self.attr
.rename
.clone()
- .unwrap_or_else(|| self.ident().to_string()),
+ .unwrap_or_else(|| self.ident.to_string()),
)
}
fn expand_field(&self) -> Option<TokenStream> {
- if self.is_option() {
+ if self.is_option {
return None;
}
let name = self.name()?;
- let ty = &self.0.ty;
+ let ty = &self.ty;
Some(quote! {
(#name, <#ty>::TYPE)
})
}
fn expand_parse(&self) -> TokenStream {
- let ident = self.ident();
- let ty = &self.0.ty;
- if self.1.flatten {
+ let ident = &self.ident;
+ let ty = &self.ty;
+ if self.attr.flatten {
// optional flatten is handled in same way as serde
- return if self.is_option() {
+ return if self.is_option {
quote! {
- #ident: <#ty>::parse(&obj).ok(),
+ #ident: <#ty>::parse(&obj, s.clone()).ok(),
}
} else {
quote! {
- #ident: <#ty>::parse(&obj)?,
+ #ident: <#ty>::parse(&obj, s.clone())?,
}
};
};
let name = self.name().unwrap();
- let value = if let Some(ty) = self.as_option() {
+ let value = if self.is_option {
quote! {
- if let Some(value) = obj.get(#name.into())? {
- Some(<#ty>::try_from(vakue)?)
+ if let Some(value) = obj.get(s.clone(), #name.into())? {
+ Some(<#ty>::from_untyped(value, s.clone())?)
} else {
None
}
}
} else {
quote! {
- <#ty>::try_from(obj.get(#name.into())?.ok_or_else(|| Error::NoSuchField(#name.into()))?)?
+ <#ty>::from_untyped(obj.get(s.clone(), #name.into())?.ok_or_else(|| Error::NoSuchField(#name.into()))?, s.clone())?
}
};
@@ -493,39 +538,33 @@
#ident: #value,
}
}
- fn expand_serialize(&self) -> TokenStream {
- let ident = self.ident();
- if let Some(name) = self.name() {
- if self.is_option() {
+ fn expand_serialize(&self) -> Result<TokenStream> {
+ let ident = &self.ident;
+ let ty = &self.ty;
+ Ok(if let Some(name) = self.name() {
+ if self.is_option {
quote! {
if let Some(value) = self.#ident {
- out.member(#name.into()).value(value.try_into()?)?;
+ out.member(#name.into()).value(s.clone(), <#ty>::into_untyped(value, s.clone())?)?;
}
}
} else {
quote! {
- out.member(#name.into()).value(self.#ident.try_into()?)?;
+ out.member(#name.into()).value(s.clone(), <#ty>::into_untyped(self.#ident, s.clone())?)?;
}
}
- } else if self.is_option() {
+ } else if self.is_option {
quote! {
if let Some(value) = self.#ident {
- value.serialize(out)?;
+ value.serialize(s.clone(), out)?;
}
}
} else {
quote! {
- self.#ident.serialize(out)?;
+ self.#ident.serialize(s.clone(), out)?;
}
- }
- }
-
- fn as_option(&self) -> Option<&Type> {
- extract_type_from_option(&self.0.ty).unwrap()
+ })
}
- fn is_option(&self) -> bool {
- self.as_option().is_some()
- }
}
#[proc_macro_derive(Typed, attributes(typed))]
@@ -548,7 +587,7 @@
let fields = data
.fields
.iter()
- .map(TypedField::try_new)
+ .map(TypedField::parse)
.collect::<Result<Vec<_>>>()?;
let typed = {
@@ -566,12 +605,12 @@
fn from_untyped(value: Val, s: State) -> Result<Self> {
let obj = value.as_obj().expect("shape is correct");
- Self::parse(&obj)
+ Self::parse(&obj, s)
}
fn into_untyped(value: Self, s: State) -> Result<Val> {
let mut out = ObjValueBuilder::new();
- value.serialize(&mut out)?;
+ value.serialize(s, &mut out)?;
Ok(Val::Obj(out.build()))
}
@@ -580,26 +619,29 @@
};
let fields_parse = fields.iter().map(TypedField::expand_parse);
- let fields_serialize = fields.iter().map(TypedField::expand_serialize);
+ let fields_serialize = fields
+ .iter()
+ .map(TypedField::expand_serialize)
+ .collect::<Result<Vec<_>>>()?;
Ok(quote! {
const _: () = {
use ::jrsonnet_evaluator::{
typed::{ComplexValType, Typed, TypedObj, CheckType},
- Val,
- error::{LocError, Error},
+ Val, State,
+ error::{LocError, Error, Result},
ObjValueBuilder, ObjValue,
};
#typed
- impl #ident {
- fn serialize(self, out: &mut ObjValueBuilder) -> Result<(), LocError> {
+ impl TypedObj for #ident {
+ fn serialize(self, s: State, out: &mut ObjValueBuilder) -> Result<(), LocError> {
#(#fields_serialize)*
Ok(())
}
- fn parse(obj: &ObjValue) -> Result<Self, LocError> {
+ fn parse(obj: &ObjValue, s: State) -> Result<Self, LocError> {
Ok(Self {
#(#fields_parse)*
})