Repository: smithy-lang/smithy
Pull request #2273 (2024-05-03 17:05:36)

Add maps and lists to directed codegen
This adds lists and maps to directed codegen. This is needed because when generating Python code there needs to be some data generated for lists and maps that must be placed in topological ordering as much as is possible.

While technically possible to generate that information without it being in topological ordering, the workarounds are *extremely* unfortunate.

---
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.



Patch:
diff --git a/smithy-codegen-core/src/main/java/software/amazon/smithy/codegen/core/directed/CodegenDirector.java b/smithy-codegen-core/src/main/java/software/amazon/smithy/codegen/core/directed/CodegenDirector.java
index 81e024138..3b3647913 100644
--- a/smithy-codegen-core/src/main/java/software/amazon/smithy/codegen/core/directed/CodegenDirector.java
+++ b/smithy-codegen-core/src/main/java/software/amazon/smithy/codegen/core/directed/CodegenDirector.java
@@ -39,6 +39,8 @@ import software.amazon.smithy.model.node.NodeMapper;
 import software.amazon.smithy.model.node.ObjectNode;
 import software.amazon.smithy.model.shapes.EnumShape;
 import software.amazon.smithy.model.shapes.IntEnumShape;
+import software.amazon.smithy.model.shapes.ListShape;
+import software.amazon.smithy.model.shapes.MapShape;
 import software.amazon.smithy.model.shapes.OperationShape;
 import software.amazon.smithy.model.shapes.ResourceShape;
 import software.amazon.smithy.model.shapes.ServiceShape;
@@ -556,6 +558,20 @@ public final class CodegenDirector<
             return null;
         }
 
+        @Override
+        public Void listShape(ListShape shape) {
+            LOGGER.finest(() -> "Generating list " + shape.getId());
+            directedCodegen.generateList(new GenerateListDirective<>(context, serviceShape, shape));
+            return null;
+        }
+
+        @Override
+        public Void mapShape(MapShape shape) {
+            LOGGER.finest(() -> "Generating map " + shape.getId());
+            directedCodegen.generateMap(new GenerateMapDirective<>(context, serviceShape, shape));
+            return null;
+        }
+
         @Override
         public Void stringShape(StringShape shape) {
             if (shape.hasTrait(EnumTrait.class)) {
diff --git a/smithy-codegen-core/src/main/java/software/amazon/smithy/codegen/core/directed/DirectedCodegen.java b/smithy-codegen-core/src/main/java/software/amazon/smithy/codegen/core/directed/DirectedCodegen.java
index e321473e7..469c81bfa 100644
--- a/smithy-codegen-core/src/main/java/software/amazon/smithy/codegen/core/directed/DirectedCodegen.java
+++ b/smithy-codegen-core/src/main/java/software/amazon/smithy/codegen/core/directed/DirectedCodegen.java
@@ -99,6 +99,20 @@ public interface DirectedCodegen<C extends CodegenContext<S, ?, I>, S, I extends
      */
     void generateUnion(GenerateUnionDirective<C, S> directive);
 
+    /**
+     * Generates any code needed for a list shape.
+     *
+     * @param directive Directive to perform.
+     */
+    default void generateList(GenerateListDirective<C, S> directive) {}
+
+    /**
+     * Generates any code needed for a map shape.
+     *
+     * @param directive Directive to perform.
+     */
+    default void generateMap(GenerateMapDirective<C, S> directive) {}
+
     /**
      * Generates the code needed for an enum shape, whether it's a string shape
      * marked with the enum trait, or a proper enum shape introduced in Smithy
diff --git a/smithy-codegen-core/src/main/java/software/amazon/smithy/codegen/core/directed/GenerateListDirective.java b/smithy-codegen-core/src/main/java/software/amazon/smithy/codegen/core/directed/GenerateListDirective.java
new file mode 100644
index 000000000..e3bbc76b4
--- /dev/null
+++ b/smithy-codegen-core/src/main/java/software/amazon/smithy/codegen/core/directed/GenerateListDirective.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package software.amazon.smithy.codegen.core.directed;
+
+import software.amazon.smithy.codegen.core.CodegenContext;
+import software.amazon.smithy.model.shapes.ListShape;
+import software.amazon.smithy.model.shapes.ServiceShape;
+
+/**
+ * Directive used to generate a list.
+ *
+ * @param <C> CodegenContext type.
+ * @param <S> Codegen settings type.
+ * @see DirectedCodegen#generateList
+ */
+public class GenerateListDirective<C extends CodegenContext<S, ?, ?>, S>
+        extends ShapeDirective<ListShape, C, S> {
+    GenerateListDirective(C context, ServiceShape service, ListShape shape) {
+        super(context, service, shape);
+    }
+}
diff --git a/smithy-codegen-core/src/main/java/software/amazon/smithy/codegen/core/directed/GenerateMapDirective.java b/smithy-codegen-core/src/main/java/software/amazon/smithy/codegen/core/directed/GenerateMapDirective.java
new file mode 100644
index 000000000..1c2f16abe
--- /dev/null
+++ b/smithy-codegen-core/src/main/java/software/amazon/smithy/codegen/core/directed/GenerateMapDirective.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package software.amazon.smithy.codegen.core.directed;
+
+import software.amazon.smithy.codegen.core.CodegenContext;
+import software.amazon.smithy.model.shapes.MapShape;
+import software.amazon.smithy.model.shapes.ServiceShape;
+
+/**
+ * Directive used to generate a map.
+ *
+ * @param <C> CodegenContext type.
+ * @param <S> Codegen settings type.
+ * @see DirectedCodegen#generateMap
+ */
+public class GenerateMapDirective<C extends CodegenContext<S, ?, ?>, S>
+        extends ShapeDirective<MapShape, C, S> {
+    GenerateMapDirective(C context, ServiceShape service, MapShape shape) {
+        super(context, service, shape);
+    }
+}


Test patch:
diff --git a/smithy-codegen-core/src/test/java/software/amazon/smithy/codegen/core/directed/CodegenDirectorTest.java b/smithy-codegen-core/src/test/java/software/amazon/smithy/codegen/core/directed/CodegenDirectorTest.java
index df320005b..f4a8bbc12 100644
--- a/smithy-codegen-core/src/test/java/software/amazon/smithy/codegen/core/directed/CodegenDirectorTest.java
+++ b/smithy-codegen-core/src/test/java/software/amazon/smithy/codegen/core/directed/CodegenDirectorTest.java
@@ -99,6 +99,16 @@ public class CodegenDirectorTest {
             generatedShapes.add(directive.shape().getId());
         }
 
+        @Override
+        public void generateList(GenerateListDirective<TestContext, TestSettings> directive) {
+            generatedShapes.add(directive.shape().getId());
+        }
+
+        @Override
+        public void generateMap(GenerateMapDirective<TestContext, TestSettings> directive) {
+            generatedShapes.add(directive.shape().getId());
+        }
+
         @Override
         public void generateEnumShape(GenerateEnumDirective<TestContext, TestSettings> directive) {
             generatedShapes.add(directive.shape().getId());
@@ -187,6 +197,9 @@ public class CodegenDirectorTest {
                 ShapeId.from("smithy.example#TheFoo"),
                 ShapeId.from("smithy.example#ListFooInput"),
                 ShapeId.from("smithy.example#ListFooOutput"),
+                ShapeId.from("smithy.example#FooStructure"),
+                ShapeId.from("smithy.example#FooList"),
+                ShapeId.from("smithy.example#StringMap"),
                 ShapeId.from("smithy.example#Status"),
                 ShapeId.from("smithy.example#FaceCard"),
                 ShapeId.from("smithy.example#Instruction"),
@@ -229,6 +242,9 @@ public class CodegenDirectorTest {
                 ShapeId.from("smithy.example#TheFoo"),
                 ShapeId.from("smithy.example#ListFooInput"),
                 ShapeId.from("smithy.example#ListFooOutput"),
+                ShapeId.from("smithy.example#FooStructure"),
+                ShapeId.from("smithy.example#FooList"),
+                ShapeId.from("smithy.example#StringMap"),
                 ShapeId.from("smithy.example#Status"),
                 ShapeId.from("smithy.example#FaceCard"),
                 ShapeId.from("smithy.example#Instruction"),
diff --git a/smithy-codegen-core/src/test/resources/software/amazon/smithy/codegen/core/directed/directed-model.smithy b/smithy-codegen-core/src/test/resources/software/amazon/smithy/codegen/core/directed/directed-model.smithy
index 48d6c51fc..686af4988 100644
--- a/smithy-codegen-core/src/test/resources/software/amazon/smithy/codegen/core/directed/directed-model.smithy
+++ b/smithy-codegen-core/src/test/resources/software/amazon/smithy/codegen/core/directed/directed-model.smithy
@@ -21,14 +21,24 @@ operation ListFoo {
     }
     output:= with [Paginated] {
         status: Status
-        items: StringList
+        items: FooList
         instruction: Instruction
         facecard: FaceCard
     }
 }
 
-list StringList {
-    member: String
+structure FooStructure {
+    id: String
+    tags: StringMap
+}
+
+map StringMap {
+    key: String
+    value: String
+}
+
+list FooList {
+    member: FooStructure
 }
 
 @enum([