use super::constants;
use super::helpers;
use crate::ast::attr::MainField;
use quote::quote;
pub(super) fn gen_known_keys_check(
name: &syn::Ident,
fields: &[MainField],
) -> proc_macro2::TokenStream {
let keys: Vec<_> = fields
.iter()
.filter_map(|f| f.attrs.as_ref().map(|a| a.key.clone()))
.collect();
if keys.is_empty() {
return quote! { _ if false };
}
let pattern = quote! { #(#keys)|* };
quote! {
if !matches!(key, #pattern) {
return ::core::result::Result::Err(
concat!(
"invalid key (expected one of the keys defined on `",
stringify!(#name),
"`; to turn this off, remove `deny_unknown_keys`)",
)
);
}
}
}
pub(super) fn gen_items_match(
fields: &[MainField],
stream: &syn::Type,
debug: bool,
) -> proc_macro2::TokenStream {
let arms = fields
.iter()
.filter_map(|f| f.attrs.as_ref().map(|attr| (&f.name, f.ty, attr)))
.map(|(name, ty, attrs)| {
let key = &attrs.key;
let dec_tokens: proc_macro2::TokenStream = if attrs.fallback_dec {
let t = helpers::unwrap_option_type(ty).unwrap_or(ty);
quote! { <#t as ::tinyklv::traits::DecodeValue<#stream>>::decode_value }
} else {
#[allow(clippy::unwrap_used, reason = "`gen_decode_impl` call ensures that `attrs.dec` is `Some`")]
let dec = attrs.dec.as_ref().unwrap();
quote! { #dec }
};
let varlen = attrs.var.as_ref().map(|v| v.value).unwrap_or(false); let optional_len_arg = if varlen {
quote! { (len) }
} else {
quote! {}
};
let latebind_map = match attrs.latebind.as_ref() {
Some(lb) => {
let inner = &lb.inner;
if lb.is_mut {
quote! { .map(|mut __v| { #inner(&mut __v); __v }) }
} else {
quote! { .map(#inner) }
}
}
None => quote! {},
};
match debug {
true => {
let logger = constants::logger();
quote! {
#key => {
let val = #dec_tokens #optional_len_arg (&mut subinput);
#logger ("\t{}: {:?}", stringify!(#name), val);
__acc.#name = val.ok() #latebind_map .or(__acc.#name);
},
}
}
false => quote! {
#key => __acc.#name = #dec_tokens #optional_len_arg (&mut subinput).ok() #latebind_map .or(__acc.#name),
},
}
});
quote! { #(#arms)* }
}