diff --git a/codegen/smithy-go-codegen-test/model/main.smithy b/codegen/smithy-go-codegen-test/model/main.smithy index 88b211ea7..d41e76f8b 100644 --- a/codegen/smithy-go-codegen-test/model/main.smithy +++ b/codegen/smithy-go-codegen-test/model/main.smithy @@ -310,6 +310,11 @@ structure ListCitiesInput { pageSize: Integer } +intEnum SimpleOneZero { + ONE = 1 + ZERO = 0 +} + @mixin structure ListCitiesMixin { someEnum: SimpleYesNo, @@ -318,6 +323,7 @@ structure ListCitiesMixin { boxedBool: Boolean, defaultNumber: DefaultInteger, boxedNumber: Integer, + someIntegerEnum: SimpleOneZero } structure ListCitiesOutput with [ListCitiesMixin] { diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/CodegenVisitor.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/CodegenVisitor.java index f58861db5..d0316db10 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/CodegenVisitor.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/CodegenVisitor.java @@ -39,6 +39,7 @@ import software.amazon.smithy.model.knowledge.ServiceIndex; import software.amazon.smithy.model.knowledge.TopDownIndex; import software.amazon.smithy.model.neighbor.Walker; +import software.amazon.smithy.model.shapes.IntEnumShape; import software.amazon.smithy.model.shapes.OperationShape; import software.amazon.smithy.model.shapes.ServiceShape; import software.amazon.smithy.model.shapes.Shape; @@ -327,4 +328,10 @@ public Void serviceShape(ServiceShape shape) { }); return null; } + + @Override + public Void intEnumShape(IntEnumShape shape) { + writers.useShapeWriter(shape, writer -> new IntEnumGenerator(symbolProvider, writer, shape).run()); + return null; + } } diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/IntEnumGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/IntEnumGenerator.java new file mode 100644 index 000000000..7b101c492 --- /dev/null +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/IntEnumGenerator.java @@ -0,0 +1,98 @@ +/* + * Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package software.amazon.smithy.go.codegen; + +import java.util.LinkedHashSet; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.logging.Logger; +import software.amazon.smithy.codegen.core.Symbol; +import software.amazon.smithy.codegen.core.SymbolProvider; +import software.amazon.smithy.model.shapes.IntEnumShape; +import software.amazon.smithy.model.shapes.MemberShape; +import software.amazon.smithy.model.traits.DocumentationTrait; +import software.amazon.smithy.model.traits.EnumValueTrait; +import software.amazon.smithy.utils.StringUtils; + +/** + * Renders intEnums and their constants. + */ +final class IntEnumGenerator implements Runnable { + private static final Logger LOGGER = Logger.getLogger(IntEnumGenerator.class.getName()); + + private final SymbolProvider symbolProvider; + private final GoWriter writer; + private final IntEnumShape shape; + + IntEnumGenerator(SymbolProvider symbolProvider, GoWriter writer, IntEnumShape shape) { + this.symbolProvider = symbolProvider; + this.writer = writer; + this.shape = shape; + } + + @Override + public void run() { + Symbol symbol = symbolProvider.toSymbol(shape); + + writer.write("type $L int32", symbol.getName()).write(""); + + writer.writeDocs(String.format("Enum values for %s", symbol.getName())); + Set constants = new LinkedHashSet<>(); + writer.openBlock("const (", ")", () -> { + for (Map.Entry entry : shape.getAllMembers().entrySet()) { + StringBuilder labelBuilder = new StringBuilder(symbol.getName()); + String name = entry.getKey(); + + for (String part : name.split("(?U)[\\W_]")) { + if (part.matches(".*[a-z].*") && part.matches(".*[A-Z].*")) { + // Mixed case names should not be changed other than first letter capitalized. + labelBuilder.append(StringUtils.capitalize(part)); + } else { + // For all non-mixed case parts title case first letter, followed by all other lower cased. + labelBuilder.append(StringUtils.capitalize(part.toLowerCase(Locale.US))); + } + } + String label = labelBuilder.toString(); + + // If camel-casing would cause a conflict, don't camel-case this enum value. + if (constants.contains(label)) { + LOGGER.warning(String.format( + "Multiple enums resolved to the same name, `%s`, using unaltered value for: %s", + label, name)); + label = name; + } + constants.add(label); + + entry.getValue().getTrait(DocumentationTrait.class) + .ifPresent(trait -> writer.writeDocs(trait.getValue())); + writer.write("$L $L = $L", label, symbol.getName(), + entry.getValue().expectTrait(EnumValueTrait.class).expectIntValue()); + } + }).write(""); + + writer.writeDocs(String.format("Values returns all known values for %s. Note that this can be expanded in the " + + "future, and so it is only as up to date as the client.%n%nThe ordering of this slice is not " + + "guaranteed to be stable across updates.", symbol.getName())); + writer.openBlock("func ($L) Values() []$L {", "}", symbol.getName(), symbol.getName(), () -> { + writer.openBlock("return []$L{", "}", symbol.getName(), () -> { + for (Map.Entry entry : shape.getEnumValues().entrySet()) { + writer.write("$L,", entry.getValue()); + } + }); + }); + } +} diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SymbolVisitor.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SymbolVisitor.java index 50a68c183..04d47bc14 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SymbolVisitor.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SymbolVisitor.java @@ -41,6 +41,7 @@ import software.amazon.smithy.model.shapes.DocumentShape; import software.amazon.smithy.model.shapes.DoubleShape; import software.amazon.smithy.model.shapes.FloatShape; +import software.amazon.smithy.model.shapes.IntEnumShape; import software.amazon.smithy.model.shapes.IntegerShape; import software.amazon.smithy.model.shapes.ListShape; import software.amazon.smithy.model.shapes.LongShape; @@ -504,4 +505,12 @@ public Symbol memberShape(MemberShape member) { public Symbol timestampShape(TimestampShape shape) { return symbolBuilderFor(shape, "Time", SmithyGoDependency.TIME).build(); } + + @Override + public Symbol intEnumShape(IntEnumShape shape) { + String name = getDefaultShapeName(shape); + return symbolBuilderFor(shape, name, typesPackageName) + .definitionFile("./types/enums.go") + .build(); + } } diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/knowledge/GoPointableIndex.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/knowledge/GoPointableIndex.java index 5c11e41e2..895188878 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/knowledge/GoPointableIndex.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/knowledge/GoPointableIndex.java @@ -192,7 +192,8 @@ private boolean isShapePointable(Shape shape) { private boolean isShapeEnum(Shape shape) { return shape.getType() == ShapeType.STRING && shape.hasTrait(EnumTrait.class) - || shape.getType() == ShapeType.ENUM; + || shape.getType() == ShapeType.ENUM + || shape.getType() == ShapeType.INT_ENUM; } private boolean isBlobStream(Shape shape) { diff --git a/codegen/smithy-go-codegen/src/main/resources/software/amazon/smithy/go/codegen/smithy-tests/int-enum-shape-test/expected/changeCardInput.go.struct b/codegen/smithy-go-codegen/src/main/resources/software/amazon/smithy/go/codegen/smithy-tests/int-enum-shape-test/expected/changeCardInput.go.struct new file mode 100644 index 000000000..7c2b51eab --- /dev/null +++ b/codegen/smithy-go-codegen/src/main/resources/software/amazon/smithy/go/codegen/smithy-tests/int-enum-shape-test/expected/changeCardInput.go.struct @@ -0,0 +1,8 @@ +type ChangeCardInput struct { + + Number types.Number + + Suit types.Suit + + noSmithyDocumentSerde +} diff --git a/codegen/smithy-go-codegen/src/main/resources/software/amazon/smithy/go/codegen/smithy-tests/int-enum-shape-test/expected/changeCardOutput.go.struct b/codegen/smithy-go-codegen/src/main/resources/software/amazon/smithy/go/codegen/smithy-tests/int-enum-shape-test/expected/changeCardOutput.go.struct new file mode 100644 index 000000000..de2f82bc1 --- /dev/null +++ b/codegen/smithy-go-codegen/src/main/resources/software/amazon/smithy/go/codegen/smithy-tests/int-enum-shape-test/expected/changeCardOutput.go.struct @@ -0,0 +1,11 @@ +type ChangeCardOutput struct { + + Number types.Number + + Suit types.Suit + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} diff --git a/codegen/smithy-go-codegen/src/main/resources/software/amazon/smithy/go/codegen/smithy-tests/int-enum-shape-test/expected/types/enums.go b/codegen/smithy-go-codegen/src/main/resources/software/amazon/smithy/go/codegen/smithy-tests/int-enum-shape-test/expected/types/enums.go new file mode 100644 index 000000000..038302c35 --- /dev/null +++ b/codegen/smithy-go-codegen/src/main/resources/software/amazon/smithy/go/codegen/smithy-tests/int-enum-shape-test/expected/types/enums.go @@ -0,0 +1,66 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + + +package types + +type Number int32 + +// Enum values for Number +const ( + NumberAce Number = 1 + NumberTwo Number = 2 + NumberThree Number = 3 + NumberFour Number = 4 + NumberFive Number = 5 + NumberSix Number = 6 + NumberSeven Number = 7 + NumberEight Number = 8 + NumberNine Number = 9 + NumberTen Number = 10 + NumberJack Number = 11 + NumberQueen Number = 12 + NumberKing Number = 13 +) + +// Values returns all known values for Number. Note that this can be expanded in +// the future, and so it is only as up to date as the client. The ordering of this +// slice is not guaranteed to be stable across updates. +func (Number) Values() []Number { + return []Number{ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + } +} + +type Suit string + +// Enum values for Suit +const ( + SuitDiamond Suit = "DIAMOND" + SuitClub Suit = "CLUB" + SuitHeart Suit = "HEART" + SuitSpade Suit = "SPADE" +) + +// Values returns all known values for Suit. Note that this can be expanded in the +// future, and so it is only as up to date as the client. The ordering of this +// slice is not guaranteed to be stable across updates. +func (Suit) Values() []Suit { + return []Suit{ + "DIAMOND", + "CLUB", + "HEART", + "SPADE", + } +} diff --git a/codegen/smithy-go-codegen/src/main/resources/software/amazon/smithy/go/codegen/smithy-tests/int-enum-shape-test/int-enum-shape-test.smithy b/codegen/smithy-go-codegen/src/main/resources/software/amazon/smithy/go/codegen/smithy-tests/int-enum-shape-test/int-enum-shape-test.smithy new file mode 100644 index 000000000..591923205 --- /dev/null +++ b/codegen/smithy-go-codegen/src/main/resources/software/amazon/smithy/go/codegen/smithy-tests/int-enum-shape-test/int-enum-shape-test.smithy @@ -0,0 +1,43 @@ +$version: "2.0" + +namespace smithy.example + +service Example { + version: "1.0.0" + operations: [ + ChangeCard + ] +} + +operation ChangeCard { + input: Card + output: Card +} + +structure Card { + suit: Suit + number: Number +} + +enum Suit { + DIAMOND + CLUB + HEART + SPADE +} + +intEnum Number { + ACE = 1 + TWO = 2 + THREE = 3 + FOUR = 4 + FIVE = 5 + SIX = 6 + SEVEN = 7 + EIGHT = 8 + NINE = 9 + TEN = 10 + JACK = 11 + QUEEN = 12 + KING = 13 +} diff --git a/codegen/smithy-go-codegen/src/test/java/software/amazon/smithy/go/codegen/IntEnumShapeGeneratorTest.java b/codegen/smithy-go-codegen/src/test/java/software/amazon/smithy/go/codegen/IntEnumShapeGeneratorTest.java new file mode 100644 index 000000000..89e79e4d6 --- /dev/null +++ b/codegen/smithy-go-codegen/src/test/java/software/amazon/smithy/go/codegen/IntEnumShapeGeneratorTest.java @@ -0,0 +1,75 @@ +/* + * Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package software.amazon.smithy.go.codegen; + +import java.util.logging.Logger; +import org.junit.jupiter.api.Test; + +import software.amazon.smithy.build.MockManifest; +import software.amazon.smithy.build.PluginContext; +import software.amazon.smithy.go.codegen.GoCodegenPlugin; +import software.amazon.smithy.model.Model; + +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +import static software.amazon.smithy.go.codegen.TestUtils.buildMockPluginContext; +import static software.amazon.smithy.go.codegen.TestUtils.loadSmithyModelFromResource; +import static software.amazon.smithy.go.codegen.TestUtils.loadExpectedFileStringFromResource; + + +public class IntEnumShapeGeneratorTest { + private static final Logger LOGGER = Logger.getLogger(IntEnumShapeGeneratorTest.class.getName()); + + @Test + public void testIntEnumShapeTest() { + + // Arrange + Model model = + loadSmithyModelFromResource("int-enum-shape-test"); + MockManifest manifest = + new MockManifest(); + PluginContext context = + buildMockPluginContext(model, manifest, "smithy.example#Example"); + + // Act + (new GoCodegenPlugin()).execute(context); + + // Assert + String actualEnumShapeCode = + manifest.getFileString("types/enums.go").get(); + String expectedEnumShapeCode = + loadExpectedFileStringFromResource("int-enum-shape-test", "types/enums.go"); + assertThat("intEnum shape actual generated code is equal to the expected generated code", + actualEnumShapeCode, + is(expectedEnumShapeCode)); + String actualChangeCardOperationCode = + manifest.getFileString("api_op_ChangeCard.go").get(); + String expectedChangeCardInputCode = + loadExpectedFileStringFromResource("int-enum-shape-test", "changeCardInput.go.struct"); + assertThat("intEnum shape properly referenced in generated input structure code", + actualChangeCardOperationCode, + containsString(expectedChangeCardInputCode)); + String expectedChangeCardOutputCode = + loadExpectedFileStringFromResource("int-enum-shape-test", "changeCardOutput.go.struct"); + assertThat("intEnum shape properly referenced in generated output structure code", + actualChangeCardOperationCode, + containsString(expectedChangeCardOutputCode)); + + } + +}