difftreelog
feat rename/flatten field in derive(Typed)
in: master
4 files changed
crates/jrsonnet-evaluator/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/lib.rs
+++ b/crates/jrsonnet-evaluator/src/lib.rs
@@ -1280,7 +1280,8 @@
#[derive(Typed, PartialEq, Debug)]
struct MyTyped {
a: u32,
- b: String,
+ #[typed(rename = "b")]
+ c: String,
}
#[test]
@@ -1293,7 +1294,7 @@
typed,
MyTyped {
a: 14,
- b: "Hello, world!".to_string()
+ c: "Hello, world!".to_string()
}
);
es.settings_mut().globals.insert(
crates/jrsonnet-evaluator/src/typed/conversions.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/typed/conversions.rs
+++ b/crates/jrsonnet-evaluator/src/typed/conversions.rs
@@ -8,9 +8,19 @@
error::{Error::*, LocError, Result},
throw,
typed::CheckType,
- ArrValue, FuncVal, IndexableVal, ObjValue, Val,
+ ArrValue, FuncVal, IndexableVal, ObjValue, ObjValueBuilder, Val,
};
+pub trait TypedObj: Typed {
+ fn serialize(self, out: &mut ObjValueBuilder) -> Result<()>;
+ fn parse(obj: &ObjValue) -> Result<Self>;
+ fn into_object(self) -> Result<ObjValue> {
+ let mut builder = ObjValueBuilder::new();
+ self.serialize(&mut builder)?;
+ Ok(builder.build())
+ }
+}
+
pub trait Typed: TryFrom<Val, Error = LocError> + TryInto<Val, Error = LocError> {
const TYPE: &'static ComplexValType;
}
crates/jrsonnet-evaluator/src/typed/mod.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/typed/mod.rs
+++ b/crates/jrsonnet-evaluator/src/typed/mod.rs
@@ -15,7 +15,7 @@
pub enum TypeError {
#[error("expected {0}, got {1}")]
ExpectedGot(ComplexValType, ValType),
- #[error("missing property {0} from {1:?}")]
+ #[error("missing property {0} from {1}")]
MissingProperty(#[skip_trace] Rc<str>, ComplexValType),
#[error("every failed from {0}:\n{1}")]
UnionFailed(ComplexValType, TypeLocErrorList),
crates/jrsonnet-macros/src/lib.rsdiffbeforeafterboth1use 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};1314fn parse_attr<A: Parse, I>(attrs: &[Attribute], ident: I) -> Result<Option<A>>15where16 Ident: PartialEq<I>,17{18 let attrs = attrs19 .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>()?;3233 Ok(Some(attr))34}7358fn 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()?,679568mod 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}101102struct EmptyAttr;103impl Parse for EmptyAttr {104 fn parse(input: ParseStream) -> Result<Self> {105 Ok(Self)106 }107}7110872struct 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}294295#[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}336337struct 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.0352 .ident353 .clone()354 .expect("constructor disallows fields without name")355 }356 /// None if this field is flattened in jsonnet output357 fn name(&self) -> Option<String> {358 if self.1.flatten {359 return None;360 }361 Some(362 self.1363 .rename364 .clone()365 .unwrap_or_else(|| self.ident().to_string()),366 )367 }368369 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 serde393 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 };403404 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 None411 }412 }413 } else {414 quote! {415 <#ty>::try_from(obj.get(#name.into())?.ok_or_else(|| Error::NoSuchField(#name.into()))?)?416 }417 };418419 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 }451452 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}257459258#[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 {269471270 let ident = &input.ident;472 let ident = &input.ident;271272 let fields_def = data.fields.iter().map(|f| {473 let fields = match data273 let name = f474 .fields475 .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 };482283 let fields_parse = data.fields.iter().map(|f| {483 let typed = {284 let ident = f.ident.as_ref().unwrap();484 let fields = fields485 .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 };498291 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);299501300 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 };308510511 #typed512513 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 ];516312 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 }315525316 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");321322 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 }