use crate::ast::attr::MainContainer;
use crate::ast::types::{self, XcoderSigil};
use crate::expand::helpers;
use quote::{quote, quote_spanned};
pub(crate) fn gen_encode_impl(
input: &MainContainer,
key_encoder: &types::SiguledXcoder,
len_encoder: &types::SiguledXcoder,
) -> proc_macro2::TokenStream {
let name = &input.ident;
let sentinel = input.attrs.sentinel.as_ref();
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let items_encoded = gen_items_encoded(input, key_encoder, len_encoder);
let encode_with_key_len = match sentinel {
Some(sentinel) => quote! {
#[automatically_derived]
impl #impl_generics ::tinyklv::traits::EncodeFrame<Vec<u8>> for #name #ty_generics #where_clause {
fn encode_frame(&self) -> Vec<u8> {
self.encode_value().into_klv(
#sentinel,
#len_encoder,
)
}
}
},
None => quote! {},
};
quote! {
#[automatically_derived]
impl #impl_generics ::tinyklv::traits::EncodeValue<Vec<u8>> for #name #ty_generics #where_clause {
fn encode_value(&self) -> Vec<u8> {
let mut output = vec![];
#items_encoded
output
}
}
#encode_with_key_len
}
}
fn gen_items_encoded(
input: &MainContainer,
key_encoder: &types::SiguledXcoder,
len_encoder: &types::SiguledXcoder,
) -> proc_macro2::TokenStream {
let items_encoded = input
.data
.iter()
.filter_map(|field| {
field
.attrs
.as_ref()
.map(|attr| (&field.name, &field.ty, attr))
})
.map(|(name, ty, attrs)| {
#[allow(
clippy::unwrap_used,
reason = "`gen_encode_impl` call ensures that `attrs.enc` is `Some`"
)]
let value_encoder = attrs.enc.as_ref().unwrap();
let key = &attrs.key;
let span = name.span();
let enc_tokens: proc_macro2::TokenStream = if attrs.fallback_enc {
let t = helpers::unwrap_option_type(ty).unwrap_or(ty);
quote! { <#t as ::tinyklv::traits::EncodeValue<Vec<u8>>>::encode_value }
} else {
let inner = &value_encoder.inner;
quote! { #inner }
};
let (nonopt_arg, opt_arg) = match value_encoder.sigil {
XcoderSigil::None => (
quote_spanned! { span => &self.#name },
quote_spanned! { span => __val },
),
XcoderSigil::Ref => (
quote_spanned! { span => ::tinyklv::traits::EncodeAs::encode_as(&self.#name) },
quote_spanned! { span => ::tinyklv::traits::EncodeAs::encode_as(__val) },
),
XcoderSigil::Deref => (
quote_spanned! { span => self.#name },
quote_spanned! { span => *__val },
),
};
if crate::expand::helpers::is_option(ty) {
quote_spanned! { span =>
if let Some(ref __val) = self.#name {
output.extend(#enc_tokens(#opt_arg).into_klv(#key_encoder(#key), #len_encoder));
}
}
} else {
quote_spanned! { span =>
output.extend(#enc_tokens(#nonopt_arg).into_klv(#key_encoder(#key), #len_encoder));
}
}
});
quote! { #(#items_encoded)* }
}