summary refs log blame commit diff
path: root/msg_socket/msg_on_socket_derive/msg_on_socket_derive.rs
blob: 77fc7f5ea0bda54aee6a7a3a8cb690453b988383 (plain) (tree)
1
2
3
4
5
6
7
8
9




                                                                         


                  
                                     

                                 

                                                                                                   
  

                                                                              
                                                            

                                                                                        
                                                
                         

 
                                                       
                                          
                       
                                                                                    

          
                      

                                     
                                                      
                    
                                                      

             
                                                         
                     
                                                                             




                                             
                                          


                                                                                            






                    
                                                                      
                                       

                                                                    


                                                                      
            
                                                
                         



                               
     

 
                         
                                                          







                                                          

                                                


                                             









                                                               
                                                                       















                                                                                                



       







                                                                     


                        






                                           
                                                                         
                                                                  
            
                                     

                     

                                     



         
                                                                                        

                                     

                                         


                                                 
                                 








                                                                             
                                        
     



                                             
                                                   












                                         
                                                                                         
                                      




                                         



                                                                  

                                       




                                                 
                                           








                                                                                    
                                                            

                                                                     

                                                                
            
                                                
                         



                               
     

 





                                                                      
     
 



                                       
            

                                                   



         






















                                                                                           
                                        




                                                                            
                 









                                                                                        

                             




                                                        
             






                                            
             





                                            
     

 
                                                                            

                                        



                                                          


                                               
                                        
                                                    

                                                                                           

                                             
                                
                             


                                                     
                                                                                   
                     
                  




                                               

                                                                       
                                                     
                                                                                          
                                             

                 
                                
                             




                                                                                 
                  


                                       
                                
                                                          
                  


                                       
     



                                             
                                                   
                                                                                   

                                   
                                                            

             
     

 
                                                                             
                                        
                        



                                                          


                                                
                                        
                                                             

                                                                                         


                                               

                                                                 
                                         




                                                     
                  




                                                

                                                                      
                                                     
                                                                                    
                                               

                 
                                
                                                              
                                         




                                                     
                  


                                       
                                
                                             
                                         

                             
                  


                                       

     




                                                 
                                           


                                                                    



                                   
     

 
                                                                      
            
                                                                                         
                                     




                                                                                     
                                                                      
                                      
 



                                                                      
            
                                                
                         



                               
     

 
                                                         
                                      





                                                                  


                                                       




                                       



                
                                                                     



                          
                                                   



                                           
     

 
                                                                                       

                                     
                                                   
                                                   
                                                                
                                                                                
                                     
                                            

     



                                             
                                                   












                                         
                                                                                        

                                      


                                                                          


                                       




                                                 
                                           








                                                                                 





                                                                                     
                         
         
                    
                                                       

                    
                                                       
              





                              
                                                                             
            
                                                                                   
                                   




                           
                                                                 
            
                                                                                              
                                          





                         
                                       
                               
                     















                                               
                                                                    
     











                                               
                                                    

                                                                             
                 








                                                    
                 


                                                     
                                                           


                                                                                              
                                               


                                                                                                 
                                               


                                                                                               
                                               



                                                       



                                                         
                                                   

                                                 

                                
                                                                                            
                                                  
                                     

                                
                                                                                            
                                                  
                                     

                                
                                                                                            
                                                  



                                     
 

          
                                                                             








                                               
                                                    





                                                                                 
                 



                                                    
                 


                                                     
                                                           


                                                                                              
                                               


                                                                                               
                                               


                                                                                                
                                               



                                                                                



                                                         
                                                   


                                                                         

                                                                                                          
                                     

                                                                                                          
                                     

                                                                                                          





                                     
                                                                             







                                               



                              



                               
                                                    








                                                                             
                 





                                                                             
                 


                                                     
                                                           


                                                                          



                                                         

                                                                                                      
                                               

                                                                    




                                                         

                                                                                                      

                                               

                                                                                                         

                                               
                                                                  
                         
                                                                    

                     



                                                         
                                                   


                                                                            
                                
                                                  


                                                         
                                               
                                                                                                    
                                                               






                                             
                                                


                                                         

                                                                                                          
                                             

                                                                                                          





                                             

          
                                                                             
     






































                                                                  
                                                                             
     
 
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#![recursion_limit = "256"]

use std::vec::Vec;

use proc_macro2::{Span, TokenStream};
use quote::{format_ident, quote};
use syn::{
    parse_macro_input, Data, DataEnum, DataStruct, DeriveInput, Fields, Ident, Index, Member, Meta,
    NestedMeta, Type,
};

/// The function that derives the recursive implementation for struct or enum.
#[proc_macro_derive(MsgOnSocket, attributes(msg_on_socket))]
pub fn msg_on_socket_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    let impl_for_input = msg_socket_impl(input);
    impl_for_input.into()
}

fn msg_socket_impl(input: DeriveInput) -> TokenStream {
    if !input.generics.params.is_empty() {
        return quote! {
            compile_error!("derive(MsgSocket) does not support generic parameters");
        };
    }
    match input.data {
        Data::Struct(ds) => {
            if is_named_struct(&ds) {
                impl_for_named_struct(input.ident, ds)
            } else {
                impl_for_tuple_struct(input.ident, ds)
            }
        }
        Data::Enum(de) => impl_for_enum(input.ident, de),
        _ => quote! {
            compile_error!("derive(MsgSocket) only support struct and enum");
        },
    }
}

fn is_named_struct(ds: &DataStruct) -> bool {
    matches!(&ds.fields, Fields::Named(_))
}

/************************** Named Struct Impls ********************************************/

struct StructField {
    member: Member,
    ty: Type,
    skipped: bool,
}

fn impl_for_named_struct(name: Ident, ds: DataStruct) -> TokenStream {
    let fields = get_struct_fields(ds);
    let uses_fd_impl = define_uses_fd_for_struct(&fields);
    let buffer_sizes_impls = define_buffer_size_for_struct(&fields);

    let read_buffer = define_read_buffer_for_struct(&name, &fields);
    let write_buffer = define_write_buffer_for_struct(&name, &fields);
    quote! {
        impl msg_socket::MsgOnSocket for #name {
            #uses_fd_impl
            #buffer_sizes_impls
            #read_buffer
            #write_buffer
        }
    }
}

// Flatten struct fields.
fn get_struct_fields(ds: DataStruct) -> Vec<StructField> {
    let fields = match ds.fields {
        Fields::Named(fields_named) => fields_named.named,
        _ => {
            panic!("Struct must have named fields");
        }
    };
    let mut vec = Vec::new();
    for field in fields {
        let member = match field.ident {
            Some(ident) => Member::Named(ident),
            None => panic!("Unknown Error."),
        };
        let ty = field.ty;
        let mut skipped = false;
        for attr in field
            .attrs
            .iter()
            .filter(|attr| attr.path.is_ident("msg_on_socket"))
        {
            match attr.parse_meta().unwrap() {
                Meta::List(meta) => {
                    for nested in meta.nested {
                        match nested {
                            NestedMeta::Meta(Meta::Path(ref meta_path))
                                if meta_path.is_ident("skip") =>
                            {
                                skipped = true;
                            }
                            _ => panic!("unrecognized attribute meta `{}`", quote! { #nested }),
                        }
                    }
                }
                _ => panic!("unrecognized attribute `{}`", quote! { #attr }),
            }
        }
        vec.push(StructField {
            member,
            ty,
            skipped,
        });
    }
    vec
}

fn define_uses_fd_for_struct(fields: &[StructField]) -> TokenStream {
    let field_types: Vec<_> = fields
        .iter()
        .filter(|f| !f.skipped)
        .map(|f| &f.ty)
        .collect();

    if field_types.is_empty() {
        return quote!();
    }

    quote! {
        fn uses_fd() -> bool {
            #(<#field_types>::uses_fd())||*
        }
    }
}

fn define_buffer_size_for_struct(fields: &[StructField]) -> TokenStream {
    let (msg_size, fd_count) = get_fields_buffer_size_sum(fields);
    quote! {
        fn msg_size(&self) -> usize {
            #msg_size
        }
        fn fd_count(&self) -> usize {
            #fd_count
        }
    }
}

fn define_read_buffer_for_struct(_name: &Ident, fields: &[StructField]) -> TokenStream {
    let mut read_fields = Vec::new();
    let mut init_fields = Vec::new();
    for field in fields {
        let ident = match &field.member {
            Member::Named(ident) => ident,
            Member::Unnamed(_) => unreachable!(),
        };
        let name = ident.clone();
        if field.skipped {
            let ty = &field.ty;
            init_fields.push(quote! {
                #name: <#ty>::default()
            });
            continue;
        }
        let read_field = read_from_buffer_and_move_offset(&ident, &field.ty);
        read_fields.push(read_field);
        init_fields.push(quote!(#name));
    }
    quote! {
        unsafe fn read_from_buffer(
            buffer: &[u8],
            fds: &[std::os::unix::io::RawFd],
        ) -> msg_socket::MsgResult<(Self, usize)> {
            let mut __offset = 0usize;
            let mut __fd_offset = 0usize;
            #(#read_fields)*
            Ok((
                Self {
                    #(#init_fields),*
                },
                __fd_offset
            ))
        }
    }
}

fn define_write_buffer_for_struct(_name: &Ident, fields: &[StructField]) -> TokenStream {
    let mut write_fields = Vec::new();
    for field in fields {
        if field.skipped {
            continue;
        }
        let ident = match &field.member {
            Member::Named(ident) => ident,
            Member::Unnamed(_) => unreachable!(),
        };
        let write_field = write_to_buffer_and_move_offset(&ident);
        write_fields.push(write_field);
    }
    quote! {
        fn write_to_buffer(
            &self,
            buffer: &mut [u8],
            fds: &mut [std::os::unix::io::RawFd],
        ) -> msg_socket::MsgResult<usize> {
            let mut __offset = 0usize;
            let mut __fd_offset = 0usize;
            #(#write_fields)*
            Ok(__fd_offset)
        }
    }
}

/************************** Enum Impls ********************************************/
fn impl_for_enum(name: Ident, de: DataEnum) -> TokenStream {
    let uses_fd_impl = define_uses_fd_for_enum(&de);
    let buffer_sizes_impls = define_buffer_size_for_enum(&name, &de);
    let read_buffer = define_read_buffer_for_enum(&name, &de);
    let write_buffer = define_write_buffer_for_enum(&name, &de);
    quote! {
        impl msg_socket::MsgOnSocket for #name {
            #uses_fd_impl
            #buffer_sizes_impls
            #read_buffer
            #write_buffer
        }
    }
}

fn define_uses_fd_for_enum(de: &DataEnum) -> TokenStream {
    let mut variant_field_types = Vec::new();
    for variant in &de.variants {
        for variant_field_ty in variant.fields.iter().map(|f| &f.ty) {
            variant_field_types.push(variant_field_ty);
        }
    }

    if variant_field_types.len() == 0 {
        return quote!();
    }

    quote! {
        fn uses_fd() -> bool {
            #(<#variant_field_types>::uses_fd())||*
        }
    }
}

fn define_buffer_size_for_enum(name: &Ident, de: &DataEnum) -> TokenStream {
    let mut msg_size_match_variants = Vec::new();
    let mut fd_count_match_variants = Vec::new();

    for variant in &de.variants {
        let variant_name = &variant.ident;
        match &variant.fields {
            Fields::Named(fields) => {
                let mut tmp_names = Vec::new();
                for field in &fields.named {
                    tmp_names.push(field.ident.clone().unwrap());
                }

                let v = quote! {
                    #name::#variant_name { #(#tmp_names),* } => #(#tmp_names.msg_size())+*,
                };
                msg_size_match_variants.push(v);

                let v = quote! {
                    #name::#variant_name { #(#tmp_names),* } => #(#tmp_names.fd_count())+*,
                };
                fd_count_match_variants.push(v);
            }
            Fields::Unnamed(fields) => {
                let mut tmp_names = Vec::new();
                for idx in 0..fields.unnamed.len() {
                    let tmp_name = format!("enum_field{}", idx);
                    let tmp_name = Ident::new(&tmp_name, Span::call_site());
                    tmp_names.push(tmp_name.clone());
                }

                let v = quote! {
                    #name::#variant_name(#(#tmp_names),*) => #(#tmp_names.msg_size())+*,
                };
                msg_size_match_variants.push(v);

                let v = quote! {
                    #name::#variant_name(#(#tmp_names),*) => #(#tmp_names.fd_count())+*,
                };
                fd_count_match_variants.push(v);
            }
            Fields::Unit => {
                let v = quote! {
                    #name::#variant_name => 0,
                };
                msg_size_match_variants.push(v.clone());
                fd_count_match_variants.push(v);
            }
        }
    }

    quote! {
        fn msg_size(&self) -> usize {
            1 + match self {
                #(#msg_size_match_variants)*
            }
        }
        fn fd_count(&self) -> usize {
            match self {
                #(#fd_count_match_variants)*
            }
        }
    }
}

fn define_read_buffer_for_enum(name: &Ident, de: &DataEnum) -> TokenStream {
    let mut match_variants = Vec::new();
    let de = de.clone();
    for (idx, variant) in de.variants.iter().enumerate() {
        let idx = idx as u8;
        let variant_name = &variant.ident;
        match &variant.fields {
            Fields::Named(fields) => {
                let mut tmp_names = Vec::new();
                let mut read_tmps = Vec::new();
                for f in &fields.named {
                    tmp_names.push(f.ident.clone());
                    let read_tmp =
                        read_from_buffer_and_move_offset(f.ident.as_ref().unwrap(), &f.ty);
                    read_tmps.push(read_tmp);
                }
                let v = quote! {
                    #idx => {
                        let mut __offset = 1usize;
                        let mut __fd_offset = 0usize;
                        #(#read_tmps)*
                        Ok((#name::#variant_name { #(#tmp_names),* }, __fd_offset))
                    }
                };
                match_variants.push(v);
            }
            Fields::Unnamed(fields) => {
                let mut tmp_names = Vec::new();
                let mut read_tmps = Vec::new();
                for (idx, field) in fields.unnamed.iter().enumerate() {
                    let tmp_name = format_ident!("enum_field{}", idx);
                    tmp_names.push(tmp_name.clone());
                    let read_tmp = read_from_buffer_and_move_offset(&tmp_name, &field.ty);
                    read_tmps.push(read_tmp);
                }

                let v = quote! {
                    #idx => {
                        let mut __offset = 1usize;
                        let mut __fd_offset = 0usize;
                        #(#read_tmps)*
                        Ok((#name::#variant_name( #(#tmp_names),*), __fd_offset))
                    }
                };
                match_variants.push(v);
            }
            Fields::Unit => {
                let v = quote! {
                    #idx => Ok((#name::#variant_name, 0)),
                };
                match_variants.push(v);
            }
        }
    }
    quote! {
        unsafe fn read_from_buffer(
            buffer: &[u8],
            fds: &[std::os::unix::io::RawFd],
        ) -> msg_socket::MsgResult<(Self, usize)> {
            let v = buffer.get(0).ok_or(msg_socket::MsgError::WrongMsgBufferSize)?;
            match v {
                #(#match_variants)*
                _ => Err(msg_socket::MsgError::InvalidType),
            }
        }
    }
}

fn define_write_buffer_for_enum(name: &Ident, de: &DataEnum) -> TokenStream {
    let mut match_variants = Vec::new();
    let de = de.clone();
    for (idx, variant) in de.variants.iter().enumerate() {
        let idx = idx as u8;
        let variant_name = &variant.ident;
        match &variant.fields {
            Fields::Named(fields) => {
                let mut tmp_names = Vec::new();
                let mut write_tmps = Vec::new();
                for f in &fields.named {
                    tmp_names.push(f.ident.clone().unwrap());
                    let write_tmp =
                        enum_write_to_buffer_and_move_offset(&f.ident.as_ref().unwrap());
                    write_tmps.push(write_tmp);
                }

                let v = quote! {
                    #name::#variant_name { #(#tmp_names),* } => {
                        buffer[0] = #idx;
                        let mut __offset = 1usize;
                        let mut __fd_offset = 0usize;
                        #(#write_tmps)*
                        Ok(__fd_offset)
                    }
                };
                match_variants.push(v);
            }
            Fields::Unnamed(fields) => {
                let mut tmp_names = Vec::new();
                let mut write_tmps = Vec::new();
                for idx in 0..fields.unnamed.len() {
                    let tmp_name = format_ident!("enum_field{}", idx);
                    tmp_names.push(tmp_name.clone());
                    let write_tmp = enum_write_to_buffer_and_move_offset(&tmp_name);
                    write_tmps.push(write_tmp);
                }

                let v = quote! {
                    #name::#variant_name(#(#tmp_names),*) => {
                        buffer[0] = #idx;
                        let mut __offset = 1usize;
                        let mut __fd_offset = 0usize;
                        #(#write_tmps)*
                        Ok(__fd_offset)
                    }
                };
                match_variants.push(v);
            }
            Fields::Unit => {
                let v = quote! {
                    #name::#variant_name => {
                        buffer[0] = #idx;
                        Ok(0)
                    }
                };
                match_variants.push(v);
            }
        }
    }

    quote! {
        fn write_to_buffer(
            &self,
            buffer: &mut [u8],
            fds: &mut [std::os::unix::io::RawFd],
        ) -> msg_socket::MsgResult<usize> {
            if buffer.is_empty() {
                return Err(msg_socket::MsgError::WrongMsgBufferSize)
            }
            match self {
                #(#match_variants)*
            }
        }
    }
}

fn enum_write_to_buffer_and_move_offset(name: &Ident) -> TokenStream {
    quote! {
        let o = #name.write_to_buffer(&mut buffer[__offset..], &mut fds[__fd_offset..])?;
        __offset += #name.msg_size();
        __fd_offset += o;
    }
}

/************************** Tuple Impls ********************************************/
fn impl_for_tuple_struct(name: Ident, ds: DataStruct) -> TokenStream {
    let fields = get_tuple_fields(ds);

    let uses_fd_impl = define_uses_fd_for_tuples(&fields);
    let buffer_sizes_impls = define_buffer_size_for_struct(&fields);
    let read_buffer = define_read_buffer_for_tuples(&name, &fields);
    let write_buffer = define_write_buffer_for_tuples(&name, &fields);
    quote! {
        impl msg_socket::MsgOnSocket for #name {
            #uses_fd_impl
            #buffer_sizes_impls
            #read_buffer
            #write_buffer
        }
    }
}

fn get_tuple_fields(ds: DataStruct) -> Vec<StructField> {
    let mut field_idents = Vec::new();
    let fields = match ds.fields {
        Fields::Unnamed(fields_unnamed) => fields_unnamed.unnamed,
        _ => {
            panic!("Tuple struct must have unnamed fields.");
        }
    };
    for (idx, field) in fields.iter().enumerate() {
        let member = Member::Unnamed(Index::from(idx));
        let ty = field.ty.clone();
        field_idents.push(StructField {
            member,
            ty,
            skipped: false,
        });
    }
    field_idents
}

fn define_uses_fd_for_tuples(fields: &[StructField]) -> TokenStream {
    if fields.len() == 0 {
        return quote!();
    }

    let field_types = fields.iter().map(|f| &f.ty);
    quote! {
        fn uses_fd() -> bool {
            #(<#field_types>::uses_fd())||*
        }
    }
}

fn define_read_buffer_for_tuples(name: &Ident, fields: &[StructField]) -> TokenStream {
    let mut read_fields = Vec::new();
    let mut init_fields = Vec::new();
    for (idx, field) in fields.iter().enumerate() {
        let tmp_name = format!("tuple_tmp{}", idx);
        let tmp_name = Ident::new(&tmp_name, Span::call_site());
        let read_field = read_from_buffer_and_move_offset(&tmp_name, &field.ty);
        read_fields.push(read_field);
        init_fields.push(quote!(#tmp_name));
    }

    quote! {
        unsafe fn read_from_buffer(
            buffer: &[u8],
            fds: &[std::os::unix::io::RawFd],
        ) -> msg_socket::MsgResult<(Self, usize)> {
            let mut __offset = 0usize;
            let mut __fd_offset = 0usize;
            #(#read_fields)*
            Ok((
                #name (
                    #(#init_fields),*
                ),
                __fd_offset
            ))
        }
    }
}

fn define_write_buffer_for_tuples(name: &Ident, fields: &[StructField]) -> TokenStream {
    let mut write_fields = Vec::new();
    let mut tmp_names = Vec::new();
    for idx in 0..fields.len() {
        let tmp_name = format_ident!("tuple_tmp{}", idx);
        let write_field = enum_write_to_buffer_and_move_offset(&tmp_name);
        write_fields.push(write_field);
        tmp_names.push(tmp_name);
    }
    quote! {
        fn write_to_buffer(
            &self,
            buffer: &mut [u8],
            fds: &mut [std::os::unix::io::RawFd],
        ) -> msg_socket::MsgResult<usize> {
            let mut __offset = 0usize;
            let mut __fd_offset = 0usize;
            let #name( #(#tmp_names),* ) = self;
            #(#write_fields)*
            Ok(__fd_offset)
        }
    }
}
/************************** Helpers ********************************************/
fn get_fields_buffer_size_sum(fields: &[StructField]) -> (TokenStream, TokenStream) {
    let fields: Vec<_> = fields
        .iter()
        .filter(|f| !f.skipped)
        .map(|f| &f.member)
        .collect();
    if fields.len() > 0 {
        (
            quote! {
                #( self.#fields.msg_size() as usize )+*
            },
            quote! {
                #( self.#fields.fd_count() as usize )+*
            },
        )
    } else {
        (quote!(0), quote!(0))
    }
}

fn read_from_buffer_and_move_offset(name: &Ident, ty: &Type) -> TokenStream {
    quote! {
        let t = <#ty>::read_from_buffer(&buffer[__offset..], &fds[__fd_offset..])?;
        __offset += t.0.msg_size();
        __fd_offset += t.1;
        let #name = t.0;
    }
}

fn write_to_buffer_and_move_offset(name: &Ident) -> TokenStream {
    quote! {
        let o = self.#name.write_to_buffer(&mut buffer[__offset..], &mut fds[__fd_offset..])?;
        __offset += self.#name.msg_size();
        __fd_offset += o;
    }
}

#[cfg(test)]
mod tests {
    use super::define_uses_fd_for_enum;
    use crate::msg_socket_impl;
    use quote::quote;
    use syn::{parse_quote, Data, DeriveInput};

    #[test]
    fn simple_enum_uses_fd() {
        let input: DeriveInput = parse_quote! {
            enum Simple {
                A,
                B,
            }
        };

        let data = match input.data {
            Data::Enum(data) => data,
            _ => unreachable!(),
        };

        assert_eq!(define_uses_fd_for_enum(&data).to_string(), "",);
    }

    #[test]
    fn end_to_end_struct_test() {
        let input: DeriveInput = parse_quote! {
            struct MyMsg {
                a: u8,
                b: RawFd,
                c: u32,
            }
        };

        let expected = quote! {
            impl msg_socket::MsgOnSocket for MyMsg {
                fn uses_fd() -> bool {
                    <u8>::uses_fd() || <RawFd>::uses_fd() || <u32>::uses_fd()
                }
                fn msg_size(&self) -> usize {
                    self.a.msg_size() as usize
                        + self.b.msg_size() as usize
                        + self.c.msg_size() as usize
                }
                fn fd_count(&self) -> usize {
                    self.a.fd_count() as usize
                        + self.b.fd_count() as usize
                        + self.c.fd_count() as usize
                }
                unsafe fn read_from_buffer(
                    buffer: &[u8],
                    fds: &[std::os::unix::io::RawFd],
                ) -> msg_socket::MsgResult<(Self, usize)> {
                    let mut __offset = 0usize;
                    let mut __fd_offset = 0usize;
                    let t = <u8>::read_from_buffer(&buffer[__offset..], &fds[__fd_offset..])?;
                    __offset += t.0.msg_size();
                    __fd_offset += t.1;
                    let a = t.0;
                    let t = <RawFd>::read_from_buffer(&buffer[__offset..], &fds[__fd_offset..])?;
                    __offset += t.0.msg_size();
                    __fd_offset += t.1;
                    let b = t.0;
                    let t = <u32>::read_from_buffer(&buffer[__offset..], &fds[__fd_offset..])?;
                    __offset += t.0.msg_size();
                    __fd_offset += t.1;
                    let c = t.0;
                    Ok((Self { a, b, c }, __fd_offset))
                }
                fn write_to_buffer(
                    &self,
                    buffer: &mut [u8],
                    fds: &mut [std::os::unix::io::RawFd],
                ) -> msg_socket::MsgResult<usize> {
                    let mut __offset = 0usize;
                    let mut __fd_offset = 0usize;
                    let o = self
                        .a
                        .write_to_buffer(&mut buffer[__offset..], &mut fds[__fd_offset..])?;
                    __offset += self.a.msg_size();
                    __fd_offset += o;
                    let o = self
                        .b
                        .write_to_buffer(&mut buffer[__offset..], &mut fds[__fd_offset..])?;
                    __offset += self.b.msg_size();
                    __fd_offset += o;
                    let o = self
                        .c
                        .write_to_buffer(&mut buffer[__offset..], &mut fds[__fd_offset..])?;
                    __offset += self.c.msg_size();
                    __fd_offset += o;
                    Ok(__fd_offset)
                }
            }

        };

        assert_eq!(msg_socket_impl(input).to_string(), expected.to_string());
    }

    #[test]
    fn end_to_end_tuple_struct_test() {
        let input: DeriveInput = parse_quote! {
            struct MyMsg(u8, u32, File);
        };

        let expected = quote! {
            impl msg_socket::MsgOnSocket for MyMsg {
                fn uses_fd() -> bool {
                    <u8>::uses_fd() || <u32>::uses_fd() || <File>::uses_fd()
                }
                fn msg_size(&self) -> usize {
                    self.0.msg_size() as usize
                        + self.1.msg_size() as usize + self.2.msg_size() as usize
                }
                fn fd_count(&self) -> usize {
                    self.0.fd_count() as usize
                        + self.1.fd_count() as usize
                        + self.2.fd_count() as usize
                }
                unsafe fn read_from_buffer(
                    buffer: &[u8],
                    fds: &[std::os::unix::io::RawFd],
                ) -> msg_socket::MsgResult<(Self, usize)> {
                    let mut __offset = 0usize;
                    let mut __fd_offset = 0usize;
                    let t = <u8>::read_from_buffer(&buffer[__offset..], &fds[__fd_offset..])?;
                    __offset += t.0.msg_size();
                    __fd_offset += t.1;
                    let tuple_tmp0 = t.0;
                    let t = <u32>::read_from_buffer(&buffer[__offset..], &fds[__fd_offset..])?;
                    __offset += t.0.msg_size();
                    __fd_offset += t.1;
                    let tuple_tmp1 = t.0;
                    let t = <File>::read_from_buffer(&buffer[__offset..], &fds[__fd_offset..])?;
                    __offset += t.0.msg_size();
                    __fd_offset += t.1;
                    let tuple_tmp2 = t.0;
                    Ok((MyMsg(tuple_tmp0, tuple_tmp1, tuple_tmp2), __fd_offset))
                }
                fn write_to_buffer(
                    &self,
                    buffer: &mut [u8],
                    fds: &mut [std::os::unix::io::RawFd],
                ) -> msg_socket::MsgResult<usize> {
                    let mut __offset = 0usize;
                    let mut __fd_offset = 0usize;
                    let MyMsg(tuple_tmp0, tuple_tmp1, tuple_tmp2) = self;
                    let o = tuple_tmp0.write_to_buffer(&mut buffer[__offset..], &mut fds[__fd_offset..])?;
                    __offset += tuple_tmp0.msg_size();
                    __fd_offset += o;
                    let o = tuple_tmp1.write_to_buffer(&mut buffer[__offset..], &mut fds[__fd_offset..])?;
                    __offset += tuple_tmp1.msg_size();
                    __fd_offset += o;
                    let o = tuple_tmp2.write_to_buffer(&mut buffer[__offset..], &mut fds[__fd_offset..])?;
                    __offset += tuple_tmp2.msg_size();
                    __fd_offset += o;
                    Ok(__fd_offset)
                }
            }
        };

        assert_eq!(msg_socket_impl(input).to_string(), expected.to_string());
    }

    #[test]
    fn end_to_end_enum_test() {
        let input: DeriveInput = parse_quote! {
            enum MyMsg {
                A(u8),
                B,
                C {
                    f0: u8,
                    f1: RawFd,
                },
            }
        };

        let expected = quote! {
            impl msg_socket::MsgOnSocket for MyMsg {
                fn uses_fd() -> bool {
                    <u8>::uses_fd() || <u8>::uses_fd() || <RawFd>::uses_fd()
                }
                fn msg_size(&self) -> usize {
                    1 + match self {
                        MyMsg::A(enum_field0) => enum_field0.msg_size(),
                        MyMsg::B => 0,
                        MyMsg::C { f0, f1 } => f0.msg_size() + f1.msg_size(),
                    }
                }
                fn fd_count(&self) -> usize {
                    match self {
                        MyMsg::A(enum_field0) => enum_field0.fd_count(),
                        MyMsg::B => 0,
                        MyMsg::C { f0, f1 } => f0.fd_count() + f1.fd_count(),
                    }
                }
                unsafe fn read_from_buffer(
                    buffer: &[u8],
                    fds: &[std::os::unix::io::RawFd],
                ) -> msg_socket::MsgResult<(Self, usize)> {
                    let v = buffer
                        .get(0)
                        .ok_or(msg_socket::MsgError::WrongMsgBufferSize)?;
                    match v {
                        0u8 => {
                            let mut __offset = 1usize;
                            let mut __fd_offset = 0usize;
                            let t = <u8>::read_from_buffer(&buffer[__offset..], &fds[__fd_offset..])?;
                            __offset += t.0.msg_size();
                            __fd_offset += t.1;
                            let enum_field0 = t.0;
                            Ok((MyMsg::A(enum_field0), __fd_offset))
                        }
                        1u8 => Ok((MyMsg::B, 0)),
                        2u8 => {
                            let mut __offset = 1usize;
                            let mut __fd_offset = 0usize;
                            let t = <u8>::read_from_buffer(&buffer[__offset..], &fds[__fd_offset..])?;
                            __offset += t.0.msg_size();
                            __fd_offset += t.1;
                            let f0 = t.0;
                            let t = <RawFd>::read_from_buffer(&buffer[__offset..], &fds[__fd_offset..])?;
                            __offset += t.0.msg_size();
                            __fd_offset += t.1;
                            let f1 = t.0;
                            Ok((MyMsg::C { f0, f1 }, __fd_offset))
                        }
                        _ => Err(msg_socket::MsgError::InvalidType),
                    }
                }
                fn write_to_buffer(
                    &self,
                    buffer: &mut [u8],
                    fds: &mut [std::os::unix::io::RawFd],
                ) -> msg_socket::MsgResult<usize> {
                    if buffer.is_empty() {
                        return Err(msg_socket::MsgError::WrongMsgBufferSize)
                    }
                    match self {
                        MyMsg::A(enum_field0) => {
                            buffer[0] = 0u8;
                            let mut __offset = 1usize;
                            let mut __fd_offset = 0usize;
                            let o = enum_field0
                                .write_to_buffer(&mut buffer[__offset..], &mut fds[__fd_offset..])?;
                            __offset += enum_field0.msg_size();
                            __fd_offset += o;
                            Ok(__fd_offset)
                        }
                        MyMsg::B => {
                            buffer[0] = 1u8;
                            Ok(0)
                        }
                        MyMsg::C { f0, f1 } => {
                            buffer[0] = 2u8;
                            let mut __offset = 1usize;
                            let mut __fd_offset = 0usize;
                            let o = f0.write_to_buffer(&mut buffer[__offset..], &mut fds[__fd_offset..])?;
                            __offset += f0.msg_size();
                            __fd_offset += o;
                            let o = f1.write_to_buffer(&mut buffer[__offset..], &mut fds[__fd_offset..])?;
                            __offset += f1.msg_size();
                            __fd_offset += o;
                            Ok(__fd_offset)
                        }
                    }
                }
            }
        };

        assert_eq!(msg_socket_impl(input).to_string(), expected.to_string());
    }

    #[test]
    fn end_to_end_struct_skip_test() {
        let input: DeriveInput = parse_quote! {
            struct MyMsg {
                #[msg_on_socket(skip)]
                a: u8,
            }
        };

        let expected = quote! {
            impl msg_socket::MsgOnSocket for MyMsg {
                fn msg_size(&self) -> usize {
                    0
                }
                fn fd_count(&self) -> usize {
                    0
                }
                unsafe fn read_from_buffer(
                    buffer: &[u8],
                    fds: &[std::os::unix::io::RawFd],
                ) -> msg_socket::MsgResult<(Self, usize)> {
                    let mut __offset = 0usize;
                    let mut __fd_offset = 0usize;
                    Ok((Self { a: <u8>::default() }, __fd_offset))
                }
                fn write_to_buffer(
                    &self,
                    buffer: &mut [u8],
                    fds: &mut [std::os::unix::io::RawFd],
                ) -> msg_socket::MsgResult<usize> {
                    let mut __offset = 0usize;
                    let mut __fd_offset = 0usize;
                    Ok(__fd_offset)
                }
            }

        };

        assert_eq!(msg_socket_impl(input).to_string(), expected.to_string());
    }
}