Repository: Aleph-Alpha/ts-rs (rust)

Issue:
bug: Regression Empty Struct Enum Variants with serde tag attribute
**Describe the bug**
Attempting to derive TS for an enum containing empty structs as well as the serde tag attribute fails.

This bug is a regression from v9.1

**Expected behavior**
The code should compile

**Version**
v10.0

**Additional context**
Not Applicable


Pull request description:
Fix empty struct tagged enum variant
## Goal

Fix incorrect behavior of the `tag` attribute for structs without any fields declared with braces (`struct Foo {}`). These structs, when used with the `tag` attribute should compile and produce a type containing a single field representing the struct's tag.

This bug was revealed by a regression caused #344, which refactored internally tagged enums to take advantage of struct tagging instead of manually reimplementing the behavior.
Closes #369 

## Changes

How did you go about solving the problem?

## Checklist

- [x] I have followed the steps listed in the [Contributing guide](https://github.com/Aleph-Alpha/ts-rs/blob/main/CONTRIBUTING.md).
- [x] If necessary, I have added documentation related to the changes made.
- [x] I have added or updated the tests related to the changes made.


Patch:
diff --git a/macros/src/types/mod.rs b/macros/src/types/mod.rs
index b22ebb3..50bf3c4 100644
--- a/macros/src/types/mod.rs
+++ b/macros/src/types/mod.rs
@@ -37,7 +37,7 @@ fn type_def(attr: &StructAttr, ident: &str, fields: &Fields) -> Result<DerivedTS
 
     match fields {
         Fields::Named(named) => match named.named.len() {
-            0 => unit::empty_object(attr, &name),
+            0 if attr.tag.is_none() => unit::empty_object(attr, &name),
             _ => named::named(attr, &name, named),
         },
         Fields::Unnamed(unnamed) => match unnamed.unnamed.len() {
diff --git a/ts-rs/src/export.rs b/ts-rs/src/export.rs
index 7b0d08f..3640c96 100644
--- a/ts-rs/src/export.rs
+++ b/ts-rs/src/export.rs
@@ -181,9 +181,9 @@ fn export_and_merge(
     Ok(())
 }
 
-const HEADER_ERROR_MESSAGE: &'static str = "The generated strings must have their NOTE and imports separated from their type declarations by a new line";
+const HEADER_ERROR_MESSAGE: &str = "The generated strings must have their NOTE and imports separated from their type declarations by a new line";
 
-const DECLARATION_START: &'static str = "export type ";
+const DECLARATION_START: &str = "export type ";
 
 /// Inserts the imports and declaration from the newly generated type
 /// into the contents of the file, removimg duplicate imports and organazing


Test patch:
diff --git a/ts-rs/tests/integration/enum_variant_annotation.rs b/ts-rs/tests/integration/enum_variant_annotation.rs
index 6be0d41..a9107a8 100644
--- a/ts-rs/tests/integration/enum_variant_annotation.rs
+++ b/ts-rs/tests/integration/enum_variant_annotation.rs
@@ -99,3 +99,25 @@ fn test_variant_quoted() {
     }
     assert_eq!(E::inline(), r#"{ "variant-name": { f: string, } }"#)
 }
+
+#[derive(TS)]
+#[ts(export, export_to = "enum_variant_anotation/")]
+enum D {
+    Foo {},
+}
+
+#[derive(TS)]
+#[ts(export, export_to = "enum_variant_anotation/", tag = "type")]
+enum E {
+    Foo {},
+    Bar {},
+    Biz { x: i32 },
+}
+
+#[test]
+fn test_empty_struct_variant_with_tag() {
+    assert_eq!(
+        E::inline(),
+        r#"{ "type": "Foo", } | { "type": "Bar", } | { "type": "Biz", x: number, }"#
+    )
+}
diff --git a/ts-rs/tests/integration/struct_tag.rs b/ts-rs/tests/integration/struct_tag.rs
index e80475e..bcb4996 100644
--- a/ts-rs/tests/integration/struct_tag.rs
+++ b/ts-rs/tests/integration/struct_tag.rs
@@ -13,10 +13,21 @@ struct TaggedType {
     b: i32,
 }
 
+#[derive(TS)]
+#[cfg_attr(feature = "serde-compat", derive(Serialize))]
+#[cfg_attr(feature = "serde-compat", serde(tag = "type"))]
+#[cfg_attr(not(feature = "serde-compat"), ts(tag = "type"))]
+struct EmptyTaggedType {}
+
 #[test]
 fn test() {
     assert_eq!(
         TaggedType::inline(),
         "{ \"type\": \"TaggedType\", a: number, b: number, }"
-    )
+    );
+
+    assert_eq!(
+        EmptyTaggedType::inline(),
+        r#"{ "type": "EmptyTaggedType", }"#
+    );
 }