Skip to content

Commit 18c0a0e

Browse files
committed
Use sealed interfaces for enums
1 parent 3daa684 commit 18c0a0e

15 files changed

Lines changed: 519 additions & 283 deletions

File tree

codegen/codegen-core/src/it/java/software/amazon/smithy/java/codegen/test/ClientErrorCorrectionTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,9 @@ void correctsErrors() {
4444
assertEquals(corrected.getList(), List.of());
4545
assertEquals(corrected.getMap(), Map.of());
4646
assertEquals(corrected.getTimestamp(), Instant.EPOCH);
47-
assertEquals(corrected.getEnum().getType(), NestedEnum.Type.$UNKNOWN);
47+
assertEquals(NestedEnum.$Unknown.class, corrected.getEnum().getClass());
4848
assertEquals(corrected.getEnum().getValue(), "");
49-
assertEquals(corrected.getIntEnum().getType(), NestedIntEnum.Type.$UNKNOWN);
49+
assertEquals(NestedIntEnum.$Unknown.class, corrected.getIntEnum().getClass());
5050
assertEquals(corrected.getIntEnum().getValue(), 0);
5151
}
5252
}

codegen/codegen-core/src/it/java/software/amazon/smithy/java/codegen/test/EnumTest.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
package software.amazon.smithy.java.codegen.test;
77

88
import static org.junit.jupiter.api.Assertions.assertEquals;
9+
import static org.junit.jupiter.api.Assertions.fail;
910

1011
import org.junit.jupiter.api.Test;
1112
import software.amazon.smithy.java.codegen.test.model.EnumType;
@@ -19,6 +20,9 @@ void unknownTypeDeserializedIntoUnknownVariant() {
1920
try (var codec = JsonCodec.builder().useJsonName(true).useTimestampFormat(true).build()) {
2021
output = codec.deserializeShape("\"option-n\"", EnumType.builder());
2122
}
22-
assertEquals(output.getType(), EnumType.Type.$UNKNOWN);
23+
switch (output) {
24+
case EnumType.$Unknown(String value) -> assertEquals("option-n", value);
25+
default -> fail("Expected UnknownEnumType");
26+
}
2327
}
2428
}

codegen/codegen-core/src/main/java/software/amazon/smithy/java/codegen/CodegenUtils.java

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -331,19 +331,6 @@ public static String toUpperSnakeCase(String string) {
331331
return CaseUtils.toSnakeCase(string).toUpperCase(Locale.ENGLISH);
332332
}
333333

334-
/**
335-
* Gets a symbol representing a nested {@code Type} enum for the given root symbol.
336-
*
337-
* @return Symbol representing a nested {@code Type} enum
338-
*/
339-
public static Symbol getInnerTypeEnumSymbol(Symbol symbol) {
340-
return symbol.toBuilder()
341-
.namespace(symbol.getFullName(), ".")
342-
.putProperty(SymbolProperties.IS_LOCALLY_DEFINED, true)
343-
.name("Type")
344-
.build();
345-
}
346-
347334
/**
348335
* Determines if a shape is recursive and should use a schema builder when defined as a Root- or Member-Schema.
349336
*

codegen/codegen-core/src/main/java/software/amazon/smithy/java/codegen/generators/EnumGenerator.java

Lines changed: 111 additions & 148 deletions
Large diffs are not rendered by default.

codegen/codegen-core/src/main/java/software/amazon/smithy/java/codegen/generators/SchemaFieldGenerator.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -135,8 +135,8 @@ public Void enumShape(EnumShape shape) {
135135
writer.putContext("variants", shape.members().stream().map(symbolProvider::toMemberName).toList());
136136
writer.putContext("set", Set.class);
137137
writer.write("""
138-
public static final ${schemaClass:T} ${name:L} = ${schemaClass:T}.createEnum(${shapeId:T}.from(${id:S}),
139-
${set:T}.of(${#variants}${value:L}.value${^key.last}, ${/key.last}${/variants})${traits:C}
138+
${schemaClass:T} ${name:L} = ${schemaClass:T}.createEnum(${shapeId:T}.from(${id:S}),
139+
${set:T}.of(${#variants}${value:L}.getValue()${^key.last}, ${/key.last}${/variants})${traits:C}
140140
);
141141
""");
142142
writer.popState();
@@ -151,8 +151,8 @@ public Void intEnumShape(IntEnumShape shape) {
151151
writer.putContext("set", Set.class);
152152
writer.write(
153153
"""
154-
public static final ${schemaClass:T} ${name:L} = ${schemaClass:T}.createIntEnum(${shapeId:T}.from(${id:S}),
155-
${set:T}.of(${#variants}${value:L}.value${^key.last}, ${/key.last}${/variants})${traits:C}
154+
${schemaClass:T} ${name:L} = ${schemaClass:T}.createIntEnum(${shapeId:T}.from(${id:S}),
155+
${set:T}.of(${#variants}${value:L}.getValue()${^key.last}, ${/key.last}${/variants})${traits:C}
156156
);
157157
""");
158158
writer.popState();

codegen/codegen-core/src/main/java/software/amazon/smithy/java/codegen/generators/TypeEnumGenerator.java

Lines changed: 0 additions & 37 deletions
This file was deleted.

codegen/codegen-core/src/test/java/software/amazon/smithy/java/codegen/NamingTest.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import org.junit.jupiter.params.provider.Arguments;
1717
import org.junit.jupiter.params.provider.MethodSource;
1818
import software.amazon.smithy.java.codegen.utils.AbstractCodegenFileTest;
19+
import software.amazon.smithy.utils.CaseUtils;
1920

2021
public class NamingTest extends AbstractCodegenFileTest {
2122
private static final URL TEST_FILE = Objects.requireNonNull(
@@ -144,7 +145,10 @@ static List<Arguments> enumCaseArgs() {
144145
@MethodSource("enumCaseArgs")
145146
void enumCasing(String updated, String original) {
146147
var fileStr = getFileStringForClass("EnumCasing");
147-
// All variants should maintain the raw value for the value, but convert the type to expected string
148-
assertTrue(fileStr.contains(String.format("new EnumCasing(Type.%s, \"%s\")", updated, original)));
148+
// Enum field stays as UPPER_SNAKE_CASE (like CAMEL_CASE), but inner class uses PascalCase + Type suffix (like CamelCaseType)
149+
// Sealed interface pattern: EnumCasing CAMEL_CASE = new CamelCaseType(); with getValue() returning "camelCase"
150+
var className = CaseUtils.toPascalCase(updated) + "Type";
151+
assertTrue(fileStr.contains(String.format("EnumCasing %s = new %s();", updated, className)));
152+
assertTrue(fileStr.contains(String.format("return \"%s\";", original)));
149153
}
150154
}

codegen/codegen-core/src/test/java/software/amazon/smithy/java/codegen/NonNullAnnotationTest.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -75,14 +75,12 @@ void nonNullAnnotationAddedForUnionVariant() {
7575
@Test
7676
void nonNullAnnotationAddedForEnumVariant() {
7777
var fileStr = getFileStringForClass("TestEnum");
78-
var expectedValueGetter = "public @TestNonNullAnnotation String getValue() {";
79-
var expectedValueField = "private final @TestNonNullAnnotation String value;";
78+
var expectedValueGetter = "@TestNonNullAnnotation String getValue();";
8079
var expectedToString = "public @TestNonNullAnnotation String toString() {";
81-
var expectedTypeGetter = "public @TestNonNullAnnotation Type getType() {";
80+
var expectedUnknownValueField = "record $Unknown(@TestNonNullAnnotation String value)";
8281

8382
assertTrue(fileStr.contains(expectedValueGetter));
84-
assertTrue(fileStr.contains(expectedValueField));
8583
assertTrue(fileStr.contains(expectedToString));
86-
assertTrue(fileStr.contains(expectedTypeGetter));
84+
assertTrue(fileStr.contains(expectedUnknownValueField));
8785
}
8886
}

codegen/codegen-core/src/test/java/software/amazon/smithy/java/codegen/integrations/javadoc/JavadocIntegrationTest.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@ public String getRollupMember() {
192192
@Test
193193
void addsToEnumVariants() {
194194
var fileContents = getFileStringForClass("EnumWithDocs");
195+
// Sealed interface pattern - static fields (UPPER_SNAKE_CASE) instantiate inner classes (PascalCase + Type suffix)
195196
assertThat(
196197
fileContents,
197198
containsString(
@@ -200,12 +201,12 @@ void addsToEnumVariants() {
200201
* @deprecated As of the past.
201202
*/
202203
@Deprecated(since = "the past")
203-
public static final EnumWithDocs DOCUMENTED = new EnumWithDocs(Type.DOCUMENTED, "DOCUMENTED");
204+
EnumWithDocs DOCUMENTED = new DocumentedType();
204205
/**
205206
* General Docs
206207
*/
207208
@SmithyUnstableApi
208-
public static final EnumWithDocs ALSO_DOCUMENTED = new EnumWithDocs(Type.ALSO_DOCUMENTED, "ALSO_DOCUMENTED");
209+
EnumWithDocs ALSO_DOCUMENTED = new AlsoDocumentedType();
209210
"""));
210211
}
211212

codegen/plugins/types-codegen/src/test/java/software/amazon/smithy/java/codegen/types/TypesCodegenPluginTest.java

Lines changed: 4 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,10 @@
55

66
package software.amazon.smithy.java.codegen.types;
77

8-
import static org.junit.jupiter.api.Assertions.assertEquals;
9-
import static org.junit.jupiter.api.Assertions.assertNotNull;
10-
import static org.junit.jupiter.api.Assertions.assertTrue;
8+
import static org.assertj.core.api.Assertions.assertThat;
119
import static software.amazon.smithy.java.codegen.test.PluginTestRunner.addTestCasesFromUrl;
12-
import static software.amazon.smithy.java.codegen.test.PluginTestRunner.findGotContent;
1310

14-
import java.io.File;
15-
import java.io.IOException;
16-
import java.nio.charset.StandardCharsets;
17-
import java.nio.file.Files;
18-
import java.nio.file.Path;
1911
import java.util.Collection;
20-
import java.util.Set;
21-
import java.util.stream.Collectors;
2212
import org.junit.jupiter.params.ParameterizedTest;
2313
import org.junit.jupiter.params.provider.MethodSource;
2414
import software.amazon.smithy.java.codegen.test.PluginTestRunner.TestCase;
@@ -28,36 +18,12 @@ public class TypesCodegenPluginTest {
2818
@ParameterizedTest(name = "[{index}] => {0}")
2919
@MethodSource("testCases")
3020
public void runTestCase(TestCase test) {
31-
test.builder().build();
32-
var got = test.manifests().stream().flatMap(x -> x.getFiles().stream()).collect(Collectors.toSet());
33-
for (var expected : test.expectedToContents().keySet()) {
34-
var found = findExpected(expected, got);
35-
assertNotNull(found, "Expected to find " + expected + " in the manifest");
36-
var contents = findGotContent(found, test);
37-
assertTrue(contents.isPresent());
38-
assertEquals(test.expectedToContents().get(expected), contents.get());
21+
for (var entry : test.expectedContents().entrySet()) {
22+
var actual = test.getActualContent(entry.getKey());
23+
assertThat(actual).isEqualTo(entry.getValue());
3924
}
4025
}
4126

42-
// Uncomment this test to render the java files when we there are changes to the codegen logic.
43-
// @ParameterizedTest(name = "[{index}] => {0}") @MethodSource("testCases")
44-
public void renderExpected(TestCase test) throws IOException {
45-
test.builder().build();
46-
var got = test.manifests().stream().flatMap(x -> x.getFiles().stream()).collect(Collectors.toSet());
47-
for (var expected : test.expectedToContents().keySet()) {
48-
var found = findExpected(expected, got);
49-
assertNotNull(found);
50-
var contents = findGotContent(found, test);
51-
var expectedFile = new File("/tmp/rendered/" + test + "/expected" + expected);
52-
expectedFile.getParentFile().mkdirs();
53-
Files.write(expectedFile.toPath(), contents.get().getBytes(StandardCharsets.UTF_8));
54-
}
55-
}
56-
57-
private Path findExpected(String expected, Set<Path> manifestFiles) {
58-
return manifestFiles.stream().filter(path -> path.toString().contains(expected)).findFirst().orElse(null);
59-
}
60-
6127
public static Collection<TestCase> testCases() {
6228
return addTestCasesFromUrl(TypesCodegenPluginTest.class.getResource("test-cases"));
6329
}

0 commit comments

Comments
 (0)