Repository: sharksforarms/deku (rust)

Issue:
Support unit structure `struct S;` in derive-macro `DekuWrite`
We should support unit structure `struct S;` since `struct S {}` and `struct S();` are allowed to derive `DekuWrite`.
```rust
#[derive(DekuWrite)] // error: expected tuple struct or tuple variant, found unit struct `Zero`
#[deku(magic = b"\x00")]
struct Zero;
```
And `DekuRead` is no error here.

Pull request:
Support Unit like structs for DekuWrite
* Fix support for DekuWrite by not adding fields to the struct destruction in deku-write proc-macro
* Add test

Closes #449

Patch:
diff --git a/Cargo.toml b/Cargo.toml
index b7c6e0d..bf7f759 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -46,3 +46,11 @@ assert_hex = "0.4.1"
 [[bench]]
 name = "deku"
 harness = false
+
+[lints]
+workspace = true
+
+[workspace.lints.clippy]
+# Triggers in macro generated code of darling
+# https://github.com/rust-lang/rust-clippy/issues/12643
+manual-unwrap-or-default = "allow"
diff --git a/deku-derive/Cargo.toml b/deku-derive/Cargo.toml
index 24d6e7f..b7c3d3a 100644
--- a/deku-derive/Cargo.toml
+++ b/deku-derive/Cargo.toml
@@ -28,3 +28,6 @@ proc-macro-crate = { version = "3.1.0", optional = true }
 
 [dev-dependencies]
 rstest = "0.19"
+
+[lints]
+workspace = true
diff --git a/deku-derive/src/macros/deku_write.rs b/deku-derive/src/macros/deku_write.rs
index 3f3b90f..871d162 100644
--- a/deku-derive/src/macros/deku_write.rs
+++ b/deku-derive/src/macros/deku_write.rs
@@ -34,6 +34,7 @@ fn emit_struct(input: &DekuData) -> Result<TokenStream, syn::Error> {
     let field_updates = emit_field_updates(&fields, Some(quote! { self. }));
 
     let named = fields.style.is_struct();
+    let unit = fields.style.is_unit();
 
     let field_idents = fields.iter().enumerate().filter_map(|(i, f)| {
         if !f.temp {
@@ -43,7 +44,7 @@ fn emit_struct(input: &DekuData) -> Result<TokenStream, syn::Error> {
         }
     });
 
-    let destructured = gen_struct_destruction(named, &input.ident, field_idents);
+    let destructured = gen_struct_destruction(named, unit, &input.ident, field_idents);
 
     // Implement `DekuContainerWrite` for types that don't need a context
     if input.ctx.is_none() || (input.ctx.is_some() && input.ctx_default.is_some()) {
diff --git a/deku-derive/src/macros/mod.rs b/deku-derive/src/macros/mod.rs
index 8853dc4..847db15 100644
--- a/deku-derive/src/macros/mod.rs
+++ b/deku-derive/src/macros/mod.rs
@@ -95,10 +95,15 @@ fn gen_struct_init<I: ToTokens>(
 /// - Unnamed: `#ident ( ref fields )`
 fn gen_struct_destruction<I: ToTokens, F: ToTokens>(
     named: bool,
+    unit: bool,
     ident: I,
     field_idents: impl Iterator<Item = F>,
 ) -> TokenStream {
-    if named {
+    if unit {
+        quote! {
+            #ident
+        }
+    } else if named {
         quote! {
             #ident {
                 #(ref #field_idents),*


Test patch:
diff --git a/tests/test_struct.rs b/tests/test_struct.rs
index 41e3b3d..c5bb117 100644
--- a/tests/test_struct.rs
+++ b/tests/test_struct.rs
@@ -204,3 +204,19 @@ fn test_big_endian() {
     let new_bytes = a.to_bytes().unwrap();
     assert_eq!(&bytes[..2], &*new_bytes);
 }
+
+#[test]
+fn test_units() {
+    #[derive(DekuRead, DekuWrite)]
+    #[deku(magic = b"\xf0")]
+    struct Unit;
+
+    let bytes = [0xf0];
+    let a = Unit::from_bytes((&bytes, 0)).unwrap().1;
+    let new_bytes = a.to_bytes().unwrap();
+    assert_eq!(bytes, &*new_bytes);
+
+    let a = Unit;
+    let new_bytes = a.to_bytes().unwrap();
+    assert_eq!(bytes, &*new_bytes);
+}