Crates.io | enum_parse |
lib.rs | enum_parse |
version | 0.1.0 |
source | src |
created_at | 2023-10-02 20:30:27.850829 |
updated_at | 2023-10-02 20:30:27.850829 |
description | Procedural macro generating boilerplate code for parsing enum variants |
homepage | |
repository | https://github.com/darsto/enum_parse |
max_upload_size | |
id | 990452 |
size | 23,218 |
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.