sharksforarms / deku

Declarative binary reading and writing: bit-level, symmetric, serialization/deserialization
Apache License 2.0
1.14k stars 55 forks source link

Enum discriminant selection based on a `cond` and `ctx`? #287

Closed vext01 closed 2 years ago

vext01 commented 2 years ago

Hi again,

Was just implementing a struct like this:

/// The `TargetIP` fields in packets which update the TIP.
///
/// This is a variable-width field depending upon the value if `IPBytes` in the parent packet.
#[derive(Debug, DekuRead)]
#[deku(ctx = "ip_bytes: IPBytes")]
struct TargetIP {
    #[deku(cond = "ip_bytes.val == 0b001")]
    ip16: Option<u16>,
    #[deku(cond = "ip_bytes.val == 0b010")]
    ip32: Option<u32>,
    #[deku(bits = "48", cond = "ip_bytes.val == 0b011 || ip_bytes.val == 0b100")]
    ip48: Option<u64>,
    #[deku(cond = "ip_bytes.val == 0b110")]
    ip64: Option<u64>,
}

The idea being that only one of the fields is Some.

And then though: "Wait a gosh-darned minute.... This is an enum!"

But reading the docs, it looks like I couldn't select an enum discriminant based on cond derived from ctx?

Imagine:

#[derive(Debug, DekuRead)]
#[deku(ctx = "ip_bytes: IPBytes")]
enum TargetIP {
    #[deku(cond = "ip_bytes.val == 0b001")]
    ip16(u16),
    #[deku(cond = "ip_bytes.val == 0b010")]
    ip32(u32),
    #[deku(bits = "48", cond = "ip_bytes.val == 0b011 || ip_bytes.val == 0b100")]
    ip48(u64),
    #[deku(cond = "ip_bytes.val == 0b110")]
    ip64(u64),

It would be a neat feature.

sharksforarms commented 2 years ago

Here is the documentation around enums: https://docs.rs/deku/latest/deku/index.html#enums

Without knowing your full data format, here's an example:

#[derive(Debug, DekuRead)]
struct IPBytes {
    #[deku(bits = "3")]
    val: u8
}

#[derive(Debug, DekuRead)]
#[deku(id = "ip_bytes", ctx = "ip_bytes: u8")]
enum TargetIP {
    #[deku(id = "0b001")]
    Ip16(u16),
    #[deku(id = "0b010")]
    Ip32(u32),
    #[deku(id_pat = "0b011 | 0b100")]
    Ip48(#[deku(bits = "48")] u64),
    #[deku(id = "0b110")]
    Ip64(u64),
}

#[derive(Debug, DekuRead)]
struct Container {
    ip_bytes: IPBytes,
    #[deku(ctx = "ip_bytes.val")]
    target_ip: TargetIP,
}

Closing for now, feel free to re-open

vext01 commented 2 years ago

Thanks! That was exactly what I was after.