use crate::FieldIr;
use proc_macro2::TokenStream;
use quote::quote;
use syn::{DeriveInput, Generics, Ident};
pub(crate) struct DeriveDecode {
ident: Ident,
generics: Generics,
fields: Vec<FieldIr>,
}
impl DeriveDecode {
pub fn new(input: DeriveInput) -> syn::Result<Self> {
let data = match input.data {
syn::Data::Struct(data) => data,
_ => abort!(
input.ident,
"can't derive `Decode` on this type: only `struct` types are allowed",
),
};
let fields = FieldIr::from_fields(data.fields)?;
Ok(Self {
ident: input.ident,
generics: input.generics.clone(),
fields,
})
}
pub fn to_tokens(&self) -> TokenStream {
let ident = &self.ident;
let (_, generics, where_clause) = self.generics.split_for_impl();
let mut lowerer = FieldLowerer::new();
for field in &self.fields {
lowerer.add_field(field);
}
let body = lowerer.into_tokens();
quote! {
impl #generics ::ssh_encoding::Decode for #ident #generics #where_clause {
type Error = ::ssh_encoding::Error;
fn decode(reader: &mut impl Reader) -> Result<Self, Self::Error> {
Ok(Self {
#(#body)*,
})
}
}
}
}
}
struct FieldLowerer {
body: Vec<TokenStream>,
}
impl FieldLowerer {
fn new() -> Self {
Self {
body: Vec::default(),
}
}
fn add_field(&mut self, field: &FieldIr) {
let ident = field.ident.clone();
let ty = field.ty.clone();
let field = quote! { #ident: #ty::decode()? };
self.body.push(field)
}
fn into_tokens(self) -> Vec<TokenStream> {
self.body
}
}