#parser #enums #attributes #macro #proc-macro #serialization #generate

macro enum_parse

Procedural macro generating boilerplate code for parsing enum variants

1 unstable release

0.1.0 Oct 2, 2023

#192 in #attributes

MIT license

21KB
319 lines

enum_parse

Provides the enum_parse procedural macro that generates boilerplate code for parsing enum variants.

#[enum_parse(derive(SomehowParsable, Debug),
             repr(C, packed),
             attr(parse_input = &[u8], parse_fn = somehow_parse))]
pub enum Payload {
    #[attr(ID = 0x2b)]
    Hello { a: u8, b: u64, c: u64, d: u8 },
    #[attr(ID = 0x42)]
    Goodbye { a: u8, e: u8 },
    #[attr(ID = _)]
    Unknown,
}

pub fn parse_packet(data: &[u8]) -> Option<Payload> {
    let id: usize = data[0] as usize;
    // parse method is generated by the macro
    Payload::parse(&data[1..], id)
}

(SomehowParsable is an imaginary derive that implements a trait) This is evaluated to the following code:

pub enum Payload {
    Hello(Hello),
    Goodbye(Goodbye),
    Unknown,
}

impl Payload {
    pub fn parse(data: &[u8], id: usize) -> Option<Self> {
        match id {
            Hello::ID => Hello::read_from(data).map(|s| Self::Hello(s)),
            Goodbye::ID => Goodbye::read_from(data).map(|s| Self::Goodbye(s)),
            _ => Some(Self::Unknown),
        }
    }
}

#[derive(SomehowParsable, Debug, Default)]
#[repr(C, packed)]
pub struct Hello {
    pub a: u8,
    pub b: u64,
    pub c: u64,
    pub d: u8,
}
impl Hello {
    pub const ID: usize = 0x2b;
}

#[derive(SomehowParsable, Debug, Default)]
#[repr(C, packed)]
pub struct Goodbye {
    pub a: u8,
    pub e: u8,
}
impl Goodbye {
    pub const ID: usize = 0x42;
}

#[derive(SomehowParsable, Debug, Default)]
#[repr(C, packed)]
pub struct Unknown {}

pub fn parse_packet(data: &[u8]) -> Option<Payload> {
    let id: usize = data[0] as usize;
    Payload::parse(&data[1..], id)
}

There's a lot of duplicated code in the expanded code - each struct needs to be attributed, put in the enum, and given a match case in parse() method. That's why using a proc macro instead can be useful.

Dependencies

~235–670KB
~16K SLoC