Repository: sharksforarms/deku (rust)

Issue:
Help passing context around
### Discussed in https://github.com/sharksforarms/deku/discussions/396

<div type='discussions-op-text'>

<sup>Originally posted by **alexjbuck** January  2, 2024</sup>
I'm brand new to the Deku crate. Its really cool!

I'm looking to parse some binary packet data with this crate. The general structure of packets is a Header/Payload structure. The header is an Identifier and a total packet length (as u32).

My initial thought was to treat the payload section as an enum where the id is an external id passed from the header type through the context variable (referencing the [docs](https://docs.rs/deku/0.16.0/deku/attributes/index.html#id-top-level))

Below is a minimal implementation for one variant but I'm getting a proc-macro error that I don't understand.

```rust
use deku::prelude::*;

#[derive(Debug, PartialEq, DekuRead, DekuWrite)]
struct Packet {
    header: Header,
    #[deku(ctx = "*header")]
    payload: Payload,
}


#[derive(Debug, PartialEq, DekuRead, DekuWrite)]
struct Header {
    kind: PacketType,
    length: u32,
}


#[derive(Debug, PartialEq, DekuRead, DekuWrite)]
#[deku(type = "[u8;4]", endian="big")]
enum PacketType {
    #[deku(id = b"INFO")]
    InfoType,
}


#[derive(Debug, PartialEq, DekuRead, DekuWrite)]
#[deku(ctx = "header: Header", id = "header.kind")]
enum Payload {
    #[deku(id = "PacketType::InfoType")]
    Info {
        #[deku(count = "header.length-8")]
        data: Vec<u8>
    },
}

fn main() {
    // let data: Vec<u8> = vec![0x54, 0x49, 0x4D, 0x45,0x00,0x00,0x00,0x0C, 0x00,0x00,0x00,0x10, 0x53, 0x46, 0x4F, 0x52, 0x00, 0x00, 0x00, 0xA0];
    let data : Vec<u8> = vec![0x49, 0x4E, 0x46, 0x4F, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x10];
    let mut rest = (data.as_ref(),0 as usize);

    let (_rest,val) = Packet::from_bytes(rest).unwrap();
    dbg!(val);
    dbg!(rest);
}
```
These are the errors:
```rust
error: proc-macro derive panicked
  --> src\main.rs:26:28
   |
26 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
   |                            ^^^^^^^^
   |
   = help: message: called `Result::unwrap()` on an `Err` value: Error("expected `,`")

error[E0277]: the trait bound `Payload: deku::DekuRead<'_, _>` is not satisfied
 --> src\main.rs:7:14
  |
7 |     payload: Payload,
  |              ^^^^^^^ the trait `deku::DekuRead<'_, _>` is not implemented for `Payload`
  |
  = help: the following other types implement trait `deku::DekuRead<'a, Ctx>`:
            <bool as deku::DekuRead<'a, Ctx>>
            <isize as deku::DekuRead<'_, (Endian, deku::ctx::ByteSize)>>
            <isize as deku::DekuRead<'_, (Endian, deku::ctx::BitSize)>>
            <isize as deku::DekuRead<'_, Endian>>
            <isize as deku::DekuRead<'_, deku::ctx::ByteSize>>
            <isize as deku::DekuRead<'_, deku::ctx::BitSize>>
            <isize as deku::DekuRead<'_>>
            <i8 as deku::DekuRead<'_, (Endian, deku::ctx::ByteSize)>>
          and 155 others

For more information about this error, try `rustc --explain E0277`.
error: could not compile `test-rs` (bin "test-rs") due to 3 previous errors
```

I'd appreciate any pointers to what I may be doing wrong here.</div>

Pull request:
Fix unknown id Type for EnumExt::deku_id()
Remove unwrap causing the following error when the EnumExt tried to infer what type is was to return. An attribute could be added in the future to aid this, but for now we will just not emit the deku_id() for this type of enum. I decided in other causes (which aren't tested), to also remove the error and just not emit the function also.

 help: message: called `Result::unwrap()` on an `Err` value: Error("expected `,`")

Closes #397

Patch:
diff --git a/deku-derive/src/macros/deku_read.rs b/deku-derive/src/macros/deku_read.rs
index 9e7a788..d5b20d0 100644
--- a/deku-derive/src/macros/deku_read.rs
+++ b/deku-derive/src/macros/deku_read.rs
@@ -4,7 +4,6 @@ use darling::ast::{Data, Fields};
 use darling::ToTokens;
 use proc_macro2::TokenStream;
 use quote::quote;
-use syn::spanned::Spanned;
 
 use crate::macros::{
     gen_ctx_types_and_arg, gen_field_args, gen_internal_field_ident, gen_internal_field_idents,
@@ -397,12 +396,7 @@ fn emit_enum(input: &DekuData) -> Result<TokenStream, syn::Error> {
     let deku_id_type = if let Some(id_type) = id_type {
         Some(quote! {#id_type})
     } else if let (Some(ctx), Some(id)) = (input.ctx.as_ref(), input.id.as_ref()) {
-        Some(gen_type_from_ctx_id(ctx, id).ok_or_else(|| {
-            syn::Error::new(
-                id.span(),
-                "DekuReader: cannot determine `id` type from `ctx`",
-            )
-        })?)
+        gen_type_from_ctx_id(ctx, id)
     } else {
         None
     };
diff --git a/deku-derive/src/macros/mod.rs b/deku-derive/src/macros/mod.rs
index b24d0ba..2fa865c 100644
--- a/deku-derive/src/macros/mod.rs
+++ b/deku-derive/src/macros/mod.rs
@@ -199,7 +199,9 @@ fn gen_type_from_ctx_id(
     id: &crate::Id,
 ) -> Option<TokenStream> {
     let parser = Punctuated::<Ident, Comma>::parse_terminated;
-    let s = parser.parse(id.to_token_stream().into()).unwrap();
+    let Ok(s) = parser.parse(id.to_token_stream().into()) else {
+        return None;
+    };
     let mut matching_types = quote! {};
     for s in s {
         let id = syn::Ident::new(&s.to_string(), id.span());


Test patch:
diff --git a/tests/test_regression.rs b/tests/test_regression.rs
index c3afb70..84ff990 100644
--- a/tests/test_regression.rs
+++ b/tests/test_regression.rs
@@ -359,3 +359,35 @@ fn issue_310() {
     #[derive(DekuRead, DekuWrite)]
     struct Test {}
 }
+
+#[test]
+fn issue_397() {
+    use deku::prelude::*;
+
+    #[derive(Debug, Copy, Clone, PartialEq, DekuRead, DekuWrite)]
+    struct Header {
+        kind: PacketType,
+    }
+
+    #[derive(Debug, Copy, Clone, PartialEq, DekuRead, DekuWrite)]
+    #[deku(type = "u8", endian = "big")]
+    enum PacketType {
+        #[deku(id = 0)]
+        Zero,
+    }
+
+    #[derive(Debug, Copy, Clone, PartialEq, DekuRead, DekuWrite)]
+    struct Packet {
+        header: Header,
+        #[deku(ctx = "header")]
+        payload: Payload,
+    }
+
+    #[derive(Debug, Copy, Clone, PartialEq, DekuRead, DekuWrite)]
+    #[deku(ctx = "header: &Header", id = "header.kind")]
+    enum Payload {
+        #[deku(id = "PacketType::Zero")]
+        Zero(u8),
+    }
+    let _ = Packet::from_bytes((&[0x00, 0x01], 0));
+}