Skip to content

Commit

Permalink
Add support for intEnum code generation (#383)
Browse files Browse the repository at this point in the history
- Update smithy-go-codegen-test with intEnum test
- Add unit test for intEnum shape
  • Loading branch information
Steven Yuan authored Sep 2, 2022
1 parent fef294e commit 9161575
Show file tree
Hide file tree
Showing 10 changed files with 325 additions and 1 deletion.
6 changes: 6 additions & 0 deletions codegen/smithy-go-codegen-test/model/main.smithy
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,11 @@ structure ListCitiesInput {
pageSize: Integer
}

intEnum SimpleOneZero {
ONE = 1
ZERO = 0
}

@mixin
structure ListCitiesMixin {
someEnum: SimpleYesNo,
Expand All @@ -318,6 +323,7 @@ structure ListCitiesMixin {
boxedBool: Boolean,
defaultNumber: DefaultInteger,
boxedNumber: Integer,
someIntegerEnum: SimpleOneZero
}

structure ListCitiesOutput with [ListCitiesMixin] {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -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<String> constants = new LinkedHashSet<>();
writer.openBlock("const (", ")", () -> {
for (Map.Entry<String, MemberShape> 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<String, Integer> entry : shape.getEnumValues().entrySet()) {
writer.write("$L,", entry.getValue());
}
});
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
type ChangeCardInput struct {

Number types.Number

Suit types.Suit

noSmithyDocumentSerde
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
type ChangeCardOutput struct {

Number types.Number

Suit types.Suit

// Metadata pertaining to the operation's result.
ResultMetadata middleware.Metadata

noSmithyDocumentSerde
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -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
}
Original file line number Diff line number Diff line change
@@ -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));

}

}

0 comments on commit 9161575

Please sign in to comment.