git.delta.rocks / jrsonnet / refs/commits / efea8379ff42

difftreelog

feat rename/flatten field in derive(Typed)

Yaroslav Bolyukin2022-04-17parent: #78d82df.patch.diff
in: master

4 files changed

modifiedcrates/jrsonnet-evaluator/src/lib.rsdiffbeforeafterboth
1280 #[derive(Typed, PartialEq, Debug)]1280 #[derive(Typed, PartialEq, Debug)]
1281 struct MyTyped {1281 struct MyTyped {
1282 a: u32,1282 a: u32,
1283 #[typed(rename = "b")]
1283 b: String,1284 c: String,
1284 }1285 }
12851286
1286 #[test]1287 #[test]
1293 typed,1294 typed,
1294 MyTyped {1295 MyTyped {
1295 a: 14,1296 a: 14,
1296 b: "Hello, world!".to_string()1297 c: "Hello, world!".to_string()
1297 }1298 }
1298 );1299 );
1299 es.settings_mut().globals.insert(1300 es.settings_mut().globals.insert(
modifiedcrates/jrsonnet-evaluator/src/typed/conversions.rsdiffbeforeafterboth
8 error::{Error::*, LocError, Result},8 error::{Error::*, LocError, Result},
9 throw,9 throw,
10 typed::CheckType,10 typed::CheckType,
11 ArrValue, FuncVal, IndexableVal, ObjValue, Val,11 ArrValue, FuncVal, IndexableVal, ObjValue, ObjValueBuilder, Val,
12};12};
13
14pub trait TypedObj: Typed {
15 fn serialize(self, out: &mut ObjValueBuilder) -> Result<()>;
16 fn parse(obj: &ObjValue) -> Result<Self>;
17 fn into_object(self) -> Result<ObjValue> {
18 let mut builder = ObjValueBuilder::new();
19 self.serialize(&mut builder)?;
20 Ok(builder.build())
21 }
22}
1323
14pub trait Typed: TryFrom<Val, Error = LocError> + TryInto<Val, Error = LocError> {24pub trait Typed: TryFrom<Val, Error = LocError> + TryInto<Val, Error = LocError> {
15 const TYPE: &'static ComplexValType;25 const TYPE: &'static ComplexValType;
modifiedcrates/jrsonnet-evaluator/src/typed/mod.rsdiffbeforeafterboth
15pub enum TypeError {15pub enum TypeError {
16 #[error("expected {0}, got {1}")]16 #[error("expected {0}, got {1}")]
17 ExpectedGot(ComplexValType, ValType),17 ExpectedGot(ComplexValType, ValType),
18 #[error("missing property {0} from {1:?}")]18 #[error("missing property {0} from {1}")]
19 MissingProperty(#[skip_trace] Rc<str>, ComplexValType),19 MissingProperty(#[skip_trace] Rc<str>, ComplexValType),
20 #[error("every failed from {0}:\n{1}")]20 #[error("every failed from {0}:\n{1}")]
21 UnionFailed(ComplexValType, TypeLocErrorList),21 UnionFailed(ComplexValType, TypeLocErrorList),
modifiedcrates/jrsonnet-macros/src/lib.rsdiffbeforeafterboth
1use proc_macro2::TokenStream;
1use quote::{quote, quote_spanned};2use quote::{quote, quote_spanned};
2use syn::{3use syn::{
3 parenthesized, parse::Parse, parse_macro_input, punctuated::Punctuated, spanned::Spanned,4 parenthesized,
5 parse::{Parse, ParseStream},
6 parse_macro_input,
7 punctuated::Punctuated,
8 spanned::Spanned,
4 token::Comma, DeriveInput, FnArg, GenericArgument, Ident, ItemFn, Pat, PatType, Path,9 token::Comma,
10 Attribute, DeriveInput, Error, FnArg, GenericArgument, Ident, ItemFn, LitStr, Pat, PatType,
5 PathArguments, Token, Type,11 Path, PathArguments, Result, Token, Type,
6};12};
13
14fn parse_attr<A: Parse, I>(attrs: &[Attribute], ident: I) -> Result<Option<A>>
15where
16 Ident: PartialEq<I>,
17{
18 let attrs = attrs
19 .iter()
20 .filter(|a| a.path.is_ident(&ident))
21 .collect::<Vec<_>>();
22 if attrs.len() > 1 {
23 return Err(Error::new(
24 attrs[1].span(),
25 "this attribute may be specified only once",
26 ));
27 } else if attrs.is_empty() {
28 return Ok(None);
29 }
30 let attr = attrs[0];
31 let attr = attr.parse_args::<A>()?;
32
33 Ok(Some(attr))
34}
735
8fn is_location_arg(t: &PatType) -> bool {36fn is_location_arg(t: &PatType) -> bool {
9 t.attrs.iter().any(|a| a.path.is_ident("location"))37 t.attrs.iter().any(|a| a.path.is_ident("location"))
56 ty: Type,84 ty: Type,
57}85}
58impl Parse for Field {86impl Parse for Field {
59 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {87 fn parse(input: ParseStream) -> syn::Result<Self> {
60 Ok(Self {88 Ok(Self {
61 name: input.parse()?,89 name: input.parse()?,
62 _colon: input.parse()?,90 _colon: input.parse()?,
6795
68mod kw {96mod kw {
69 syn::custom_keyword!(fields);97 syn::custom_keyword!(fields);
98 syn::custom_keyword!(rename);
99 syn::custom_keyword!(flatten);
70}100}
101
102struct EmptyAttr;
103impl Parse for EmptyAttr {
104 fn parse(input: ParseStream) -> Result<Self> {
105 Ok(Self)
106 }
107}
71108
72struct BuiltinAttrs {109struct BuiltinAttrs {
73 fields: Vec<Field>,110 fields: Vec<Field>,
74}111}
75impl Parse for BuiltinAttrs {112impl Parse for BuiltinAttrs {
76 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {113 fn parse(input: ParseStream) -> syn::Result<Self> {
77 if input.is_empty() {114 if input.is_empty() {
78 return Ok(Self { fields: Vec::new() });115 return Ok(Self { fields: Vec::new() });
79 }116 }
255 .into()292 .into()
256}293}
294
295#[derive(Default)]
296struct TypedAttr {
297 rename: Option<String>,
298 flatten: bool,
299}
300impl Parse for TypedAttr {
301 fn parse(input: ParseStream) -> syn::Result<Self> {
302 let mut out = Self::default();
303 loop {
304 let lookahead = input.lookahead1();
305 if lookahead.peek(kw::rename) {
306 input.parse::<kw::rename>()?;
307 input.parse::<Token![=]>()?;
308 let name = input.parse::<LitStr>()?;
309 if out.rename.is_some() {
310 return Err(Error::new(
311 name.span(),
312 "rename attribute may only be specified once",
313 ));
314 }
315 out.rename = Some(name.value());
316 } else if lookahead.peek(kw::flatten) {
317 input.parse::<kw::flatten>()?;
318 out.flatten = true;
319 } else if input.is_empty() {
320 break;
321 } else {
322 return Err(lookahead.error());
323 }
324 if input.peek(Token![,]) {
325 input.parse::<Token![,]>()?;
326 } else {
327 break;
328 }
329 }
330 // input.parse::<kw::rename>()?;
331 // input.parse::<Token![=]>()?;
332 // let rename = input.parse::<LitStr>()?.value();
333 Ok(out)
334 }
335}
336
337struct TypedField<'f>(&'f syn::Field, TypedAttr);
338impl<'f> TypedField<'f> {
339 fn try_new(field: &'f syn::Field) -> Result<Self> {
340 let attr =
341 parse_attr::<TypedAttr, _>(&field.attrs, "typed")?.unwrap_or_else(Default::default);
342 if field.ident.is_none() {
343 return Err(Error::new(
344 field.span(),
345 "this field should appear in output object, but it has no visible name",
346 ));
347 }
348 Ok(Self(field, attr))
349 }
350 fn ident(&self) -> Ident {
351 self.0
352 .ident
353 .clone()
354 .expect("constructor disallows fields without name")
355 }
356 /// None if this field is flattened in jsonnet output
357 fn name(&self) -> Option<String> {
358 if self.1.flatten {
359 return None;
360 }
361 Some(
362 self.1
363 .rename
364 .clone()
365 .unwrap_or_else(|| self.ident().to_string()),
366 )
367 }
368
369 fn expand_shallow_field(&self) -> Option<TokenStream> {
370 if self.is_option() {
371 return None;
372 }
373 let name = self.name()?;
374 Some(quote! {
375 (#name, ComplexValType::Any)
376 })
377 }
378 fn expand_field(&self) -> Option<TokenStream> {
379 if self.is_option() {
380 return None;
381 }
382 let name = self.name()?;
383 let ty = &self.0.ty;
384 Some(quote! {
385 (#name, <#ty>::TYPE)
386 })
387 }
388 fn expand_parse(&self) -> TokenStream {
389 let ident = self.ident();
390 let ty = &self.0.ty;
391 if self.1.flatten {
392 // optional flatten is handled in same way as serde
393 return if self.is_option() {
394 quote! {
395 #ident: <#ty>::parse(&obj).ok(),
396 }
397 } else {
398 quote! {
399 #ident: <#ty>::parse(&obj)?,
400 }
401 };
402 };
403
404 let name = self.name().unwrap();
405 let value = if let Some(ty) = self.as_option() {
406 quote! {
407 if let Some(value) = obj.get(#name.into())? {
408 Some(<#ty>::try_from(vakue)?)
409 } else {
410 None
411 }
412 }
413 } else {
414 quote! {
415 <#ty>::try_from(obj.get(#name.into())?.ok_or_else(|| Error::NoSuchField(#name.into()))?)?
416 }
417 };
418
419 quote! {
420 #ident: #value,
421 }
422 }
423 fn expand_serialize(&self) -> TokenStream {
424 let ident = self.ident();
425 if let Some(name) = self.name() {
426 if self.is_option() {
427 quote! {
428 if let Some(value) = self.#ident {
429 out.member(#name.into()).value(value.try_into()?);
430 }
431 }
432 } else {
433 quote! {
434 out.member(#name.into()).value(self.#ident.try_into()?);
435 }
436 }
437 } else {
438 if self.is_option() {
439 quote! {
440 if let Some(value) = self.#ident {
441 value.serialize(out)?;
442 }
443 }
444 } else {
445 quote! {
446 self.#ident.serialize(out)?;
447 }
448 }
449 }
450 }
451
452 fn as_option(&self) -> Option<&Type> {
453 extract_type_from_option(&self.0.ty)
454 }
455 fn is_option(&self) -> bool {
456 self.as_option().is_some()
457 }
458}
257459
258#[proc_macro_derive(Typed)]460#[proc_macro_derive(Typed, attributes(typed))]
259pub fn derive_typed(item: proc_macro::TokenStream) -> proc_macro::TokenStream {461pub fn derive_typed(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
260 let input = parse_macro_input!(item as DeriveInput);462 let input = parse_macro_input!(item as DeriveInput);
261 let data = match &input.data {463 let data = match &input.data {
269471
270 let ident = &input.ident;472 let ident = &input.ident;
271
272 let fields_def = data.fields.iter().map(|f| {473 let fields = match data
273 let name = f474 .fields
475 .iter()
476 .map(TypedField::try_new)
477 .collect::<Result<Vec<_>>>()
478 {
479 Ok(v) => v,
274 .ident480 Err(e) => return e.to_compile_error().into(),
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 });481 };
482
283 let fields_parse = data.fields.iter().map(|f| {483 let typed = {
284 let ident = f.ident.as_ref().unwrap();484 let fields = fields
485 .iter()
486 .flat_map(TypedField::expand_field)
487 .collect::<Vec<_>>();
285 let name = ident.to_string();488 let len = fields.len();
286 let ty = &f.ty;
287 quote! {489 quote! {
490 const ITEMS: [(&'static str, &'static ComplexValType); #len] = [
288 #ident: #ty::try_from(obj.get(#name.into())?.expect("shape is correct"))?,491 #(#fields,)*
492 ];
493 impl Typed for #ident {
494 const TYPE: &'static ComplexValType = &ComplexValType::ObjectRef(&ITEMS);
495 }
289 }496 }
290 });497 };
498
291 let fields_serialize = data.fields.iter().map(|f| {499 let fields_parse = fields.iter().map(TypedField::expand_parse);
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();500 let fields_serialize = fields.iter().map(TypedField::expand_serialize);
299501
300 quote! {502 quote! {
301 const _: () = {503 const _: () = {
302 use ::jrsonnet_evaluator::{504 use ::jrsonnet_evaluator::{
303 typed::{ComplexValType, Typed, CheckType},505 typed::{ComplexValType, Typed, TypedObj, CheckType},
304 Val,506 Val,
305 error::LocError,507 error::{LocError, Error},
306 obj::ObjValueBuilder,508 ObjValueBuilder, ObjValue,
307 };509 };
308510
511 #typed
512
513 impl #ident {
309 const ITEMS: [(&'static str, &'static ComplexValType); #field_count] = [514 fn serialize(self, out: &mut ObjValueBuilder) -> Result<(), LocError> {
310 #(#fields_def)*515 #(#fields_serialize)*
311 ];516
312 impl Typed for #ident {517 Ok(())
518 }
313 const TYPE: &'static ComplexValType = &ComplexValType::ObjectRef(&ITEMS);519 fn parse(obj: &ObjValue) -> Result<Self, LocError> {
314 }520 Ok(Self {
521 #(#fields_parse)*
522 })
523 }
524 }
315525
316 impl TryFrom<Val> for #ident {526 impl TryFrom<Val> for #ident {
317 type Error = LocError;527 type Error = LocError;
318 fn try_from(value: Val) -> Result<Self, Self::Error> {528 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");529 let obj = value.as_obj().expect("shape is correct");
321
322 Ok(Self {530 Self::parse(&obj)
323 #(#fields_parse)*
324 })
325 }531 }
326 }532 }
327 impl TryInto<Val> for #ident {533 impl TryInto<Val> for #ident {
328 type Error = LocError;534 type Error = LocError;
329 fn try_into(self) -> Result<Val, Self::Error> {535 fn try_into(self) -> Result<Val, Self::Error> {
330 let mut out = ObjValueBuilder::new();536 let mut out = ObjValueBuilder::new();
331 #(#fields_serialize)*537 self.serialize(&mut out)?;
332 Ok(Val::Obj(out.build()))538 Ok(Val::Obj(out.build()))
333 }539 }
334 }540 }