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.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/function.rs
+++ b/crates/jrsonnet-evaluator/src/function.rs
@@ -8,6 +8,7 @@
};
use gcmodule::Trace;
use jrsonnet_interner::IStr;
+pub use jrsonnet_macros::builtin;
use jrsonnet_parser::{ArgsDesc, ExprLocation, LocExpr, ParamsDesc};
use std::{borrow::Cow, collections::HashMap, convert::TryFrom};
@@ -377,6 +378,7 @@
pub has_default: bool,
}
+/// Do not implement it directly, instead use #[builtin] macro
pub trait Builtin: Trace {
fn name(&self) -> &str;
fn params(&self) -> &[BuiltinParam];
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.rsdiffbeforeafterboth1use std::convert::{TryFrom, TryInto};23use jrsonnet_interner::IStr;4use jrsonnet_types::{ComplexValType, ValType};56use crate::{7 error::{Error::*, LocError, Result},8 throw,9 typed::CheckType,10 ArrValue, FuncVal, IndexableVal, ObjValue, Val,11};1213pub trait Typed: TryFrom<Val, Error = LocError> + TryInto<Val, Error = LocError> {14 const TYPE: &'static ComplexValType;15}1617macro_rules! impl_int {18 ($($ty:ty)*) => {$(19 impl Typed for $ty {20 const TYPE: &'static ComplexValType =21 &ComplexValType::BoundedNumber(Some(Self::MIN as f64), Some(Self::MAX as f64));22 }23 impl TryFrom<Val> for $ty {24 type Error = LocError;2526 fn try_from(value: Val) -> Result<Self> {27 <Self as Typed>::TYPE.check(&value)?;28 match value {29 Val::Num(n) => {30 if n.trunc() != n {31 throw!(RuntimeError(32 format!(33 "cannot convert number with fractional part to {}",34 stringify!($ty)35 )36 .into()37 ))38 }39 Ok(n as Self)40 }41 _ => unreachable!(),42 }43 }44 }45 impl TryFrom<$ty> for Val {46 type Error = LocError;4748 fn try_from(value: $ty) -> Result<Self> {49 Ok(Self::Num(value as f64))50 }51 }52 )*};53}5455impl_int!(i8 u8 i16 u16 i32 u32);5657impl Typed for f64 {58 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Num);59}60impl TryFrom<Val> for f64 {61 type Error = LocError;6263 fn try_from(value: Val) -> Result<Self> {64 <Self as Typed>::TYPE.check(&value)?;65 match value {66 Val::Num(n) => Ok(n),67 _ => unreachable!(),68 }69 }70}71impl TryFrom<f64> for Val {72 type Error = LocError;7374 fn try_from(value: f64) -> Result<Self> {75 Ok(Self::Num(value))76 }77}7879pub struct PositiveF64(pub f64);80impl Typed for PositiveF64 {81 const TYPE: &'static ComplexValType = &ComplexValType::BoundedNumber(Some(0.0), None);82}83impl TryFrom<Val> for PositiveF64 {84 type Error = LocError;8586 fn try_from(value: Val) -> Result<Self> {87 <Self as Typed>::TYPE.check(&value)?;88 match value {89 Val::Num(n) => Ok(Self(n)),90 _ => unreachable!(),91 }92 }93}94impl TryFrom<PositiveF64> for Val {95 type Error = LocError;9697 fn try_from(value: PositiveF64) -> Result<Self> {98 Ok(Self::Num(value.0))99 }100}101102impl Typed for usize {103 // It is possible to store 54 bits of precision in f64, but leaving u32::MAX here for compatibility104 const TYPE: &'static ComplexValType =105 &ComplexValType::BoundedNumber(Some(0.0), Some(4294967295.0));106}107impl TryFrom<Val> for usize {108 type Error = LocError;109110 fn try_from(value: Val) -> Result<Self> {111 <Self as Typed>::TYPE.check(&value)?;112 match value {113 Val::Num(n) => {114 if n.trunc() != n {115 throw!(RuntimeError(116 "cannot convert number with fractional part to usize".into()117 ))118 }119 Ok(n as Self)120 }121 _ => unreachable!(),122 }123 }124}125impl TryFrom<usize> for Val {126 type Error = LocError;127128 fn try_from(value: usize) -> Result<Self> {129 if value > u32::MAX as usize {130 throw!(RuntimeError("number is too large".into()))131 }132 Ok(Self::Num(value as f64))133 }134}135136impl Typed for IStr {137 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Str);138}139impl TryFrom<Val> for IStr {140 type Error = LocError;141142 fn try_from(value: Val) -> Result<Self> {143 <Self as Typed>::TYPE.check(&value)?;144 match value {145 Val::Str(s) => Ok(s),146 _ => unreachable!(),147 }148 }149}150impl TryFrom<IStr> for Val {151 type Error = LocError;152153 fn try_from(value: IStr) -> Result<Self> {154 Ok(Self::Str(value))155 }156}157158impl Typed for String {159 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Str);160}161impl TryFrom<Val> for String {162 type Error = LocError;163164 fn try_from(value: Val) -> Result<Self> {165 <Self as Typed>::TYPE.check(&value)?;166 match value {167 Val::Str(s) => Ok(s.to_string()),168 _ => unreachable!(),169 }170 }171}172impl TryFrom<String> for Val {173 type Error = LocError;174175 fn try_from(value: String) -> Result<Self> {176 Ok(Self::Str(value.into()))177 }178}179180impl Typed for char {181 const TYPE: &'static ComplexValType = &ComplexValType::Char;182}183impl TryFrom<Val> for char {184 type Error = LocError;185186 fn try_from(value: Val) -> Result<Self> {187 <Self as Typed>::TYPE.check(&value)?;188 match value {189 Val::Str(s) => Ok(s.chars().next().unwrap()),190 _ => unreachable!(),191 }192 }193}194impl TryFrom<char> for Val {195 type Error = LocError;196197 fn try_from(value: char) -> Result<Self> {198 Ok(Self::Str(value.to_string().into()))199 }200}201202impl<T> Typed for Vec<T>203where204 T: Typed,205 T: TryFrom<Val, Error = LocError>,206 T: TryInto<Val, Error = LocError>,207{208 const TYPE: &'static ComplexValType = &ComplexValType::ArrayRef(T::TYPE);209}210impl<T> TryFrom<Val> for Vec<T>211where212 T: Typed,213 T: TryFrom<Val, Error = LocError>,214 T: TryInto<Val, Error = LocError>,215{216 type Error = LocError;217218 fn try_from(value: Val) -> Result<Self> {219 <Self as Typed>::TYPE.check(&value)?;220 match value {221 Val::Arr(a) => {222 let mut o = Self::with_capacity(a.len());223 for i in a.iter() {224 o.push(T::try_from(i?)?);225 }226 Ok(o)227 }228 _ => unreachable!(),229 }230 }231}232impl<T> TryFrom<Vec<T>> for Val233where234 T: Typed,235 T: TryFrom<Self, Error = LocError>,236 T: TryInto<Self, Error = LocError>,237{238 type Error = LocError;239240 fn try_from(value: Vec<T>) -> Result<Self> {241 let mut o = Vec::with_capacity(value.len());242 for i in value {243 o.push(i.try_into()?);244 }245 Ok(Self::Arr(o.into()))246 }247}248249/// To be used in Vec<Any>250/// Regular Val can't be used here, because it has wrong TryFrom::Error type251#[derive(Clone)]252pub struct Any(pub Val);253254impl Typed for Any {255 const TYPE: &'static ComplexValType = &ComplexValType::Any;256}257impl TryFrom<Val> for Any {258 type Error = LocError;259260 fn try_from(value: Val) -> Result<Self> {261 Ok(Self(value))262 }263}264impl TryFrom<Any> for Val {265 type Error = LocError;266267 fn try_from(value: Any) -> Result<Self> {268 Ok(value.0)269 }270}271272/// Specialization, provides faster TryFrom<VecVal> for Val273pub struct VecVal(pub Vec<Val>);274275impl Typed for VecVal {276 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Arr);277}278impl TryFrom<Val> for VecVal {279 type Error = LocError;280281 fn try_from(value: Val) -> Result<Self> {282 <Self as Typed>::TYPE.check(&value)?;283 match value {284 Val::Arr(a) => Ok(Self(a.evaluated()?.to_vec())),285 _ => unreachable!(),286 }287 }288}289impl TryFrom<VecVal> for Val {290 type Error = LocError;291292 fn try_from(value: VecVal) -> Result<Self> {293 Ok(Self::Arr(value.0.into()))294 }295}296297pub struct M1;298impl Typed for M1 {299 const TYPE: &'static ComplexValType = &ComplexValType::BoundedNumber(Some(-1.0), Some(-1.0));300}301impl TryFrom<Val> for M1 {302 type Error = LocError;303304 fn try_from(value: Val) -> Result<Self> {305 <Self as Typed>::TYPE.check(&value)?;306 Ok(Self)307 }308}309impl TryFrom<M1> for Val {310 type Error = LocError;311312 fn try_from(_: M1) -> Result<Self> {313 Ok(Self::Num(-1.0))314 }315}316317macro_rules! decl_either {318 ($($name: ident, $($id: ident)*);*) => {$(319 pub enum $name<$($id),*> {320 $($id($id)),*321 }322 impl<$($id),*> Typed for $name<$($id),*>323 where324 $($id: Typed,)*325 {326 const TYPE: &'static ComplexValType = &ComplexValType::UnionRef(&[$($id::TYPE),*]);327 }328 impl<$($id),*> TryFrom<Val> for $name<$($id),*>329 where330 $($id: Typed,)*331 {332 type Error = LocError;333334 fn try_from(value: Val) -> Result<Self> {335 $(336 if $id::TYPE.check(&value).is_ok() {337 $id::try_from(value).map(Self::$id)338 } else339 )* {340 <Self as Typed>::TYPE.check(&value)?;341 unreachable!()342 }343 }344 }345 impl<$($id),*> TryFrom<$name<$($id),*>> for Val346 where347 $($id: Typed,)*348 {349 type Error = LocError;350 fn try_from(value: $name<$($id),*>) -> Result<Self> {351 match value {$(352 $name::$id(v) => v.try_into()353 ),*}354 }355 }356 )*}357}358decl_either!(359 Either1, A;360 Either2, A B;361 Either3, A B C;362 Either4, A B C D;363 Either5, A B C D E;364 Either6, A B C D E F;365 Either7, A B C D E F G366);367#[macro_export]368macro_rules! Either {369 ($a:ty) => {Either1<$a>};370 ($a:ty, $b:ty) => {Either2<$a, $b>};371 ($a:ty, $b:ty, $c:ty) => {Either3<$a, $b, $c>};372 ($a:ty, $b:ty, $c:ty, $d:ty) => {Either4<$a, $b, $c, $d>};373 ($a:ty, $b:ty, $c:ty, $d:ty, $e:ty) => {Either5<$a, $b, $c, $d, $e>};374 ($a:ty, $b:ty, $c:ty, $d:ty, $e:ty, $f:ty) => {Either6<$a, $b, $c, $d, $e, $f>};375 ($a:ty, $b:ty, $c:ty, $d:ty, $e:ty, $f:ty, $g:ty) => {Either7<$a, $b, $c, $d, $e, $f, $g>};376}377378pub type MyType = Either![u32, f64, String];379380impl Typed for ArrValue {381 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Arr);382}383impl TryFrom<Val> for ArrValue {384 type Error = LocError;385386 fn try_from(value: Val) -> Result<Self> {387 <Self as Typed>::TYPE.check(&value)?;388 match value {389 Val::Arr(a) => Ok(a),390 _ => unreachable!(),391 }392 }393}394impl TryFrom<ArrValue> for Val {395 type Error = LocError;396397 fn try_from(value: ArrValue) -> Result<Self> {398 Ok(Self::Arr(value))399 }400}401402impl Typed for FuncVal {403 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Func);404}405impl TryFrom<Val> for FuncVal {406 type Error = LocError;407408 fn try_from(value: Val) -> Result<Self> {409 <Self as Typed>::TYPE.check(&value)?;410 match value {411 Val::Func(a) => Ok(a),412 _ => unreachable!(),413 }414 }415}416impl TryFrom<FuncVal> for Val {417 type Error = LocError;418419 fn try_from(value: FuncVal) -> Result<Self> {420 Ok(Self::Func(value))421 }422}423impl Typed for ObjValue {424 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Obj);425}426impl TryFrom<Val> for ObjValue {427 type Error = LocError;428429 fn try_from(value: Val) -> Result<Self> {430 <Self as Typed>::TYPE.check(&value)?;431 match value {432 Val::Obj(a) => Ok(a),433 _ => unreachable!(),434 }435 }436}437impl TryFrom<ObjValue> for Val {438 type Error = LocError;439440 fn try_from(value: ObjValue) -> Result<Self> {441 Ok(Self::Obj(value))442 }443}444445impl Typed for bool {446 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Bool);447}448impl TryFrom<Val> for bool {449 type Error = LocError;450451 fn try_from(value: Val) -> Result<Self> {452 <Self as Typed>::TYPE.check(&value)?;453 match value {454 Val::Bool(a) => Ok(a),455 _ => unreachable!(),456 }457 }458}459impl TryFrom<bool> for Val {460 type Error = LocError;461462 fn try_from(value: bool) -> Result<Self> {463 Ok(Self::Bool(value))464 }465}466467impl Typed for IndexableVal {468 const TYPE: &'static ComplexValType = &ComplexValType::UnionRef(&[469 &ComplexValType::Simple(ValType::Arr),470 &ComplexValType::Simple(ValType::Str),471 ]);472}473impl TryFrom<Val> for IndexableVal {474 type Error = LocError;475476 fn try_from(value: Val) -> Result<Self> {477 <Self as Typed>::TYPE.check(&value)?;478 value.into_indexable()479 }480}481impl TryFrom<IndexableVal> for Val {482 type Error = LocError;483484 fn try_from(value: IndexableVal) -> Result<Self> {485 match value {486 IndexableVal::Str(s) => Ok(Self::Str(s)),487 IndexableVal::Arr(a) => Ok(Self::Arr(a)),488 }489 }490}491492pub struct Null;493impl Typed for Null {494 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Null);495}496impl TryFrom<Val> for Null {497 type Error = LocError;498499 fn try_from(value: Val) -> Result<Self> {500 <Self as Typed>::TYPE.check(&value)?;501 Ok(Self)502 }503}504impl TryFrom<Null> for Val {505 type Error = LocError;506507 fn try_from(_: Null) -> Result<Self> {508 Ok(Self::Null)509 }510}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>),