difftreelog
feat derive(Typed) for struct
in: master
9 files changed
Cargo.lockdiffbeforeafterboth--- a/Cargo.lock
+++ b/Cargo.lock
@@ -70,8 +70,9 @@
[[package]]
name = "clap"
-version = "3.0.0-beta.2"
-source = "git+https://github.com/clap-rs/clap?rev=f0c5ea5e1503de5c8e74d8c047a799cf51498e83#f0c5ea5e1503de5c8e74d8c047a799cf51498e83"
+version = "3.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "71c47df61d9e16dc010b55dba1952a57d8c215dbb533fd13cdd13369aac73b1c"
dependencies = [
"atty",
"bitflags",
@@ -82,27 +83,28 @@
"strsim",
"termcolor",
"textwrap",
- "vec_map",
]
[[package]]
+name = "clap_complete"
+version = "3.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df6f3613c0a3cddfd78b41b10203eb322cb29b600cbdf808a7d3db95691b8e25"
+dependencies = [
+ "clap",
+]
+
+[[package]]
name = "clap_derive"
-version = "3.0.0-beta.2"
-source = "git+https://github.com/clap-rs/clap?rev=f0c5ea5e1503de5c8e74d8c047a799cf51498e83#f0c5ea5e1503de5c8e74d8c047a799cf51498e83"
+version = "3.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3aab4734e083b809aaf5794e14e756d1c798d2c69c7f7de7a09a2f5214993c1"
dependencies = [
"heck",
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
-]
-
-[[package]]
-name = "clap_generate"
-version = "3.0.0-beta.2"
-source = "git+https://github.com/clap-rs/clap?rev=f0c5ea5e1503de5c8e74d8c047a799cf51498e83#f0c5ea5e1503de5c8e74d8c047a799cf51498e83"
-dependencies = [
- "clap",
]
[[package]]
@@ -148,12 +150,9 @@
[[package]]
name = "heck"
-version = "0.3.3"
+version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
-dependencies = [
- "unicode-segmentation",
-]
+checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
[[package]]
name = "hermit-abi"
@@ -185,7 +184,7 @@
version = "0.4.2"
dependencies = [
"clap",
- "clap_generate",
+ "clap_complete",
"gcmodule",
"jrsonnet-cli",
"jrsonnet-evaluator",
@@ -214,6 +213,7 @@
"bincode",
"gcmodule",
"jrsonnet-interner",
+ "jrsonnet-macros",
"jrsonnet-parser",
"jrsonnet-stdlib",
"jrsonnet-types",
@@ -236,6 +236,15 @@
]
[[package]]
+name = "jrsonnet-macros"
+version = "0.4.2"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
name = "jrsonnet-parser"
version = "0.4.2"
dependencies = [
@@ -301,6 +310,12 @@
checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771"
[[package]]
+name = "memchr"
+version = "2.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
+
+[[package]]
name = "mimalloc-sys"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -321,9 +336,12 @@
[[package]]
name = "os_str_bytes"
-version = "3.1.0"
+version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6acbef58a60fe69ab50510a55bc8cdd4d6cf2283d27ad338f54cb52747a9cf2d"
+checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64"
+dependencies = [
+ "memchr",
+]
[[package]]
name = "parking_lot"
@@ -357,9 +375,9 @@
[[package]]
name = "peg"
-version = "0.7.0"
+version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "07c0b841ea54f523f7aa556956fbd293bcbe06f2e67d2eb732b7278aaf1d166a"
+checksum = "af728fe826811af3b38c37e93de6d104485953ea373d656eebae53d6987fcd2c"
dependencies = [
"peg-macros",
"peg-runtime",
@@ -367,9 +385,9 @@
[[package]]
name = "peg-macros"
-version = "0.7.0"
+version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b5aa52829b8decbef693af90202711348ab001456803ba2a98eb4ec8fb70844c"
+checksum = "4536be147b770b824895cbad934fccce8e49f14b4c4946eaa46a6e4a12fcdc16"
dependencies = [
"peg-runtime",
"proc-macro2",
@@ -378,9 +396,9 @@
[[package]]
name = "peg-runtime"
-version = "0.7.0"
+version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c719dcf55f09a3a7e764c6649ab594c18a177e3599c467983cdf644bfc0a4088"
+checksum = "f9b0efd3ba03c3a409d44d60425f279ec442bcf0b9e63ff4e410da31c8b0f69f"
[[package]]
name = "proc-macro-error"
@@ -536,12 +554,9 @@
[[package]]
name = "textwrap"
-version = "0.14.2"
+version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80"
-dependencies = [
- "unicode-width",
-]
+checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
[[package]]
name = "thiserror"
@@ -562,12 +577,6 @@
"quote",
"syn",
]
-
-[[package]]
-name = "unicode-segmentation"
-version = "1.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b"
[[package]]
name = "unicode-width"
@@ -580,12 +589,6 @@
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
-
-[[package]]
-name = "vec_map"
-version = "0.8.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]]
name = "version_check"
crates/jrsonnet-evaluator/src/error.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/error.rs
+++ b/crates/jrsonnet-evaluator/src/error.rs
@@ -179,7 +179,7 @@
}
}
-pub type Result<V> = std::result::Result<V, LocError>;
+pub type Result<V, E = LocError> = std::result::Result<V, E>;
#[macro_export]
macro_rules! throw {
crates/jrsonnet-evaluator/src/function.rsdiffbeforeafterboth1use crate::{2 error::{Error::*, LocError},3 evaluate, evaluate_named,4 gc::TraceBox,5 throw,6 typed::Typed,7 Context, FutureWrapper, GcHashMap, LazyVal, LazyValValue, Result, Val,8};9use gcmodule::Trace;10use jrsonnet_interner::IStr;11use jrsonnet_parser::{ArgsDesc, ExprLocation, LocExpr, ParamsDesc};12use std::{borrow::Cow, collections::HashMap, convert::TryFrom};1314#[derive(Trace)]15struct EvaluateLazyVal {16 context: Context,17 expr: LocExpr,18}19impl LazyValValue for EvaluateLazyVal {20 fn get(self: Box<Self>) -> Result<Val> {21 evaluate(self.context, &self.expr)22 }23}2425pub trait ArgLike {26 fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<LazyVal>;27}28impl ArgLike for &LocExpr {29 fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<LazyVal> {30 Ok(if tailstrict {31 LazyVal::new_resolved(evaluate(ctx, self)?)32 } else {33 LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {34 context: ctx,35 expr: (*self).clone(),36 })))37 })38 }39}40impl<T> ArgLike for T41where42 T: Typed + Clone,43 Val: TryFrom<T, Error = LocError>,44{45 fn evaluate_arg(&self, _ctx: Context, _tailstrict: bool) -> Result<LazyVal> {46 let val: Val = Val::try_from(self.clone())?;47 Ok(LazyVal::new_resolved(val))48 }49}50pub enum TlaArg {51 String(IStr),52 Code(LocExpr),53 Val(Val),54}55impl ArgLike for TlaArg {56 fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<LazyVal> {57 match self {58 TlaArg::String(s) => Ok(LazyVal::new_resolved(Val::Str(s.clone()))),59 TlaArg::Code(code) => Ok(if tailstrict {60 LazyVal::new_resolved(evaluate(ctx, code)?)61 } else {62 LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {63 context: ctx,64 expr: code.clone(),65 })))66 }),67 TlaArg::Val(val) => Ok(LazyVal::new_resolved(val.clone())),68 }69 }70}7172pub trait ArgsLike {73 fn unnamed_len(&self) -> usize;74 fn unnamed_iter(75 &self,76 ctx: Context,77 tailstrict: bool,78 handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,79 ) -> Result<()>;80 fn named_iter(81 &self,82 ctx: Context,83 tailstrict: bool,84 handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,85 ) -> Result<()>;86 fn named_names(&self, handler: &mut dyn FnMut(&IStr));87}8889impl ArgsLike for ArgsDesc {90 fn unnamed_len(&self) -> usize {91 self.unnamed.len()92 }9394 fn unnamed_iter(95 &self,96 ctx: Context,97 tailstrict: bool,98 handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,99 ) -> Result<()> {100 for (id, arg) in self.unnamed.iter().enumerate() {101 handler(102 id,103 if tailstrict {104 LazyVal::new_resolved(evaluate(ctx.clone(), arg)?)105 } else {106 LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {107 context: ctx.clone(),108 expr: arg.clone(),109 })))110 },111 )?;112 }113 Ok(())114 }115116 fn named_iter(117 &self,118 ctx: Context,119 tailstrict: bool,120 handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,121 ) -> Result<()> {122 for (name, arg) in self.named.iter() {123 handler(124 name,125 if tailstrict {126 LazyVal::new_resolved(evaluate(ctx.clone(), arg)?)127 } else {128 LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {129 context: ctx.clone(),130 expr: arg.clone(),131 })))132 },133 )?;134 }135 Ok(())136 }137138 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {139 for (name, _) in self.named.iter() {140 handler(name)141 }142 }143}144145impl<A: ArgLike> ArgsLike for [(IStr, A)] {146 fn unnamed_len(&self) -> usize {147 0148 }149150 fn unnamed_iter(151 &self,152 _ctx: Context,153 _tailstrict: bool,154 _handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,155 ) -> Result<()> {156 Ok(())157 }158159 fn named_iter(160 &self,161 ctx: Context,162 tailstrict: bool,163 handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,164 ) -> Result<()> {165 for (name, val) in self.iter() {166 handler(name, val.evaluate_arg(ctx.clone(), tailstrict)?)?;167 }168 Ok(())169 }170171 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {172 for (name, _) in self.iter() {173 handler(name);174 }175 }176}177178impl<A: ArgLike> ArgsLike for HashMap<IStr, A> {179 fn unnamed_len(&self) -> usize {180 0181 }182183 fn unnamed_iter(184 &self,185 _ctx: Context,186 _tailstrict: bool,187 _handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,188 ) -> Result<()> {189 Ok(())190 }191192 fn named_iter(193 &self,194 ctx: Context,195 tailstrict: bool,196 handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,197 ) -> Result<()> {198 for (name, value) in self.iter() {199 handler(name, value.evaluate_arg(ctx.clone(), tailstrict)?)?;200 }201 Ok(())202 }203204 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {205 for (name, _) in self.iter() {206 handler(name);207 }208 }209}210211impl<A: ArgLike> ArgsLike for [A] {212 fn unnamed_len(&self) -> usize {213 self.len()214 }215216 fn unnamed_iter(217 &self,218 ctx: Context,219 tailstrict: bool,220 handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,221 ) -> Result<()> {222 for (i, arg) in self.iter().enumerate() {223 handler(i, arg.evaluate_arg(ctx.clone(), tailstrict)?)?;224 }225 Ok(())226 }227228 fn named_iter(229 &self,230 _ctx: Context,231 _tailstrict: bool,232 _handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,233 ) -> Result<()> {234 Ok(())235 }236237 fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}238}239impl<A: ArgLike> ArgsLike for &[A] {240 fn unnamed_len(&self) -> usize {241 (*self).unnamed_len()242 }243244 fn unnamed_iter(245 &self,246 ctx: Context,247 tailstrict: bool,248 handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,249 ) -> Result<()> {250 (*self).unnamed_iter(ctx, tailstrict, handler)251 }252253 fn named_iter(254 &self,255 ctx: Context,256 tailstrict: bool,257 handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,258 ) -> Result<()> {259 (*self).named_iter(ctx, tailstrict, handler)260 }261262 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {263 (*self).named_names(handler)264 }265}266267/// Creates correct [context](Context) for function body evaluation returning error on invalid call.268///269/// ## Parameters270/// * `ctx`: used for passed argument expressions' execution and for body execution (if `body_ctx` is not set)271/// * `body_ctx`: used for default parameter values' execution and for body execution (if set)272/// * `params`: function parameters' definition273/// * `args`: passed function arguments274/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily275pub fn parse_function_call(276 ctx: Context,277 body_ctx: Context,278 params: &ParamsDesc,279 args: &dyn ArgsLike,280 tailstrict: bool,281) -> Result<Context> {282 let mut passed_args = GcHashMap::with_capacity(params.len());283 if args.unnamed_len() > params.len() {284 throw!(TooManyArgsFunctionHas(params.len()))285 }286287 let mut filled_args = 0;288289 args.unnamed_iter(ctx.clone(), tailstrict, &mut |id, arg| {290 let name = params[id].0.clone();291 passed_args.insert(name, arg);292 filled_args += 1;293 Ok(())294 })?;295296 args.named_iter(ctx, tailstrict, &mut |name, value| {297 // FIXME: O(n) for arg existence check298 if !params.iter().any(|p| &p.0 == name) {299 throw!(UnknownFunctionParameter((name as &str).to_owned()));300 }301 if passed_args.insert(name.clone(), value).is_some() {302 throw!(BindingParameterASecondTime(name.clone()));303 }304 filled_args += 1;305 Ok(())306 })?;307308 if filled_args < params.len() {309 // Some args are unset, but maybe we have defaults for them310 // Default values should be created in newly created context311 let future_context = FutureWrapper::<Context>::new();312 let mut defaults = GcHashMap::with_capacity(params.len() - filled_args);313314 for param in params.iter().filter(|p| p.1.is_some()) {315 if passed_args.contains_key(¶m.0.clone()) {316 continue;317 }318 #[derive(Trace)]319 struct LazyNamedBinding {320 future_context: FutureWrapper<Context>,321 name: IStr,322 value: LocExpr,323 }324 impl LazyValValue for LazyNamedBinding {325 fn get(self: Box<Self>) -> Result<Val> {326 evaluate_named(self.future_context.unwrap(), &self.value, self.name)327 }328 }329 LazyVal::new(TraceBox(Box::new(LazyNamedBinding {330 future_context: future_context.clone(),331 name: param.0.clone(),332 value: param.1.clone().unwrap(),333 })));334335 defaults.insert(336 param.0.clone(),337 LazyVal::new(TraceBox(Box::new(LazyNamedBinding {338 future_context: future_context.clone(),339 name: param.0.clone(),340 value: param.1.clone().unwrap(),341 }))),342 );343 filled_args += 1;344 }345346 // Some args still wasn't filled347 if filled_args != params.len() {348 for param in params.iter().skip(args.unnamed_len()) {349 let mut found = false;350 args.named_names(&mut |name| {351 if name == ¶m.0 {352 found = true;353 }354 });355 if !found {356 throw!(FunctionParameterNotBoundInCall(param.0.clone()));357 }358 }359 unreachable!();360 }361362 Ok(body_ctx363 .extend(passed_args, None, None, None)364 .extend_bound(defaults)365 .into_future(future_context))366 } else {367 let body_ctx = body_ctx.extend(passed_args, None, None, None);368 Ok(body_ctx)369 }370}371372type BuiltinParamName = Cow<'static, str>;373374#[derive(Clone, Trace)]375pub struct BuiltinParam {376 pub name: BuiltinParamName,377 pub has_default: bool,378}379380pub trait Builtin: Trace {381 fn name(&self) -> &str;382 fn params(&self) -> &[BuiltinParam];383 fn call(384 &self,385 context: Context,386 loc: Option<&ExprLocation>,387 args: &dyn ArgsLike,388 ) -> Result<Val>;389}390391pub trait StaticBuiltin: Builtin + Send + Sync392where393 Self: 'static,394{395 // In impl, to make it object safe:396 // const INST: &'static Self;397}398399/// You shouldn't probally use this function, use jrsonnet_macros::builtin instead400///401/// ## Parameters402/// * `ctx`: used for passed argument expressions' execution and for body execution (if `body_ctx` is not set)403/// * `params`: function parameters' definition404/// * `args`: passed function arguments405/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily406pub fn parse_builtin_call(407 ctx: Context,408 params: &[BuiltinParam],409 args: &dyn ArgsLike,410 tailstrict: bool,411) -> Result<GcHashMap<BuiltinParamName, LazyVal>> {412 let mut passed_args = GcHashMap::with_capacity(params.len());413 if args.unnamed_len() > params.len() {414 throw!(TooManyArgsFunctionHas(params.len()))415 }416417 let mut filled_args = 0;418419 args.unnamed_iter(ctx.clone(), tailstrict, &mut |id, arg| {420 let name = params[id].name.clone();421 passed_args.insert(name, arg);422 filled_args += 1;423 Ok(())424 })?;425426 args.named_iter(ctx, tailstrict, &mut |name, arg| {427 // FIXME: O(n) for arg existence check428 let p = params429 .iter()430 .find(|p| p.name == name as &str)431 .ok_or_else(|| UnknownFunctionParameter((name as &str).to_owned()))?;432 if passed_args.insert(p.name.clone(), arg).is_some() {433 throw!(BindingParameterASecondTime(name.clone()));434 }435 filled_args += 1;436 Ok(())437 })?;438439 if filled_args < params.len() {440 for param in params.iter().filter(|p| p.has_default) {441 if passed_args.contains_key(¶m.name) {442 continue;443 }444 filled_args += 1;445 }446447 // Some args still wasn't filled448 if filled_args != params.len() {449 for param in params.iter().skip(args.unnamed_len()) {450 let mut found = false;451 args.named_names(&mut |name| {452 if name as &str == ¶m.name as &str {453 found = true;454 }455 });456 if !found {457 throw!(FunctionParameterNotBoundInCall(param.name.clone().into()));458 }459 }460 unreachable!();461 }462 }463 Ok(passed_args)464}crates/jrsonnet-evaluator/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/lib.rs
+++ b/crates/jrsonnet-evaluator/src/lib.rs
@@ -174,7 +174,11 @@
pub(crate) static EVAL_STATE: RefCell<Option<EvaluationState>> = RefCell::new(None)
}
pub(crate) fn with_state<T>(f: impl FnOnce(&EvaluationState) -> T) -> T {
- EVAL_STATE.with(|s| f(s.borrow().as_ref().unwrap()))
+ EVAL_STATE.with(|s| {
+ f(s.borrow().as_ref().expect(
+ "missing evaluation state, some functions should be called inside of run_in_state call",
+ ))
+ })
}
pub fn push_frame<T>(
e: Option<&ExprLocation>,
@@ -728,12 +732,15 @@
}
macro_rules! eval {
- ($str: expr) => {
- EvaluationState::default()
- .with_stdlib()
- .evaluate_snippet_raw(PathBuf::from("raw.jsonnet").into(), $str.into())
- .unwrap()
- };
+ ($str: expr) => {{
+ let evaluator = EvaluationState::default();
+ evaluator.with_stdlib();
+ evaluator.run_in_state(|| {
+ evaluator
+ .evaluate_snippet_raw(PathBuf::from("raw.jsonnet").into(), $str.into())
+ .unwrap()
+ })
+ }};
}
macro_rules! eval_json {
($str: expr) => {{
@@ -1265,4 +1272,47 @@
assert_eval!(r#"std.assertEqual(std.count(["a", "b", "a"], "d"), 0)"#);
assert_eval!(r#"std.assertEqual(std.count(["a", "b", "a"], "a"), 2)"#);
}
+
+ mod derive_typed {
+ use crate::{typed::Typed, EvaluationState};
+ use std::path::PathBuf;
+
+ #[derive(Typed, PartialEq, Debug)]
+ struct MyTyped {
+ a: u32,
+ b: String,
+ }
+
+ #[test]
+ fn test() {
+ let es = EvaluationState::default();
+ let val = eval!("{a: 14, b: 'Hello, world!'}");
+ let typed = es.run_in_state(|| MyTyped::try_from(val).unwrap());
+
+ assert_eq!(
+ typed,
+ MyTyped {
+ a: 14,
+ b: "Hello, world!".to_string()
+ }
+ );
+ es.settings_mut().globals.insert(
+ "mytyped".into(),
+ es.run_in_state(|| typed.try_into()).unwrap(),
+ );
+
+ let v = es
+ .evaluate_snippet_raw(
+ PathBuf::from("raw.jsonnet").into(),
+ "
+ mytyped == {a: 14, b: 'Hello, world!'}
+ "
+ .into(),
+ )
+ .unwrap()
+ .as_bool()
+ .unwrap();
+ assert!(v)
+ }
+ }
}
crates/jrsonnet-evaluator/src/typed/conversions.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/typed/conversions.rs
+++ b/crates/jrsonnet-evaluator/src/typed/conversions.rs
@@ -1,6 +1,7 @@
use std::convert::{TryFrom, TryInto};
use jrsonnet_interner::IStr;
+pub use jrsonnet_macros::Typed;
use jrsonnet_types::{ComplexValType, ValType};
use crate::{
crates/jrsonnet-evaluator/src/typed/mod.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/typed/mod.rs
+++ b/crates/jrsonnet-evaluator/src/typed/mod.rs
@@ -8,20 +8,8 @@
push_description_frame, Val,
};
use gcmodule::Trace;
-use jrsonnet_types::{ComplexValType, ValType};
+pub use jrsonnet_types::{ComplexValType, ValType};
use thiserror::Error;
-
-#[macro_export]
-macro_rules! unwrap_type {
- ($desc:expr, $value:expr, $typ:expr => $match:path) => {{
- use $crate::{push_frame, typed::CheckType};
- push_frame(None, $desc, || Ok($typ.check(&$value)?))?;
- match $value {
- $match(v) => v,
- _ => unreachable!(),
- }
- }};
-}
#[derive(Debug, Error, Clone, Trace)]
pub enum TypeError {
@@ -136,7 +124,7 @@
impl Display for ValuePathItem {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
- Self::Field(name) => write!(f, ".{}", name)?,
+ Self::Field(name) => write!(f, ".{:?}", name)?,
Self::Index(idx) => write!(f, "[{}]", idx)?,
}
Ok(())
crates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/val.rs
+++ b/crates/jrsonnet-evaluator/src/val.rs
@@ -91,7 +91,7 @@
Normal(Cc<FuncDesc>),
/// Standard library function
StaticBuiltin(#[skip_trace] &'static dyn StaticBuiltin),
-
+ /// User-provided function
Builtin(Cc<TraceBox<dyn Builtin>>),
}
@@ -99,8 +99,10 @@
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Normal(arg0) => f.debug_tuple("Normal").field(arg0).finish(),
- Self::StaticBuiltin(arg0) => f.debug_tuple("Intrinsic").field(&arg0.name()).finish(),
- Self::Builtin(arg0) => f.debug_tuple("Intrinsic").field(&arg0.name()).finish(),
+ Self::StaticBuiltin(arg0) => {
+ f.debug_tuple("StaticBuiltin").field(&arg0.name()).finish()
+ }
+ Self::Builtin(arg0) => f.debug_tuple("Builtin").field(&arg0.name()).finish(),
}
}
}
@@ -338,6 +340,49 @@
}
impl Val {
+ pub fn as_bool(&self) -> Option<bool> {
+ match self {
+ Val::Bool(v) => Some(*v),
+ _ => None,
+ }
+ }
+ pub fn as_null(&self) -> Option<()> {
+ match self {
+ Val::Null => Some(()),
+ _ => None,
+ }
+ }
+ pub fn as_str(&self) -> Option<IStr> {
+ match self {
+ Val::Str(s) => Some(s.clone()),
+ _ => None,
+ }
+ }
+ pub fn as_num(&self) -> Option<f64> {
+ match self {
+ Val::Num(n) => Some(*n),
+ _ => None,
+ }
+ }
+ pub fn as_arr(&self) -> Option<ArrValue> {
+ match self {
+ Val::Arr(a) => Some(a.clone()),
+ _ => None,
+ }
+ }
+ pub fn as_obj(&self) -> Option<ObjValue> {
+ match self {
+ Val::Obj(o) => Some(o.clone()),
+ _ => None,
+ }
+ }
+ pub fn as_func(&self) -> Option<FuncVal> {
+ match self {
+ Val::Func(f) => Some(f.clone()),
+ _ => None,
+ }
+ }
+
/// Creates `Val::Num` after checking for numeric overflow.
/// As numbers are `f64`, we can just check for their finity.
pub fn new_checked_num(num: f64) -> Result<Self> {
crates/jrsonnet-macros/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-macros/src/lib.rs
+++ b/crates/jrsonnet-macros/src/lib.rs
@@ -1,8 +1,8 @@
use quote::{quote, quote_spanned};
use syn::{
parenthesized, parse::Parse, parse_macro_input, punctuated::Punctuated, spanned::Spanned,
- token::Comma, FnArg, GenericArgument, Ident, ItemFn, Pat, PatType, Path, PathArguments, Token,
- Type,
+ token::Comma, DeriveInput, FnArg, GenericArgument, Ident, ItemFn, Pat, PatType, Path,
+ PathArguments, Token, Type,
};
fn is_location_arg(t: &PatType) -> bool {
@@ -254,3 +254,86 @@
})
.into()
}
+
+#[proc_macro_derive(Typed)]
+pub fn derive_typed(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
+ let input = parse_macro_input!(item as DeriveInput);
+ let data = match &input.data {
+ syn::Data::Struct(s) => s,
+ _ => {
+ return syn::Error::new(input.span(), "only structs supported")
+ .to_compile_error()
+ .into()
+ }
+ };
+
+ let ident = &input.ident;
+
+ let fields_def = data.fields.iter().map(|f| {
+ let name = f
+ .ident
+ .as_ref()
+ .expect("only named fields supported")
+ .to_string();
+ let ty = &f.ty;
+ quote! {
+ (#name, #ty::TYPE),
+ }
+ });
+ let fields_parse = data.fields.iter().map(|f| {
+ let ident = f.ident.as_ref().unwrap();
+ let name = ident.to_string();
+ let ty = &f.ty;
+ quote! {
+ #ident: #ty::try_from(obj.get(#name.into())?.expect("shape is correct"))?,
+ }
+ });
+ let fields_serialize = data.fields.iter().map(|f| {
+ let ident = f.ident.as_ref().unwrap();
+ let name = ident.to_string();
+ quote! {
+ out.member(#name.into()).value(self.#ident.try_into()?);
+ }
+ });
+ let field_count = data.fields.len();
+
+ quote! {
+ const _: () = {
+ use ::jrsonnet_evaluator::{
+ typed::{ComplexValType, Typed, CheckType},
+ Val,
+ error::LocError,
+ obj::ObjValueBuilder,
+ };
+
+ const ITEMS: [(&'static str, &'static ComplexValType); #field_count] = [
+ #(#fields_def)*
+ ];
+ impl Typed for #ident {
+ const TYPE: &'static ComplexValType = &ComplexValType::ObjectRef(&ITEMS);
+ }
+
+ impl TryFrom<Val> for #ident {
+ type Error = LocError;
+ fn try_from(value: Val) -> Result<Self, Self::Error> {
+ <Self as Typed>::TYPE.check(&value)?;
+ let obj = value.as_obj().expect("shape is correct");
+
+ Ok(Self {
+ #(#fields_parse)*
+ })
+ }
+ }
+ impl TryInto<Val> for #ident {
+ type Error = LocError;
+ fn try_into(self) -> Result<Val, Self::Error> {
+ let mut out = ObjValueBuilder::new();
+ #(#fields_serialize)*
+ Ok(Val::Obj(out.build()))
+ }
+ }
+ ()
+ };
+ }
+ .into()
+}
crates/jrsonnet-types/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-types/src/lib.rs
+++ b/crates/jrsonnet-types/src/lib.rs
@@ -122,7 +122,7 @@
BoundedNumber(Option<f64>, Option<f64>),
Array(Box<ComplexValType>),
ArrayRef(&'static ComplexValType),
- ObjectRef(&'static [(&'static str, ComplexValType)]),
+ ObjectRef(&'static [(&'static str, &'static ComplexValType)]),
Union(Vec<ComplexValType>),
UnionRef(&'static [&'static ComplexValType]),
Sum(Vec<ComplexValType>),