Skip to content

Commit

Permalink
Add support and mechanism for enum codegen
Browse files Browse the repository at this point in the history
We will be added proper enum shapes in Smithy IDL 2.0, and we want code
generators to explicitly pay attention to doing this codegen step.
Adding a new required directive method later would be a breaking change
unless se give it a default implementation, but a default implementation
won't force implementers to notice enums need codegen.

The solution in this commit is to support both string shape and enum
shape codegen from the same directive. Generators need to ask the
directive what kind of shape is being generated to know how to perform
enum codegen. Generators that perform this transformation in advance can
just assume they're only given enum shapes during code generation.

Add Directive suffix to directive classes

This helps to make their relationship and intent more clear, in
particular classes like "Customize" that seemed overly ambiguous.
  • Loading branch information
mtdowling authored and Michael Dowling committed Apr 7, 2022
1 parent 6882b6e commit ced203e
Show file tree
Hide file tree
Showing 17 changed files with 223 additions and 73 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,10 @@
import software.amazon.smithy.model.shapes.ServiceShape;
import software.amazon.smithy.model.shapes.Shape;
import software.amazon.smithy.model.shapes.ShapeId;
import software.amazon.smithy.model.shapes.StringShape;
import software.amazon.smithy.model.shapes.StructureShape;
import software.amazon.smithy.model.shapes.UnionShape;
import software.amazon.smithy.model.traits.EnumTrait;
import software.amazon.smithy.model.traits.ErrorTrait;
import software.amazon.smithy.model.transform.ModelTransformer;
import software.amazon.smithy.utils.CodeInterceptor;
Expand All @@ -53,7 +55,7 @@
* @param <C> Type of {@link CodegenContext} to create and use.
* @param <S> Type of settings object to pass to directed methods.
*/
public final class DirectedCodegenRunner<
public final class CodegenDirector<
W extends SymbolWriter<W, ? extends ImportContainer>,
I extends SmithyIntegration<S, W, C>,
C extends CodegenContext<S, W>,
Expand All @@ -80,7 +82,7 @@ public final class DirectedCodegenRunner<
* </ul>
*
* <p><em>Note</em>: This transform is applied automatically by a code
* generator if {@link DirectedCodegenRunner#performDefaultCodegenTransforms()} is
* generator if {@link CodegenDirector#performDefaultCodegenTransforms()} is
* set to true.
*
* @param model Model being code generated.
Expand Down Expand Up @@ -192,7 +194,7 @@ public void integrationClassLoader(ClassLoader classLoader) {
}

/**
* Set to true to apply {@link DirectedCodegenRunner#simplifyModelForServiceCodegen}
* Set to true to apply {@link CodegenDirector#simplifyModelForServiceCodegen}
* prior to code generation.
*/
public void performDefaultCodegenTransforms() {
Expand Down Expand Up @@ -268,11 +270,11 @@ public void run() {
registerInterceptors(context, integrations);

LOGGER.finest(() -> "Generating service " + serviceShape.getId());
directedCodegen.generateService(new GenerateService<>(context, serviceShape));
directedCodegen.generateService(new GenerateServiceDirective<>(context, serviceShape));

generateShapesInService(context, serviceShape, shapes);

Customize<C, S> postProcess = new Customize<>(context, serviceShape);
CustomizeDirective<C, S> postProcess = new CustomizeDirective<>(context, serviceShape);

LOGGER.finest(() -> "Performing custom codegen for "
+ directedCodegen.getClass().getName() + " before integrations");
Expand Down Expand Up @@ -304,7 +306,7 @@ private void validateState() {
if (integrationFinder == null) {
LOGGER.fine(() -> String.format("Finding %s integrations using the %s class loader",
integrationClass.getName(),
DirectedCodegenRunner.class.getCanonicalName()));
CodegenDirector.class.getCanonicalName()));
integrationClassLoader(getClass().getClassLoader());
}
}
Expand Down Expand Up @@ -335,7 +337,7 @@ private void preprocessModelWithIntegrations(List<I> integrations) {
private SymbolProvider createSymbolProvider(List<I> integrations, ServiceShape serviceShape) {
LOGGER.fine(() -> "Creating a symbol provider from " + settings.getClass().getName());
SymbolProvider provider = directedCodegen.createSymbolProvider(
new CreateSymbolProvider<>(model, settings, serviceShape));
new CreateSymbolProviderDirective<>(model, settings, serviceShape));

LOGGER.finer(() -> "Decorating symbol provider using " + integrationClass.getName());
for (I integration : integrations) {
Expand All @@ -347,7 +349,7 @@ private SymbolProvider createSymbolProvider(List<I> integrations, ServiceShape s

private C createContext(ServiceShape serviceShape, SymbolProvider provider) {
LOGGER.fine(() -> "Creating a codegen context for " + directedCodegen.getClass().getName());
return directedCodegen.createContext(new CreateContext<>(
return directedCodegen.createContext(new CreateContextDirective<>(
model, settings, serviceShape, provider, fileManifest));
}

Expand All @@ -374,7 +376,7 @@ private void generateResourceShapes(C context, ServiceShape serviceShape, Set<Sh
if (shapes.contains(shape)) {
LOGGER.finest(() -> "Generating resource " + shape.getId());
directedCodegen.generateResource(
new GenerateResource<>(context, serviceShape, shape));
new GenerateResourceDirective<>(context, serviceShape, shape));
}
}
}
Expand All @@ -384,10 +386,10 @@ private void generateStructures(C context, ServiceShape serviceShape, Set<Shape>
if (shapes.contains(shape)) {
if (shape.hasTrait(ErrorTrait.class)) {
LOGGER.finest(() -> "Generating error " + shape.getId());
directedCodegen.generateError(new GenerateError<>(context, serviceShape, shape));
directedCodegen.generateError(new GenerateErrorDirective<>(context, serviceShape, shape));
} else {
LOGGER.finest(() -> "Generating structure " + shape.getId());
directedCodegen.generateStructure(new GenerateStructure<>(context, serviceShape, shape));
directedCodegen.generateStructure(new GenerateStructureDirective<>(context, serviceShape, shape));
}
}
}
Expand All @@ -397,21 +399,28 @@ private void generateUnionShapes(C context, ServiceShape serviceShape, Set<Shape
for (UnionShape shape : model.getUnionShapes()) {
if (shapes.contains(shape)) {
LOGGER.finest(() -> "Generating union " + shape.getId());
directedCodegen.generateUnion(new GenerateUnion<>(context, serviceShape, shape));
directedCodegen.generateUnion(new GenerateUnionDirective<>(context, serviceShape, shape));
}
}
}

private void generateEnumShapes(C context, ServiceShape serviceShape, Set<Shape> shapes) {
// Generate enum shapes connected to the service.
for (StringShape shape : model.getStringShapesWithTrait(EnumTrait.class)) {
if (shapes.contains(shape)) {
LOGGER.finest(() -> "Generating string enum " + shape.getId());
directedCodegen.generateEnumShape(new GenerateEnumDirective<>(context, serviceShape, shape));
}
}

/*
TODO: uncomment in idl-2.0
for (EnumShape shape : model.getEnumShapes()) {
if (shapes.contains(shape)) {
LOGGER.finest(() -> "Generating enum " + shape.getId());
directedCodegen.generateEnumShape(new GenerateEnumContext<>(context, serviceShape, shape));
}
}
TODO: uncomment in idl-2.0
*/
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,12 @@
* @param <S> Codegen settings type.
* @see DirectedCodegen#createContext
*/
public final class CreateContext<S> extends Directive<S> {
public final class CreateContextDirective<S> extends Directive<S> {

private final SymbolProvider symbolProvider;
private final FileManifest fileManifest;

CreateContext(
CreateContextDirective(
Model model,
S settings,
ServiceShape service,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@
* @param <S> Codegen settings type.
* @see DirectedCodegen#createContext
*/
public final class CreateSymbolProvider<S> extends Directive<S> {
CreateSymbolProvider(Model model, S settings, ServiceShape service) {
public final class CreateSymbolProviderDirective<S> extends Directive<S> {
CreateSymbolProviderDirective(Model model, S settings, ServiceShape service) {
super(model, settings, service);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@
* @see DirectedCodegen#customizeBeforeIntegrations
* @see DirectedCodegen#customizeAfterIntegrations
*/
public final class Customize<C extends CodegenContext<S, ?>, S> extends ContextualDirective<C, S> {
Customize(C context, ServiceShape service) {
public final class CustomizeDirective<C extends CodegenContext<S, ?>, S> extends ContextualDirective<C, S> {
CustomizeDirective(C context, ServiceShape service) {
super(context, service);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,29 +39,29 @@ public interface DirectedCodegen<C extends CodegenContext<S, ?>, S> {
* @param directive Directive context data.
* @return Returns the created SymbolProvider.
*/
SymbolProvider createSymbolProvider(CreateSymbolProvider<S> directive);
SymbolProvider createSymbolProvider(CreateSymbolProviderDirective<S> directive);

/**
* Creates the codegen context object.
*
* @param directive Directive context data.
* @return Returns the created context object used by the rest of the directed generation.
*/
C createContext(CreateContext<S> directive);
C createContext(CreateContextDirective<S> directive);

/**
* Generates the code needed for a service shape.
*
* @param directive Directive to perform.
*/
void generateService(GenerateService<C, S> directive);
void generateService(GenerateServiceDirective<C, S> directive);

/**
* Generates the code needed for a resource shape.
*
* @param directive Directive to perform.
*/
default void generateResource(GenerateResource<C, S> directive) {
default void generateResource(GenerateResourceDirective<C, S> directive) {
// Does nothing by default.
}

Expand All @@ -73,30 +73,30 @@ default void generateResource(GenerateResource<C, S> directive) {
*
* @param directive Directive to perform.
*/
void generateStructure(GenerateStructure<C, S> directive);
void generateStructure(GenerateStructureDirective<C, S> directive);

/**
* Generates the code needed for an error structure.
*
* @param directive Directive to perform.
*/
void generateError(GenerateError<C, S> directive);
void generateError(GenerateErrorDirective<C, S> directive);

/**
* Generates the code needed for a union shape.
*
* @param directive Directive to perform.
*/
void generateUnion(GenerateUnion<C, S> directive);
void generateUnion(GenerateUnionDirective<C, S> directive);

/*
* TODO: Uncomment in IDL-2.0 branch
*
* Generates the code needed for an enum shape.
/**
* 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
* IDL 2.0.
*
* @param directive Directive to perform.
*/
//void generateEnumShape(GenerateEnumContext<C, S> directive);
void generateEnumShape(GenerateEnumDirective<C, S> directive);

/**
* Performs any necessary code generation after all shapes are generated,
Expand All @@ -105,7 +105,7 @@ default void generateResource(GenerateResource<C, S> directive) {
*
* @param directive Directive to perform.
*/
default void customizeBeforeIntegrations(Customize<C, S> directive) {
default void customizeBeforeIntegrations(CustomizeDirective<C, S> directive) {
// Does nothing by default.
}

Expand All @@ -124,7 +124,7 @@ default void customizeBeforeIntegrations(Customize<C, S> directive) {
*
* @param directive Directive to perform.
*/
default void customizeAfterIntegrations(Customize<C, S> directive) {
default void customizeAfterIntegrations(CustomizeDirective<C, S> directive) {
// Does nothing by default.
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@
import software.amazon.smithy.model.shapes.ShapeId;

/**
* Base class for all Directives emitted from {@link DirectedCodegen}.
* Directive classes contain all of the context needed in order to perform
* the tasks defined in a {@link DirectedCodegen} implementation.
*
* @param <S> Settings object used to configure code generation.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* 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.codegen.core.directed;

import software.amazon.smithy.codegen.core.CodegenContext;
import software.amazon.smithy.model.shapes.ServiceShape;
import software.amazon.smithy.model.shapes.Shape;
import software.amazon.smithy.model.traits.EnumTrait;

/**
* Directive used to generate an enum shape or enum string shape.
*
* @param <C> CodegenContext type.
* @param <S> Codegen settings type.
* @see DirectedCodegen#generateUnion
*/
public final class GenerateEnumDirective<C extends CodegenContext<S, ?>, S> extends ShapeDirective<Shape, C, S> {

GenerateEnumDirective(C context, ServiceShape service, Shape shape) {
super(context, service, validateShape(shape));
}

private static Shape validateShape(Shape shape) {
if (!shape.isStringShape() || !shape.hasTrait(EnumTrait.class)) {
throw new IllegalArgumentException("GenerateEnum requires a string shape with the enum trait");
}

return shape;
}

/**
* Represents the type of enum to generate. Smithy IDL 2.0 introduces actual
* enum shapes.
*/
public enum EnumType {
/** A string shape marked with the enum trait is being generated. */
STRING,

// TODO: idl-2.0
// ENUM
}

/**
* Gets the type of enum being generated, whether it's a string shape marked with
* the enum trait or, in Smithy IDL 2.0, an actual enum shape.
*
* <p>Note that Smithy IDL 2.0 generators can perform a pre-processing transform
* to convert eligible string shape enums to proper enums, removing the need to
* check this property.
*
* @return Gets the type of enum being generated.
* @see #getEnumTrait()
*/
public EnumType getEnumType() {
// TODO: update when idl-2.0 is released.
return EnumType.STRING;
}

/**
* Gets the {@link EnumTrait} of the shape.
*
* @return Returns the enum trait.
*/
public EnumTrait getEnumTrait() {
return shape().expectTrait(EnumTrait.class);
}

/*
TODO: Uncomment after IDL-2.0 is shipped.
public EnumShape expectEnumShape() {
return shape().asEnumShape().orElseThrow(() -> new ExpectationNotMetException(
"Expected an enum shape, but found " + shape(), shape()));
}
*/
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,10 @@
* @param <S> Codegen settings type.
* @see DirectedCodegen#generateError
*/
public final class GenerateError<C extends CodegenContext<S, ?>, S> extends ShapeDirective<StructureShape, C, S> {
public final class GenerateErrorDirective<C extends CodegenContext<S, ?>, S>
extends ShapeDirective<StructureShape, C, S> {

GenerateError(C context, ServiceShape service, StructureShape shape) {
GenerateErrorDirective(C context, ServiceShape service, StructureShape shape) {
super(context, service, shape);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,9 @@
* @param <S> Codegen settings type.
* @see DirectedCodegen#generateResource
*/
public final class GenerateResource<C extends CodegenContext<S, ?>, S> extends ShapeDirective<ResourceShape, C, S> {
GenerateResource(C context, ServiceShape service, ResourceShape shape) {
public final class GenerateResourceDirective<C extends CodegenContext<S, ?>, S>
extends ShapeDirective<ResourceShape, C, S> {
GenerateResourceDirective(C context, ServiceShape service, ResourceShape shape) {
super(context, service, shape);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,10 @@
* @param <S> Codegen settings type.
* @see DirectedCodegen#generateService
*/
public final class GenerateService<C extends CodegenContext<S, ?>, S> extends ShapeDirective<ServiceShape, C, S> {
public final class GenerateServiceDirective<C extends CodegenContext<S, ?>, S>
extends ShapeDirective<ServiceShape, C, S> {

GenerateService(C context, ServiceShape service) {
GenerateServiceDirective(C context, ServiceShape service) {
super(context, service, service);
}

Expand Down
Loading

0 comments on commit ced203e

Please sign in to comment.