Repository: knurling-rs/defmt (rust)

Issue:
Corrupted stream with enum with 1 case
```rust
#[derive(defmt::Format)]
pub enum Payload {
    Number(u32),
}

let payload = Payload::Number(31337);
info!("print of death: {:?}", payload);
```

I guess it happens because the format string has no `|` char, so decoding doesn't expect the discriminator...?

Pull request:
Fix encoding of single-variant enums
Fixes #225 

Patch:
diff --git a/firmware/qemu/src/bin/log.out b/firmware/qemu/src/bin/log.out
index b3de6ae..9b70ee0 100644
--- a/firmware/qemu/src/bin/log.out
+++ b/firmware/qemu/src/bin/log.out
@@ -83,3 +83,5 @@
 0.000082 INFO [[u32;4];4]: [[1, 2, 3, 4], [2, 3, 4, 5], [3, 4, 5, 6], [4, 5, 6, 7]]
 0.000083 INFO [Option<u32>;4]: [Some(1), None, Some(3), None]
 0.000084 INFO [(u32,u32);4]: [(1, 2), (3, 4), (5, 6), (7, 8)]
+0.000085 INFO 1-variant enum: A { fld: 123 }
+0.000086 INFO wrapped: A(A { fld: 200 })
diff --git a/firmware/qemu/src/bin/log.release.out b/firmware/qemu/src/bin/log.release.out
index 0f7c890..e54fe3c 100644
--- a/firmware/qemu/src/bin/log.release.out
+++ b/firmware/qemu/src/bin/log.release.out
@@ -81,3 +81,5 @@
 0.000080 INFO [[u32;4];4]: [[1, 2, 3, 4], [2, 3, 4, 5], [3, 4, 5, 6], [4, 5, 6, 7]]
 0.000081 INFO [Option<u32>;4]: [Some(1), None, Some(3), None]
 0.000082 INFO [(u32,u32);4]: [(1, 2), (3, 4), (5, 6), (7, 8)]
+0.000083 INFO 1-variant enum: A { fld: 123 }
+0.000084 INFO wrapped: A(A { fld: 200 })
diff --git a/firmware/qemu/src/bin/log.rs b/firmware/qemu/src/bin/log.rs
index 8c858d5..2f0f64b 100644
--- a/firmware/qemu/src/bin/log.rs
+++ b/firmware/qemu/src/bin/log.rs
@@ -366,6 +366,22 @@ fn main() -> ! {
         [(1u32, 2u32), (3, 4), (5, 6), (7, 8)]
     );
 
+    {
+        #[derive(Format)]
+        enum Single {
+            A { fld: u8 },
+        }
+
+        defmt::info!("1-variant enum: {:?}", Single::A { fld: 123 });
+
+        #[derive(Format)]
+        enum Wrap {
+            A(Single),
+        }
+
+        defmt::info!("wrapped: {:?}", Wrap::A(Single::A { fld: 200 }));
+    }
+
     loop {
         debug::exit(debug::EXIT_SUCCESS)
     }
diff --git a/macros/src/lib.rs b/macros/src/lib.rs
index 9777bd3..1d2d97b 100644
--- a/macros/src/lib.rs
+++ b/macros/src/lib.rs
@@ -178,6 +178,7 @@ pub fn format(ts: TokenStream) -> TokenStream {
             }
 
             if de.variants.is_empty() {
+                // For zero-variant enums, this is unreachable code.
                 exprs.push(quote!(match *self {}));
             } else {
                 let mut arms = vec![];
@@ -202,9 +203,18 @@ pub fn format(ts: TokenStream) -> TokenStream {
                         },
                     );
 
+                    let encode_discriminant = if de.variants.len() == 1 {
+                        // For single-variant enums, there is no need to encode the discriminant.
+                        quote!()
+                    } else {
+                        quote!(
+                            f.u8(&#i);
+                        )
+                    };
+
                     arms.push(quote!(
                         #ident::#vident #pats => {
-                            f.u8(&#i);
+                            #encode_discriminant
                             f.with_tag(|f| {
                                 #(#exprs;)*
                             });
@@ -724,7 +734,8 @@ impl Codegen {
                 }
                 defmt_parser::Type::BitField(_) => {
                     let all_bitfields = parsed_params.iter().filter(|param| param.index == i);
-                    let (smallest_bit_index, largest_bit_index) = defmt_parser::get_max_bitfield_range(all_bitfields).unwrap();
+                    let (smallest_bit_index, largest_bit_index) =
+                        defmt_parser::get_max_bitfield_range(all_bitfields).unwrap();
 
                     // indices of the lowest and the highest octet which contains bitfield-relevant data
                     let lowest_byte = smallest_bit_index / 8;


Test patch:
diff --git a/tests/encode.rs b/tests/encode.rs
index 6b7971a..bb43d19 100644
--- a/tests/encode.rs
+++ b/tests/encode.rs
@@ -418,6 +418,8 @@ fn uninhabited_enum() {
     enum Void {}
 }
 
+/// Tests that univariant enums do not encode the discriminant (since the variant in use is always
+/// the same).
 #[test]
 fn univariant_enum() {
     #[derive(Format)]
@@ -430,7 +432,6 @@ fn univariant_enum() {
         &NoData::Variant,
         &[
             index, //
-            0,     // `NoData::Variant`
         ],
     );
 
@@ -444,7 +445,6 @@ fn univariant_enum() {
         &Data::Variant(0x1f, 0xaaaa),
         &[
             index, //
-            0,     // `Data::Variant`
             0x1f,  // u8
             0xaa, 0xaa, // u16
         ],
@@ -464,6 +464,7 @@ fn nested_enum() {
     #[derive(Format)]
     enum Inner {
         A(CLike, u8),
+        _B,
     }
 
     #[derive(Format)]
@@ -691,14 +692,14 @@ fn format_arrays() {
     check_format_implementation(
         &array,
         &[
-            index,             // "{:[?;3]}"
-            inc(index, 1),     // "{:u16}"
-            1,                 // [0].low
-            0,                 // [0].high
-            0,                 // [1].low
-            1,                 // [1].high
-            1,                 // [2].low
-            1,                 // [2].high
+            index,         // "{:[?;3]}"
+            inc(index, 1), // "{:u16}"
+            1,             // [0].low
+            0,             // [0].high
+            0,             // [1].low
+            1,             // [1].high
+            1,             // [2].low
+            1,             // [2].high
         ],
     );
 }