git.delta.rocks / jrsonnet / refs/commits / 78d82dfdf2a1

difftreelog

feat derive(Typed) for struct

Yaroslav Bolyukin2022-04-04parent: #d710b4f.patch.diff
in: master

9 files changed

modifiedCargo.lockdiffbeforeafterboth
7070
71[[package]]71[[package]]
72name = "clap"72name = "clap"
73version = "3.0.0-beta.2"73version = "3.1.8"
74source = "git+https://github.com/clap-rs/clap?rev=f0c5ea5e1503de5c8e74d8c047a799cf51498e83#f0c5ea5e1503de5c8e74d8c047a799cf51498e83"74source = "registry+https://github.com/rust-lang/crates.io-index"
75checksum = "71c47df61d9e16dc010b55dba1952a57d8c215dbb533fd13cdd13369aac73b1c"
75dependencies = [76dependencies = [
76 "atty",77 "atty",
77 "bitflags",78 "bitflags",
82 "strsim",83 "strsim",
83 "termcolor",84 "termcolor",
84 "textwrap",85 "textwrap",
85 "vec_map",
86]86]
8787
88[[package]]88[[package]]
89name = "clap_derive"89name = "clap_complete"
90version = "3.0.0-beta.2"90version = "3.1.1"
91source = "git+https://github.com/clap-rs/clap?rev=f0c5ea5e1503de5c8e74d8c047a799cf51498e83#f0c5ea5e1503de5c8e74d8c047a799cf51498e83"91source = "registry+https://github.com/rust-lang/crates.io-index"
92checksum = "df6f3613c0a3cddfd78b41b10203eb322cb29b600cbdf808a7d3db95691b8e25"
92dependencies = [93dependencies = [
93 "heck",94 "clap",
94 "proc-macro-error",
95 "proc-macro2",
96 "quote",
97 "syn",
98]95]
9996
100[[package]]97[[package]]
101name = "clap_generate"98name = "clap_derive"
102version = "3.0.0-beta.2"99version = "3.1.7"
103source = "git+https://github.com/clap-rs/clap?rev=f0c5ea5e1503de5c8e74d8c047a799cf51498e83#f0c5ea5e1503de5c8e74d8c047a799cf51498e83"100source = "registry+https://github.com/rust-lang/crates.io-index"
101checksum = "a3aab4734e083b809aaf5794e14e756d1c798d2c69c7f7de7a09a2f5214993c1"
104dependencies = [102dependencies = [
105 "clap",103 "heck",
104 "proc-macro-error",
105 "proc-macro2",
106 "quote",
107 "syn",
106]108]
107109
108[[package]]110[[package]]
148150
149[[package]]151[[package]]
150name = "heck"152name = "heck"
151version = "0.3.3"153version = "0.4.0"
152source = "registry+https://github.com/rust-lang/crates.io-index"154source = "registry+https://github.com/rust-lang/crates.io-index"
153checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"155checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
154dependencies = [
155 "unicode-segmentation",
156]
157156
158[[package]]157[[package]]
159name = "hermit-abi"158name = "hermit-abi"
185version = "0.4.2"184version = "0.4.2"
186dependencies = [185dependencies = [
187 "clap",186 "clap",
188 "clap_generate",187 "clap_complete",
189 "gcmodule",188 "gcmodule",
190 "jrsonnet-cli",189 "jrsonnet-cli",
191 "jrsonnet-evaluator",190 "jrsonnet-evaluator",
214 "bincode",213 "bincode",
215 "gcmodule",214 "gcmodule",
216 "jrsonnet-interner",215 "jrsonnet-interner",
216 "jrsonnet-macros",
217 "jrsonnet-parser",217 "jrsonnet-parser",
218 "jrsonnet-stdlib",218 "jrsonnet-stdlib",
219 "jrsonnet-types",219 "jrsonnet-types",
235 "serde",235 "serde",
236]236]
237
238[[package]]
239name = "jrsonnet-macros"
240version = "0.4.2"
241dependencies = [
242 "proc-macro2",
243 "quote",
244 "syn",
245]
237246
238[[package]]247[[package]]
239name = "jrsonnet-parser"248name = "jrsonnet-parser"
300source = "registry+https://github.com/rust-lang/crates.io-index"309source = "registry+https://github.com/rust-lang/crates.io-index"
301checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771"310checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771"
311
312[[package]]
313name = "memchr"
314version = "2.4.1"
315source = "registry+https://github.com/rust-lang/crates.io-index"
316checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
302317
303[[package]]318[[package]]
304name = "mimalloc-sys"319name = "mimalloc-sys"
321336
322[[package]]337[[package]]
323name = "os_str_bytes"338name = "os_str_bytes"
324version = "3.1.0"339version = "6.0.0"
325source = "registry+https://github.com/rust-lang/crates.io-index"340source = "registry+https://github.com/rust-lang/crates.io-index"
326checksum = "6acbef58a60fe69ab50510a55bc8cdd4d6cf2283d27ad338f54cb52747a9cf2d"341checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64"
342dependencies = [
343 "memchr",
344]
327345
328[[package]]346[[package]]
329name = "parking_lot"347name = "parking_lot"
357375
358[[package]]376[[package]]
359name = "peg"377name = "peg"
360version = "0.7.0"378version = "0.8.0"
361source = "registry+https://github.com/rust-lang/crates.io-index"379source = "registry+https://github.com/rust-lang/crates.io-index"
362checksum = "07c0b841ea54f523f7aa556956fbd293bcbe06f2e67d2eb732b7278aaf1d166a"380checksum = "af728fe826811af3b38c37e93de6d104485953ea373d656eebae53d6987fcd2c"
363dependencies = [381dependencies = [
364 "peg-macros",382 "peg-macros",
365 "peg-runtime",383 "peg-runtime",
366]384]
367385
368[[package]]386[[package]]
369name = "peg-macros"387name = "peg-macros"
370version = "0.7.0"388version = "0.8.0"
371source = "registry+https://github.com/rust-lang/crates.io-index"389source = "registry+https://github.com/rust-lang/crates.io-index"
372checksum = "b5aa52829b8decbef693af90202711348ab001456803ba2a98eb4ec8fb70844c"390checksum = "4536be147b770b824895cbad934fccce8e49f14b4c4946eaa46a6e4a12fcdc16"
373dependencies = [391dependencies = [
374 "peg-runtime",392 "peg-runtime",
375 "proc-macro2",393 "proc-macro2",
378396
379[[package]]397[[package]]
380name = "peg-runtime"398name = "peg-runtime"
381version = "0.7.0"399version = "0.8.0"
382source = "registry+https://github.com/rust-lang/crates.io-index"400source = "registry+https://github.com/rust-lang/crates.io-index"
383checksum = "c719dcf55f09a3a7e764c6649ab594c18a177e3599c467983cdf644bfc0a4088"401checksum = "f9b0efd3ba03c3a409d44d60425f279ec442bcf0b9e63ff4e410da31c8b0f69f"
384402
385[[package]]403[[package]]
386name = "proc-macro-error"404name = "proc-macro-error"
536554
537[[package]]555[[package]]
538name = "textwrap"556name = "textwrap"
539version = "0.14.2"557version = "0.15.0"
540source = "registry+https://github.com/rust-lang/crates.io-index"558source = "registry+https://github.com/rust-lang/crates.io-index"
541checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80"559checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
542dependencies = [
543 "unicode-width",
544]
545560
546[[package]]561[[package]]
547name = "thiserror"562name = "thiserror"
563 "syn",578 "syn",
564]579]
565
566[[package]]
567name = "unicode-segmentation"
568version = "1.8.0"
569source = "registry+https://github.com/rust-lang/crates.io-index"
570checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b"
571580
572[[package]]581[[package]]
573name = "unicode-width"582name = "unicode-width"
581source = "registry+https://github.com/rust-lang/crates.io-index"590source = "registry+https://github.com/rust-lang/crates.io-index"
582checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"591checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
583
584[[package]]
585name = "vec_map"
586version = "0.8.2"
587source = "registry+https://github.com/rust-lang/crates.io-index"
588checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
589592
590[[package]]593[[package]]
591name = "version_check"594name = "version_check"
modifiedcrates/jrsonnet-evaluator/src/error.rsdiffbeforeafterboth
179 }179 }
180}180}
181181
182pub type Result<V> = std::result::Result<V, LocError>;182pub type Result<V, E = LocError> = std::result::Result<V, E>;
183183
184#[macro_export]184#[macro_export]
185macro_rules! throw {185macro_rules! throw {
modifiedcrates/jrsonnet-evaluator/src/function.rsdiffbeforeafterboth
8};8};
9use gcmodule::Trace;9use gcmodule::Trace;
10use jrsonnet_interner::IStr;10use jrsonnet_interner::IStr;
11pub use jrsonnet_macros::builtin;
11use jrsonnet_parser::{ArgsDesc, ExprLocation, LocExpr, ParamsDesc};12use jrsonnet_parser::{ArgsDesc, ExprLocation, LocExpr, ParamsDesc};
12use std::{borrow::Cow, collections::HashMap, convert::TryFrom};13use std::{borrow::Cow, collections::HashMap, convert::TryFrom};
1314
377 pub has_default: bool,378 pub has_default: bool,
378}379}
379380
381/// Do not implement it directly, instead use #[builtin] macro
380pub trait Builtin: Trace {382pub trait Builtin: Trace {
381 fn name(&self) -> &str;383 fn name(&self) -> &str;
382 fn params(&self) -> &[BuiltinParam];384 fn params(&self) -> &[BuiltinParam];
modifiedcrates/jrsonnet-evaluator/src/lib.rsdiffbeforeafterboth
174 pub(crate) static EVAL_STATE: RefCell<Option<EvaluationState>> = RefCell::new(None)174 pub(crate) static EVAL_STATE: RefCell<Option<EvaluationState>> = RefCell::new(None)
175}175}
176pub(crate) fn with_state<T>(f: impl FnOnce(&EvaluationState) -> T) -> T {176pub(crate) fn with_state<T>(f: impl FnOnce(&EvaluationState) -> T) -> T {
177 EVAL_STATE.with(|s| f(s.borrow().as_ref().unwrap()))177 EVAL_STATE.with(|s| {
178 f(s.borrow().as_ref().expect(
179 "missing evaluation state, some functions should be called inside of run_in_state call",
180 ))
181 })
178}182}
179pub fn push_frame<T>(183pub fn push_frame<T>(
180 e: Option<&ExprLocation>,184 e: Option<&ExprLocation>,
728 }732 }
729733
730 macro_rules! eval {734 macro_rules! eval {
731 ($str: expr) => {735 ($str: expr) => {{
732 EvaluationState::default()736 let evaluator = EvaluationState::default();
733 .with_stdlib()737 evaluator.with_stdlib();
734 .evaluate_snippet_raw(PathBuf::from("raw.jsonnet").into(), $str.into())738 evaluator.run_in_state(|| {
739 evaluator
740 .evaluate_snippet_raw(PathBuf::from("raw.jsonnet").into(), $str.into())
735 .unwrap()741 .unwrap()
742 })
736 };743 }};
737 }744 }
738 macro_rules! eval_json {745 macro_rules! eval_json {
739 ($str: expr) => {{746 ($str: expr) => {{
1266 assert_eval!(r#"std.assertEqual(std.count(["a", "b", "a"], "a"), 2)"#);1273 assert_eval!(r#"std.assertEqual(std.count(["a", "b", "a"], "a"), 2)"#);
1267 }1274 }
1275
1276 mod derive_typed {
1277 use crate::{typed::Typed, EvaluationState};
1278 use std::path::PathBuf;
1279
1280 #[derive(Typed, PartialEq, Debug)]
1281 struct MyTyped {
1282 a: u32,
1283 b: String,
1284 }
1285
1286 #[test]
1287 fn test() {
1288 let es = EvaluationState::default();
1289 let val = eval!("{a: 14, b: 'Hello, world!'}");
1290 let typed = es.run_in_state(|| MyTyped::try_from(val).unwrap());
1291
1292 assert_eq!(
1293 typed,
1294 MyTyped {
1295 a: 14,
1296 b: "Hello, world!".to_string()
1297 }
1298 );
1299 es.settings_mut().globals.insert(
1300 "mytyped".into(),
1301 es.run_in_state(|| typed.try_into()).unwrap(),
1302 );
1303
1304 let v = es
1305 .evaluate_snippet_raw(
1306 PathBuf::from("raw.jsonnet").into(),
1307 "
1308 mytyped == {a: 14, b: 'Hello, world!'}
1309 "
1310 .into(),
1311 )
1312 .unwrap()
1313 .as_bool()
1314 .unwrap();
1315 assert!(v)
1316 }
1317 }
1268}1318}
12691319
modifiedcrates/jrsonnet-evaluator/src/typed/conversions.rsdiffbeforeafterboth
1use std::convert::{TryFrom, TryInto};1use std::convert::{TryFrom, TryInto};
22
3use jrsonnet_interner::IStr;3use jrsonnet_interner::IStr;
4pub use jrsonnet_macros::Typed;
4use jrsonnet_types::{ComplexValType, ValType};5use jrsonnet_types::{ComplexValType, ValType};
56
6use crate::{7use crate::{
modifiedcrates/jrsonnet-evaluator/src/typed/mod.rsdiffbeforeafterboth
8 push_description_frame, Val,8 push_description_frame, Val,
9};9};
10use gcmodule::Trace;10use gcmodule::Trace;
11use jrsonnet_types::{ComplexValType, ValType};11pub use jrsonnet_types::{ComplexValType, ValType};
12use thiserror::Error;12use thiserror::Error;
13
14#[macro_export]
15macro_rules! unwrap_type {
16 ($desc:expr, $value:expr, $typ:expr => $match:path) => {{
17 use $crate::{push_frame, typed::CheckType};
18 push_frame(None, $desc, || Ok($typ.check(&$value)?))?;
19 match $value {
20 $match(v) => v,
21 _ => unreachable!(),
22 }
23 }};
24}
2513
26#[derive(Debug, Error, Clone, Trace)]14#[derive(Debug, Error, Clone, Trace)]
27pub enum TypeError {15pub enum TypeError {
136impl Display for ValuePathItem {124impl Display for ValuePathItem {
137 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {125 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
138 match self {126 match self {
139 Self::Field(name) => write!(f, ".{}", name)?,127 Self::Field(name) => write!(f, ".{:?}", name)?,
140 Self::Index(idx) => write!(f, "[{}]", idx)?,128 Self::Index(idx) => write!(f, "[{}]", idx)?,
141 }129 }
142 Ok(())130 Ok(())
modifiedcrates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth
91 Normal(Cc<FuncDesc>),91 Normal(Cc<FuncDesc>),
92 /// Standard library function92 /// Standard library function
93 StaticBuiltin(#[skip_trace] &'static dyn StaticBuiltin),93 StaticBuiltin(#[skip_trace] &'static dyn StaticBuiltin),
9494 /// User-provided function
95 Builtin(Cc<TraceBox<dyn Builtin>>),95 Builtin(Cc<TraceBox<dyn Builtin>>),
96}96}
9797
98impl Debug for FuncVal {98impl Debug for FuncVal {
99 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {99 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
100 match self {100 match self {
101 Self::Normal(arg0) => f.debug_tuple("Normal").field(arg0).finish(),101 Self::Normal(arg0) => f.debug_tuple("Normal").field(arg0).finish(),
102 Self::StaticBuiltin(arg0) => f.debug_tuple("Intrinsic").field(&arg0.name()).finish(),102 Self::StaticBuiltin(arg0) => {
103 f.debug_tuple("StaticBuiltin").field(&arg0.name()).finish()
104 }
103 Self::Builtin(arg0) => f.debug_tuple("Intrinsic").field(&arg0.name()).finish(),105 Self::Builtin(arg0) => f.debug_tuple("Builtin").field(&arg0.name()).finish(),
104 }106 }
105 }107 }
106}108}
338}340}
339341
340impl Val {342impl Val {
343 pub fn as_bool(&self) -> Option<bool> {
344 match self {
345 Val::Bool(v) => Some(*v),
346 _ => None,
347 }
348 }
349 pub fn as_null(&self) -> Option<()> {
350 match self {
351 Val::Null => Some(()),
352 _ => None,
353 }
354 }
355 pub fn as_str(&self) -> Option<IStr> {
356 match self {
357 Val::Str(s) => Some(s.clone()),
358 _ => None,
359 }
360 }
361 pub fn as_num(&self) -> Option<f64> {
362 match self {
363 Val::Num(n) => Some(*n),
364 _ => None,
365 }
366 }
367 pub fn as_arr(&self) -> Option<ArrValue> {
368 match self {
369 Val::Arr(a) => Some(a.clone()),
370 _ => None,
371 }
372 }
373 pub fn as_obj(&self) -> Option<ObjValue> {
374 match self {
375 Val::Obj(o) => Some(o.clone()),
376 _ => None,
377 }
378 }
379 pub fn as_func(&self) -> Option<FuncVal> {
380 match self {
381 Val::Func(f) => Some(f.clone()),
382 _ => None,
383 }
384 }
385
341 /// Creates `Val::Num` after checking for numeric overflow.386 /// Creates `Val::Num` after checking for numeric overflow.
342 /// As numbers are `f64`, we can just check for their finity.387 /// As numbers are `f64`, we can just check for their finity.
modifiedcrates/jrsonnet-macros/src/lib.rsdiffbeforeafterboth
1use quote::{quote, quote_spanned};1use quote::{quote, quote_spanned};
2use syn::{2use syn::{
3 parenthesized, parse::Parse, parse_macro_input, punctuated::Punctuated, spanned::Spanned,3 parenthesized, parse::Parse, parse_macro_input, punctuated::Punctuated, spanned::Spanned,
4 token::Comma, FnArg, GenericArgument, Ident, ItemFn, Pat, PatType, Path, PathArguments, Token,4 token::Comma, DeriveInput, FnArg, GenericArgument, Ident, ItemFn, Pat, PatType, Path,
5 Type,5 PathArguments, Token, Type,
6};6};
77
255 .into()255 .into()
256}256}
257
258#[proc_macro_derive(Typed)]
259pub fn derive_typed(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
260 let input = parse_macro_input!(item as DeriveInput);
261 let data = match &input.data {
262 syn::Data::Struct(s) => s,
263 _ => {
264 return syn::Error::new(input.span(), "only structs supported")
265 .to_compile_error()
266 .into()
267 }
268 };
269
270 let ident = &input.ident;
271
272 let fields_def = data.fields.iter().map(|f| {
273 let name = f
274 .ident
275 .as_ref()
276 .expect("only named fields supported")
277 .to_string();
278 let ty = &f.ty;
279 quote! {
280 (#name, #ty::TYPE),
281 }
282 });
283 let fields_parse = data.fields.iter().map(|f| {
284 let ident = f.ident.as_ref().unwrap();
285 let name = ident.to_string();
286 let ty = &f.ty;
287 quote! {
288 #ident: #ty::try_from(obj.get(#name.into())?.expect("shape is correct"))?,
289 }
290 });
291 let fields_serialize = data.fields.iter().map(|f| {
292 let ident = f.ident.as_ref().unwrap();
293 let name = ident.to_string();
294 quote! {
295 out.member(#name.into()).value(self.#ident.try_into()?);
296 }
297 });
298 let field_count = data.fields.len();
299
300 quote! {
301 const _: () = {
302 use ::jrsonnet_evaluator::{
303 typed::{ComplexValType, Typed, CheckType},
304 Val,
305 error::LocError,
306 obj::ObjValueBuilder,
307 };
308
309 const ITEMS: [(&'static str, &'static ComplexValType); #field_count] = [
310 #(#fields_def)*
311 ];
312 impl Typed for #ident {
313 const TYPE: &'static ComplexValType = &ComplexValType::ObjectRef(&ITEMS);
314 }
315
316 impl TryFrom<Val> for #ident {
317 type Error = LocError;
318 fn try_from(value: Val) -> Result<Self, Self::Error> {
319 <Self as Typed>::TYPE.check(&value)?;
320 let obj = value.as_obj().expect("shape is correct");
321
322 Ok(Self {
323 #(#fields_parse)*
324 })
325 }
326 }
327 impl TryInto<Val> for #ident {
328 type Error = LocError;
329 fn try_into(self) -> Result<Val, Self::Error> {
330 let mut out = ObjValueBuilder::new();
331 #(#fields_serialize)*
332 Ok(Val::Obj(out.build()))
333 }
334 }
335 ()
336 };
337 }
338 .into()
339}
257340
modifiedcrates/jrsonnet-types/src/lib.rsdiffbeforeafterboth
122 BoundedNumber(Option<f64>, Option<f64>),122 BoundedNumber(Option<f64>, Option<f64>),
123 Array(Box<ComplexValType>),123 Array(Box<ComplexValType>),
124 ArrayRef(&'static ComplexValType),124 ArrayRef(&'static ComplexValType),
125 ObjectRef(&'static [(&'static str, ComplexValType)]),125 ObjectRef(&'static [(&'static str, &'static ComplexValType)]),
126 Union(Vec<ComplexValType>),126 Union(Vec<ComplexValType>),
127 UnionRef(&'static [&'static ComplexValType]),127 UnionRef(&'static [&'static ComplexValType]),
128 Sum(Vec<ComplexValType>),128 Sum(Vec<ComplexValType>),