Macro implemented with macro_rules detects wrong pattern

I have a macros where I try to parse structures like this:

packet! {
    @option[opcode=100]
    @option[no_size: true]
    @option[compressed: true]
    struct Income {
        name: String,
        age: u64,
        is_cool: bool,
    }
}

I use next pattern to detect the structures:

$(@option[opcode=$opcode_value:expr])?
$(@option[no_size:$no_size:expr])?
$(@option[compressed:$compressed_value:expr])?

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

$($PacketStructImpl: item)*

The issue with current macros implementation is that $opcode_value should be integer, but for some reason it expected by macros to be bool, so on compile I got next error:

error[E0308]: mismatched types
   --> src/main.rs:189:20
    |
189 |     @option[opcode=100]
    |                    ^^^ expected `bool`, found integer

This is full code of the macros:

macro_rules! packet {
    (
        $(@option[opcode=$opcode_value:expr])?
        $(@option[no_size:$no_size:expr])?
        $(@option[compressed:$compressed_value:expr])?

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

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

        $($PacketStructImpl)*

        impl $PacketStruct {
            // income
            pub fn from_binary(buffer: &Vec<u8>) -> Self {
                let mut omit_bytes: usize = INCOMING_HEADER_LENGTH;
                $(
                    if $no_size {
                        // because no_size packets are only on login server
                        omit_bytes = 1;
                    }
                )?
                $(
                    if $compressed_value {
                        // 4 bytes uncompressed + 2 bytes used by zlib
                        omit_bytes += 6;
                    }
                )?

                let mut internal_buffer: Vec<u8> = Vec::new();
                $(
                    if $compressed_value {
                        let data = &buffer[omit_bytes..];
                        let mut decoder = flate2::read::DeflateDecoder::new(data);
                        decoder.read_to_end(&mut internal_buffer).unwrap();
                    }
                )?

                let buffer = if internal_buffer.is_empty() {
                    buffer[omit_bytes..].to_vec()
                } else {
                    internal_buffer
                };

                let mut reader = std::io::Cursor::new(&buffer);

                Self {
                    $(
                        $field_name: BinaryConverter::read_from(
                            &mut reader
                        ).unwrap()
                    ),*
                }
            }

            // outcome
            pub fn to_binary(&mut self) -> Vec<u8> {
                let mut packet = Vec::new();
                $(
                    BinaryConverter::write_into(
                        &mut self.$field_name,
                        &mut packet
                    ).unwrap();
                )*
                let header = Self::_build_header(&packet);
                [header, packet].concat()
            }

            fn _build_header(body: &Vec<u8>) -> Vec<u8> {
                let mut header: Vec<u8> = Vec::new();
                $(
                    if $no_size {
                        header.write_u8($opcode_value).unwrap();
                    }
                )?
                $(
                    if $opcode_value {
                        let size = body.len() + OUTCOMING_OPCODE_LENGTH;
                        header.write_u16::<BigEndian>(size as u16).unwrap();
                        header.write_u32::<LittleEndian>($opcode_value).unwrap();
                    }
                )?

                header
            }

            $(
                pub fn unpack(&mut self) -> PacketOutcome {
                    ($opcode_value as u32, self.to_binary())
                }
            )?
        }
    };
}

This is sandbox that reproducing the issue.

Could somebody explain how to fix an issue and why this happen ?

I believe if $opcode_value is where the error is coming from. If you're just trying to make sure that code doesn't run if the option isn't specified in the macro input, I don't think you need the conditional. If that pattern isn't matched that block shouldn't be expanded in the macro output

2 Likes

Well, I think if was redundant there, so next should work without if $opcode_value:

$(
	let size = body.len() + OUTCOMING_OPCODE_LENGTH;
	header.write_u16::<BigEndian>(size as u16).unwrap();
	header.write_u32::<LittleEndian>($opcode_value).unwrap();
)?

But I still got errors:

error[E0277]: the trait bound `bool: BinaryConverter` is not satisfied
   --> src/main.rs:141:38
    |
141 |                           $field_name: BinaryConverter::read_from(
    |                                        ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `BinaryConverter` is not implemented for `bool`
...
186 | / packet! {
187 | |     @option[opcode=100]
188 | |     @option[no_size: true]
189 | |     @option[compressed: true]
...   |
194 | |     }
195 | | }
    | |_- in this macro invocation
    |
    = help: the following other types implement trait `BinaryConverter`:
              String
              [u8; N]
              u16
              u32
              u64
              u8
    = note: this error originates in the macro `packet` (in Nightly builds, run with -Z macro-backtrace for more info)

for me it seems like $field_name detected as bool (please correct me if I wrong). I tried to remove @options but error still there.

oh, my fault. I not implemented bool for fields. So all works OK now. Thank you @semicoleon !

1 Like

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.