Need advice how to avoid code duplication in macroses (created by macro_rules!)

I have a macros and I want to avoid code duplication there (first and third rules are pretty similar). Could somebody share common practices and good hints of how to do this ?

This is my macros:

#[macro_export]
macro_rules! packet {
    // first rule
    (
        properties {
            $(opcode $opcode_value:expr;)?
        }

        $(#[$outer:meta])*
        $vis:vis struct $PacketStruct:ident {
            $($field_name:ident: $field_type:ty),*$(,)?
        }

        $($PacketStructImpl: item)*
    ) => {
        $(#[$outer])*
        #[derive(Clone, PartialEq, Debug)]
        $vis struct $PacketStruct {
            $($field_name: $field_type),*
        }

        $($PacketStructImpl)*

        $(
            impl $PacketStruct {
                pub fn get_opcode() -> u32 {
                    $opcode_value as u32
                }
            }
        )?

        impl $PacketStruct {
            pub fn to_binary(&mut self) -> Vec<u8> {
                let mut packet = Vec::new();
                $(
                    $crate::types::traits::BinaryConverter::write_into(
                        &mut self.$field_name,
                        &mut packet
                    );
                )*;
                packet
            }
        }
    };
    // eof first rule
    // second rule
    (
        properties {
            opcode $opcode_value:expr;
            $($prop_name:ident: $prop_type:ty;)*
        }

        $(#[$outer:meta])*
        $vis:vis struct $PacketStruct:ident {
            $($field_name:ident: $field_type:ty),*$(,)?
        }

        $($PacketStructImpl: item)*
    ) => {
        packet! {
            properties {
                $($prop_name: $prop_type;)*
            }
            $(#[$outer])*
            $vis struct $PacketStruct {
                $($field_name: $field_type),*
            }

            $($PacketStructImpl)*

            impl $PacketStruct {
                pub fn get_opcode() -> u32 {
                    $opcode_value as u32
                }
            }
        }
    };
    // eof of second rule
    // third rule
    (
        properties {
            $($prop_name:ident: $prop_type:ty;)*
        }

        $(#[$outer:meta])*
        $vis:vis struct $PacketStruct:ident {
            $($field_name:ident: $field_type:ty),*$(,)?
        }

        $($PacketStructImpl: item)*
    ) => {
        $(#[$outer])*
        #[derive(Clone, PartialEq, Debug)]
        $vis struct $PacketStruct {
            $($field_name: $field_type),*
        }

        $($PacketStructImpl)*

        impl $PacketStruct {
            pub fn to_binary(&mut self) -> Vec<u8> {
                let mut packet = Vec::new();
                $(
                    $crate::types::traits::BinaryConverter::write_into(
                        &mut self.$field_name,
                        &mut packet
                    );
                )*;
                packet
            }
        }
    };
}

This is sandbox (with example of how to use it).

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.