From efb175262d3a678e0768b7e8420d28f29031a535 Mon Sep 17 00:00:00 2001 From: George Barnett Date: Mon, 13 Jan 2025 09:09:25 +0000 Subject: [PATCH 1/4] Extend structured swift to support generic structs 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 --- .../Internal/Renderer/TextBasedRenderer.swift | 21 +++++++++ .../StructuredSwiftRepresentation.swift | 6 +++ .../Renderer/TextBasedRendererTests.swift | 45 ++++++++++++++++++- 3 files changed, 71 insertions(+), 1 deletion(-) diff --git a/Sources/GRPCCodeGen/Internal/Renderer/TextBasedRenderer.swift b/Sources/GRPCCodeGen/Internal/Renderer/TextBasedRenderer.swift index 0900169c0..e1fac9546 100644 --- a/Sources/GRPCCodeGen/Internal/Renderer/TextBasedRenderer.swift +++ b/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 { diff --git a/Sources/GRPCCodeGen/Internal/StructuredSwiftRepresentation.swift b/Sources/GRPCCodeGen/Internal/StructuredSwiftRepresentation.swift index ddb340c66..8db1cca87 100644 --- a/Sources/GRPCCodeGen/Internal/StructuredSwiftRepresentation.swift +++ b/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] = [] } diff --git a/Tests/GRPCCodeGenTests/Internal/Renderer/TextBasedRendererTests.swift b/Tests/GRPCCodeGenTests/Internal/Renderer/TextBasedRendererTests.swift index 65b5eb79b..7ee82c535 100644 --- a/Tests/GRPCCodeGenTests/Internal/Renderer/TextBasedRendererTests.swift +++ b/Tests/GRPCCodeGenTests/Internal/Renderer/TextBasedRendererTests.swift @@ -820,12 +820,55 @@ 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 {} + """# + ) + try _test( + StructDescription( + name: "Structy", + generics: [.member("T")], + whereClause: WhereClause(requirements: [.conformance("T", "Foo")]) + ), + renderedBy: { $0.renderStruct(_:) }, + rendersAs: #""" + struct Structy where T: Foo {} + """# + ) + try _test( + StructDescription( + name: "Structy", + generics: [.member("T")], + conformances: ["Hashable"], + whereClause: WhereClause(requirements: [.conformance("T", "Foo")]) + ), + renderedBy: { $0.renderStruct(_:) }, + rendersAs: #""" + struct Structy: Hashable where T: Foo {} + """# + ) } func testProtocol() throws { From f9fadf782ba8ba83ba4baac57a00a6c7cd9879a1 Mon Sep 17 00:00:00 2001 From: George Barnett Date: Mon, 13 Jan 2025 10:01:44 +0000 Subject: [PATCH 2/4] fix formatting --- .../Internal/Renderer/TextBasedRendererTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/GRPCCodeGenTests/Internal/Renderer/TextBasedRendererTests.swift b/Tests/GRPCCodeGenTests/Internal/Renderer/TextBasedRendererTests.swift index 7ee82c535..17e922818 100644 --- a/Tests/GRPCCodeGenTests/Internal/Renderer/TextBasedRendererTests.swift +++ b/Tests/GRPCCodeGenTests/Internal/Renderer/TextBasedRendererTests.swift @@ -839,7 +839,7 @@ final class Test_TextBasedRenderer: XCTestCase { try _test( StructDescription( name: "Structy", - generics: [.member("T")], + generics: [.member("T")] ), renderedBy: { $0.renderStruct(_:) }, rendersAs: #""" From 7e16722b9c53d2b51cc30bc4e865e58f5e1079eb Mon Sep 17 00:00:00 2001 From: George Barnett Date: Mon, 13 Jan 2025 13:20:27 +0000 Subject: [PATCH 3/4] another test --- .../Internal/Renderer/TextBasedRendererTests.swift | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Tests/GRPCCodeGenTests/Internal/Renderer/TextBasedRendererTests.swift b/Tests/GRPCCodeGenTests/Internal/Renderer/TextBasedRendererTests.swift index 17e922818..c1fd0b4ba 100644 --- a/Tests/GRPCCodeGenTests/Internal/Renderer/TextBasedRendererTests.swift +++ b/Tests/GRPCCodeGenTests/Internal/Renderer/TextBasedRendererTests.swift @@ -850,11 +850,17 @@ final class Test_TextBasedRenderer: XCTestCase { StructDescription( name: "Structy", generics: [.member("T")], - whereClause: WhereClause(requirements: [.conformance("T", "Foo")]) + whereClause: WhereClause( + requirements: [ + .conformance("T", "Foo"), + .conformance("T", "Sendable") + ] + ) ), - renderedBy: { $0.renderStruct(_:) }, + renderedBy: { $0.renderStruct(_:) + }, rendersAs: #""" - struct Structy where T: Foo {} + struct Structy where T: Foo, T: Sendable {} """# ) try _test( From 63d6f078975f14a1af6be351fae316071a5bd6ef Mon Sep 17 00:00:00 2001 From: George Barnett Date: Mon, 13 Jan 2025 13:54:33 +0000 Subject: [PATCH 4/4] format --- .../Internal/Renderer/TextBasedRendererTests.swift | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Tests/GRPCCodeGenTests/Internal/Renderer/TextBasedRendererTests.swift b/Tests/GRPCCodeGenTests/Internal/Renderer/TextBasedRendererTests.swift index c1fd0b4ba..036eb41e9 100644 --- a/Tests/GRPCCodeGenTests/Internal/Renderer/TextBasedRendererTests.swift +++ b/Tests/GRPCCodeGenTests/Internal/Renderer/TextBasedRendererTests.swift @@ -853,11 +853,12 @@ final class Test_TextBasedRenderer: XCTestCase { whereClause: WhereClause( requirements: [ .conformance("T", "Foo"), - .conformance("T", "Sendable") + .conformance("T", "Sendable"), ] ) ), - renderedBy: { $0.renderStruct(_:) + renderedBy: { + $0.renderStruct(_:) }, rendersAs: #""" struct Structy where T: Foo, T: Sendable {}