obi_derive_internal/
struct_enc.rs

1use proc_macro2::{Span, TokenStream};
2use quote::quote;
3use syn::{Fields, Index, ItemStruct};
4
5pub fn struct_enc(input: &ItemStruct) -> syn::Result<TokenStream> {
6    let name = &input.ident;
7    let generics = &input.generics;
8    let mut body = TokenStream::new();
9    let mut encode_field_types = TokenStream::new();
10    match &input.fields {
11        Fields::Named(fields) => {
12            for field in &fields.named {
13                let field_name = field.ident.as_ref().unwrap();
14                let delta = quote! {
15                    obi::OBIEncode::encode(&self.#field_name, writer)?;
16                };
17                body.extend(delta);
18
19                let field_type = &field.ty;
20                encode_field_types.extend(quote! {
21                    #field_type: obi::enc::OBIEncode,
22                });
23            }
24        }
25        Fields::Unnamed(fields) => {
26            for field_idx in 0..fields.unnamed.len() {
27                let field_idx = Index {
28                    index: field_idx as u32,
29                    span: Span::call_site(),
30                };
31                let delta = quote! {
32                    obi::OBIEncode::encode(&self.#field_idx, writer)?;
33                };
34                body.extend(delta);
35            }
36        }
37        Fields::Unit => {}
38    }
39    Ok(quote! {
40        impl #generics obi::enc::OBIEncode for #name #generics where #encode_field_types {
41            fn encode<W: std::io::Write>(&self, writer: &mut W) -> std::result::Result<(), std::io::Error> {
42                #body
43                Ok(())
44            }
45        }
46    })
47}
48
49// Rustfmt removes commas.
50#[rustfmt::skip]
51#[cfg(test)]
52mod tests {
53    use super::*;
54
55    fn assert_eq(expected: TokenStream, actual: TokenStream) {
56        assert_eq!(expected.to_string(), actual.to_string())
57    }
58
59    #[test]
60    fn simple_struct() {
61        let item_struct: ItemStruct = syn::parse2(quote!{
62            struct A {
63                x: u64,
64                y: String,
65            }
66        }).unwrap();
67
68        let actual = struct_enc(&item_struct).unwrap();
69        let expected = quote!{
70            impl obi::enc::OBIEncode for A
71            where
72                u64: obi::enc::OBIEncode,
73                String: obi::enc::OBIEncode,
74            {
75                fn encode<W: std::io::Write>(&self, writer: &mut W) -> std::result::Result<(), std::io::Error> {
76                    obi::OBIEncode::encode(&self.x, writer)?;
77                    obi::OBIEncode::encode(&self.y, writer)?;
78                    Ok(())
79                }
80            }
81        };
82        assert_eq(expected, actual);
83    }
84}