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

Issue:
Support `inline` and `flatten` for generic types
Currently, inlining fails to take the used generic type argument into account:
```rust
struct X<T> {
  t: T
}
struct Y {
  #[ts(inline)]
  x1: X<i32>,
  #[ts(flatten)]
  x2: X<i32>
}
```

Pull request description:
Implement inline generics
Implementation of inlined generics. Not 100% sure it works, but it did pass the `#[ignore]`d test

Fixes #56

Patch:
diff --git a/macros/src/types/generics.rs b/macros/src/types/generics.rs
index 4766de9..beacc29 100644
--- a/macros/src/types/generics.rs
+++ b/macros/src/types/generics.rs
@@ -40,7 +40,7 @@ pub fn format_generics(deps: &mut Dependencies, generics: &Generics) -> TokenStr
 
 pub fn format_type(ty: &Type, dependencies: &mut Dependencies, generics: &Generics) -> TokenStream {
     // If the type matches one of the generic parameters, just pass the identifier:
-    if let Some(generic_ident) = generics
+    if let Some(generic) = generics
         .params
         .iter()
         .filter_map(|param| match param {
@@ -55,9 +55,26 @@ pub fn format_type(ty: &Type, dependencies: &mut Dependencies, generics: &Generi
                     && type_path.path.is_ident(&type_param.ident)
             )
         })
-        .map(|type_param| type_param.ident.to_string())
     {
-        return quote!(#generic_ident.to_owned());
+        let generic_ident = generic.ident.clone();
+        let generic_ident_str = generic_ident.to_string();
+
+        if !generic.bounds.is_empty() || generic.default.is_some() {
+            return quote!(#generic_ident_str.to_owned());
+        }
+
+        return quote!(
+            match <#generic_ident>::name().as_str() {
+                // When exporting a generic, the default type used is `()`,
+                // which gives "null" when calling `.name()`. In this case, we
+                // want to preserve the type param's identifier as the name used
+                "null" => #generic_ident_str.to_owned(),
+
+                // If name is not "null", a type has been provided, so we use its
+                // name instead
+                x => x.to_owned()
+            }
+        );
     }
 
     // special treatment for arrays and tuples


Test patch:
diff --git a/ts-rs/tests/generics.rs b/ts-rs/tests/generics.rs
index b4f8183..961fffc 100644
--- a/ts-rs/tests/generics.rs
+++ b/ts-rs/tests/generics.rs
@@ -147,8 +147,6 @@ fn generic_struct() {
 }
 
 #[test]
-#[ignore]
-// https://github.com/Aleph-Alpha/ts-rs/issues/56 TODO
 fn inline() {
     #[derive(TS)]
     struct Generic<T> {
@@ -167,7 +165,57 @@ fn inline() {
     assert_eq!(Generic::<()>::decl(), "type Generic<T> = { t: T, }");
     assert_eq!(
         Container::decl(),
-        "type Container = { g: Generic<string>, gi: { t: string }, t: string, }"
+        "type Container = { g: Generic<string>, gi: { t: string, }, t: string, }"
+    );
+}
+
+#[test]
+#[ignore = "We haven't figured out how to inline generics with bounds yet"]
+fn inline_with_bounds() {
+    #[derive(TS)]
+    struct Generic<T: ToString> {
+        t: T,
+    }
+
+    #[derive(TS)]
+    struct Container {
+        g: Generic<String>,
+        #[ts(inline)]
+        gi: Generic<String>,
+        #[ts(flatten)]
+        t: Generic<u32>,
+    }
+
+    assert_eq!(Generic::<&'static str>::decl(), "type Generic<T> = { t: T, }");
+    assert_eq!(
+        Container::decl(),
+        "type Container = { g: Generic<string>, gi: { t: string, }, t: number, }"
+        // Actual output: { g: Generic<string>, gi: { t: T, }, t: T, }
+    );
+}
+
+#[test]
+#[ignore = "We haven't figured out how to inline generics with defaults yet"]
+fn inline_with_default() {
+    #[derive(TS)]
+    struct Generic<T = String> {
+        t: T,
+    }
+
+    #[derive(TS)]
+    struct Container {
+        g: Generic<String>,
+        #[ts(inline)]
+        gi: Generic<String>,
+        #[ts(flatten)]
+        t: Generic<u32>,
+    }
+
+    assert_eq!(Generic::<&'static str>::decl(), "type Generic<T = string> = { t: T, }");
+    assert_eq!(
+        Container::decl(),
+        "type Container = { g: Generic<string>, gi: { t: string, }, t: number, }"
+        // Actual output: { g: Generic<string>, gi: { t: T, }, t: T, }
     );
 }