Преглед изворни кода

Extend structured swift to support generic structs (#2154)

Motivation:

The structured swift representation we're using doesn't support generic
struct at the moment. We'll need this for an upcoming change.

Modifications:

- Allow for generic structs with where clauses to be represented and
rendered.

Result:

Generic structs can be rendered
George Barnett пре 10 месеци
родитељ
комит
76073a2c1a

+ 21 - 0
Sources/GRPCCodeGen/Internal/Renderer/TextBasedRenderer.swift

@@ -740,10 +740,31 @@ struct TextBasedRenderer: RendererProtocol {
     }
     writer.writeLine("struct \(structDesc.name)")
     writer.nextLineAppendsToLastLine()
+    let generics = structDesc.generics
+    if !generics.isEmpty {
+      writer.nextLineAppendsToLastLine()
+      writer.writeLine("<")
+      for (genericType, isLast) in generics.enumeratedWithLastMarker() {
+        writer.nextLineAppendsToLastLine()
+        renderExistingTypeDescription(genericType)
+        if !isLast {
+          writer.nextLineAppendsToLastLine()
+          writer.writeLine(", ")
+        }
+      }
+      writer.nextLineAppendsToLastLine()
+      writer.writeLine(">")
+      writer.nextLineAppendsToLastLine()
+    }
     if !structDesc.conformances.isEmpty {
       writer.writeLine(": \(structDesc.conformances.joined(separator: ", "))")
       writer.nextLineAppendsToLastLine()
     }
+    if let whereClause = structDesc.whereClause {
+      writer.nextLineAppendsToLastLine()
+      writer.writeLine(" " + renderedWhereClause(whereClause))
+      writer.nextLineAppendsToLastLine()
+    }
     writer.writeLine(" {")
     if !structDesc.members.isEmpty {
       writer.withNestedLevel {

+ 6 - 0
Sources/GRPCCodeGen/Internal/StructuredSwiftRepresentation.swift

@@ -401,11 +401,17 @@ struct StructDescription: Equatable, Codable, Sendable {
   /// For example, in `struct Foo {`, `name` is `Foo`.
   var name: String
 
+  /// The generic types of the struct.
+  var generics: [ExistingTypeDescription] = []
+
   /// The type names that the struct conforms to.
   ///
   /// For example: `["Sendable", "Codable"]`.
   var conformances: [String] = []
 
+  /// A where clause constraining the struct declaration.
+  var whereClause: WhereClause? = nil
+
   /// The declarations that make up the main struct body.
   var members: [Declaration] = []
 }

+ 51 - 1
Tests/GRPCCodeGenTests/Internal/Renderer/TextBasedRendererTests.swift

@@ -820,12 +820,62 @@ final class Test_TextBasedRenderer: XCTestCase {
 
   func testStruct() throws {
     try _test(
-      .init(name: "Structy"),
+      StructDescription(name: "Structy"),
       renderedBy: { $0.renderStruct(_:) },
       rendersAs: #"""
         struct Structy {}
         """#
     )
+    try _test(
+      StructDescription(
+        name: "Structy",
+        conformances: ["Foo"]
+      ),
+      renderedBy: { $0.renderStruct(_:) },
+      rendersAs: #"""
+        struct Structy: Foo {}
+        """#
+    )
+    try _test(
+      StructDescription(
+        name: "Structy",
+        generics: [.member("T")]
+      ),
+      renderedBy: { $0.renderStruct(_:) },
+      rendersAs: #"""
+        struct Structy<T> {}
+        """#
+    )
+    try _test(
+      StructDescription(
+        name: "Structy",
+        generics: [.member("T")],
+        whereClause: WhereClause(
+          requirements: [
+            .conformance("T", "Foo"),
+            .conformance("T", "Sendable"),
+          ]
+        )
+      ),
+      renderedBy: {
+        $0.renderStruct(_:)
+      },
+      rendersAs: #"""
+        struct Structy<T> where T: Foo, T: Sendable {}
+        """#
+    )
+    try _test(
+      StructDescription(
+        name: "Structy",
+        generics: [.member("T")],
+        conformances: ["Hashable"],
+        whereClause: WhereClause(requirements: [.conformance("T", "Foo")])
+      ),
+      renderedBy: { $0.renderStruct(_:) },
+      rendersAs: #"""
+        struct Structy<T>: Hashable where T: Foo {}
+        """#
+    )
   }
 
   func testProtocol() throws {