Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.StringJoiner;
import java.util.function.BiFunction;
import java.util.logging.Logger;
Expand All @@ -28,10 +29,13 @@
import software.amazon.smithy.codegen.core.SymbolDependency;
import software.amazon.smithy.codegen.core.SymbolDependencyContainer;
import software.amazon.smithy.codegen.core.SymbolReference;
import software.amazon.smithy.go.codegen.knowledge.GoUsageIndex;
import software.amazon.smithy.model.Model;
import software.amazon.smithy.model.shapes.MemberShape;
import software.amazon.smithy.model.shapes.Shape;
import software.amazon.smithy.model.traits.DeprecatedTrait;
import software.amazon.smithy.model.traits.DocumentationTrait;
import software.amazon.smithy.model.traits.HttpPrefixHeadersTrait;
import software.amazon.smithy.model.traits.MediaTypeTrait;
import software.amazon.smithy.model.traits.RequiredTrait;
import software.amazon.smithy.model.traits.StringTrait;
Expand Down Expand Up @@ -290,24 +294,60 @@ boolean writePackageShapeDocs(Shape shape) {
* @return Returns true if docs were written.
*/
boolean writeMemberDocs(Model model, MemberShape member) {
return member.getMemberTrait(model, DocumentationTrait.class)
boolean hasDocs;

hasDocs = member.getMemberTrait(model, DocumentationTrait.class)
.map(DocumentationTrait::getValue)
.map(docs -> {
writeDocs(docs);
member.getMemberTrait(model, MediaTypeTrait.class)
.map(StringTrait::getValue)
.ifPresent(mediaType -> writeDocs(
"\n\nThis value conforms to the media type: " + mediaType));

member.getMemberTrait(model, RequiredTrait.class)
.ifPresent((value) -> {
if (docs.length() != 0) {
writeDocs("");
}
writeDocs("This member is required.");
});
return true;
}).orElse(false);

Optional<String> stringOptional = member.getMemberTrait(model, MediaTypeTrait.class)
.map(StringTrait::getValue);
if (stringOptional.isPresent()) {
if (hasDocs) {
writeDocs("");
}
writeDocs("This value conforms to the media type: " + stringOptional.get());
hasDocs = true;
}

GoUsageIndex usageIndex = GoUsageIndex.of(model);
if (usageIndex.isUsedForOutput(member)) {
if (member.getMemberTrait(model,
HttpPrefixHeadersTrait.class).isPresent()) {
if (hasDocs) {
writeDocs("");
}
writeDocs("Map keys will be normalized to lower-case.");
hasDocs = true;
}
}

if (member.getMemberTrait(model, RequiredTrait.class).isPresent()) {
if (hasDocs) {
writeDocs("");
}
writeDocs("This member is required.");
hasDocs = true;
}

Optional<DeprecatedTrait> deprecatedTrait = member.getMemberTrait(model, DeprecatedTrait.class);
if (deprecatedTrait.isPresent()) {
if (hasDocs) {
writeDocs("");
}
final String defaultMessage = "This member has been deprecated.";
writeDocs("Deprecated: " + deprecatedTrait.get().getMessage().map(s -> {
if (s.length() == 0) {
return defaultMessage;
}
return s;
}).orElse(defaultMessage));
}

return hasDocs;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import software.amazon.smithy.model.shapes.OperationShape;
import software.amazon.smithy.model.shapes.ServiceShape;
import software.amazon.smithy.model.shapes.StructureShape;
import software.amazon.smithy.model.traits.DeprecatedTrait;

/**
* Generates a client operation and associated custom shapes.
Expand Down Expand Up @@ -91,7 +92,20 @@ public void run() {
Symbol outputSymbol = symbolProvider.toSymbol(outputShape);

// Generate operation method
writer.writeShapeDocs(operation);
final boolean hasDocs = writer.writeShapeDocs(operation);
operation.getTrait(DeprecatedTrait.class)
.ifPresent(trait -> {
if (hasDocs) {
writer.writeDocs("");
}
final String defaultMessage = "This operation has been deprecated.";
writer.writeDocs("Deprecated: " + trait.getMessage().map(s -> {
if (s.length() == 0) {
return defaultMessage;
}
return s;
}).orElse(defaultMessage));
});
Symbol contextSymbol = SymbolUtils.createValueSymbolBuilder("Context", SmithyGoDependency.CONTEXT).build();
writer.openBlock("func (c $P) $T(ctx $T, params $P, optFns ...func(*Options)) ($P, error) {", "}",
serviceSymbol, operationSymbol, contextSymbol, inputSymbol, outputSymbol, () -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@

package software.amazon.smithy.go.codegen;

import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.logging.Logger;
import software.amazon.smithy.codegen.core.CodegenException;
import software.amazon.smithy.codegen.core.Symbol;
Expand All @@ -32,24 +34,31 @@
import software.amazon.smithy.model.node.NumberNode;
import software.amazon.smithy.model.node.ObjectNode;
import software.amazon.smithy.model.node.StringNode;
import software.amazon.smithy.model.shapes.MapShape;
import software.amazon.smithy.model.shapes.MemberShape;
import software.amazon.smithy.model.shapes.Shape;
import software.amazon.smithy.model.shapes.ShapeType;
import software.amazon.smithy.model.shapes.SimpleShape;
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.HttpPrefixHeadersTrait;
import software.amazon.smithy.model.traits.StreamingTrait;
import software.amazon.smithy.model.traits.Trait;
import software.amazon.smithy.utils.ListUtils;
import software.amazon.smithy.utils.OptionalUtils;
import software.amazon.smithy.utils.SmithyBuilder;

/**
* Generates a shape type declaration based on the parameters provided.
*/
public final class ShapeValueGenerator {
private static final Logger LOGGER = Logger.getLogger(ShapeValueGenerator.class.getName());

protected final Model model;
protected final SymbolProvider symbolProvider;
protected final GoPointableIndex pointableIndex;
private final Model model;
private final SymbolProvider symbolProvider;
private final GoPointableIndex pointableIndex;
private final Config config;

/**
* Initializes a shape value generator.
Expand All @@ -58,9 +67,21 @@ public final class ShapeValueGenerator {
* @param symbolProvider the symbol provider.
*/
public ShapeValueGenerator(Model model, SymbolProvider symbolProvider) {
this(model, symbolProvider, Config.builder().build());
}

/**
* Initializes a shape value generator.
*
* @param model the Smithy model references.
* @param symbolProvider the symbol provider.
* @param config the shape value generator config.
*/
public ShapeValueGenerator(Model model, SymbolProvider symbolProvider, Config config) {
this.model = model;
this.symbolProvider = symbolProvider;
this.pointableIndex = GoPointableIndex.of(model);
this.config = config;
}

/**
Expand All @@ -79,7 +100,8 @@ public void writePointableStructureShapeValueInline(GoWriter writer, StructureSh
// not within the context of a member shape reference.
Symbol symbol = symbolProvider.toSymbol(shape);
writer.write("&$T{", symbol);
params.accept(new ShapeValueNodeVisitor(writer, this, shape));
params.accept(new ShapeValueNodeVisitor(writer, this, shape, ListUtils.copyOf(shape.getAllTraits().values()),
config));
writer.writeInline("}");
}

Expand Down Expand Up @@ -149,7 +171,8 @@ protected void structDeclShapeValue(GoWriter writer, MemberShape member, Node pa

String addr = CodegenUtils.asAddressIfAddressable(model, pointableIndex, member, "");
writer.write("$L$T{", addr, symbol);
params.accept(new ShapeValueNodeVisitor(writer, this, model.expectShape(member.getTarget())));
params.accept(new ShapeValueNodeVisitor(writer, this, model.expectShape(member.getTarget()),
ListUtils.copyOf(member.getAllTraits().values()), config));
writer.writeInline("}");
}

Expand Down Expand Up @@ -197,7 +220,8 @@ protected void unionDeclShapeValue(GoWriter writer, MemberShape member, ObjectNo
*/
protected void listDeclShapeValue(GoWriter writer, MemberShape member, Node params) {
writer.write("$P{", symbolProvider.toSymbol(member));
params.accept(new ShapeValueNodeVisitor(writer, this, model.expectShape(member.getTarget())));
params.accept(new ShapeValueNodeVisitor(writer, this, model.expectShape(member.getTarget()),
ListUtils.copyOf(member.getAllTraits().values()), config));
writer.writeInline("}");
}

Expand All @@ -210,7 +234,8 @@ protected void listDeclShapeValue(GoWriter writer, MemberShape member, Node para
*/
protected void mapDeclShapeValue(GoWriter writer, MemberShape member, Node params) {
writer.write("$P{", symbolProvider.toSymbol(member));
params.accept(new ShapeValueNodeVisitor(writer, this, model.expectShape(member.getTarget())));
params.accept(new ShapeValueNodeVisitor(writer, this, model.expectShape(member.getTarget()),
ListUtils.copyOf(member.getAllTraits().values()), config));
writer.writeInline("}");
}

Expand Down Expand Up @@ -327,17 +352,58 @@ protected void writeScalarValueInline(GoWriter writer, MemberShape member, Node
break;
}

params.accept(new ShapeValueNodeVisitor(writer, this, target));
params.accept(new ShapeValueNodeVisitor(writer, this, target,
ListUtils.copyOf(member.getAllTraits().values()), config));
writer.writeInline(closing);
}

/**
* Configuration that determines how shapes values are generated.
*/
public static final class Config {
private final boolean normalizeHttpPrefixHeaderKeys;

private Config(Builder builder) {
normalizeHttpPrefixHeaderKeys = builder.normalizeHttpPrefixHeaderKeys;
}

public static Builder builder() {
return new Builder();
}

/**
* Returns whether maps with the httpPrefixHeader trait should have their keys normalized.
*
* @return whether to normalize http prefix header keys
*/
public boolean isNormalizeHttpPrefixHeaderKeys() {
return normalizeHttpPrefixHeaderKeys;
}

public static final class Builder implements SmithyBuilder<Config> {
private boolean normalizeHttpPrefixHeaderKeys;

public Builder normalizeHttpPrefixHeaderKeys(boolean normalizeHttpPrefixHeaderKeys) {
this.normalizeHttpPrefixHeaderKeys = normalizeHttpPrefixHeaderKeys;
return this;
}

@Override
public Config build() {
return new Config(this);
}
}
}

/**
* NodeVisitor to walk shape value declarations with node values.
*/
private final class ShapeValueNodeVisitor implements NodeVisitor<Void> {
GoWriter writer;
ShapeValueGenerator valueGen;
Shape currentShape;
private final GoWriter writer;
private final ShapeValueGenerator valueGen;
private final Shape currentShape;
private final List<Trait> traits;
private final Config config;

/**
* Initializes shape value visitor.
Expand All @@ -347,9 +413,42 @@ private final class ShapeValueNodeVisitor implements NodeVisitor<Void> {
* @param shape the shape that visiting is relative to.
*/
private ShapeValueNodeVisitor(GoWriter writer, ShapeValueGenerator valueGen, Shape shape) {
this(writer, valueGen, shape, ListUtils.of());
}

/**
* Initializes shape value visitor.
*
* @param writer writer to write generated code with.
* @param valueGen shape value generator.
* @param shape the shape that visiting is relative to.
* @param traits the traits applied to the target shape by a MemberShape.
*/
private ShapeValueNodeVisitor(GoWriter writer, ShapeValueGenerator valueGen, Shape shape, List<Trait> traits) {
this(writer, valueGen, shape, traits, Config.builder().build());
}

/**
* Initializes shape value visitor.
*
* @param writer writer to write generated code with.
* @param valueGen shape value generator.
* @param shape the shape that visiting is relative to.
* @param traits the traits applied to the target shape by a MemberShape.
* @param config the shape value generator config.
*/
private ShapeValueNodeVisitor(
GoWriter writer,
ShapeValueGenerator valueGen,
Shape shape,
List<Trait> traits,
Config config
) {
this.writer = writer;
this.valueGen = valueGen;
this.currentShape = shape;
this.traits = traits;
this.config = config;
}

/**
Expand Down Expand Up @@ -395,10 +494,18 @@ public Void objectNode(ObjectNode node) {
break;

case MAP:
member = this.currentShape.asMapShape().get().getValue();
MapShape mapShape = this.currentShape.asMapShape().get();

String keyValue = keyNode.getValue();
if (config.isNormalizeHttpPrefixHeaderKeys()) {
keyValue = OptionalUtils.or(getTrait(HttpPrefixHeadersTrait.class),
() -> mapShape.getTrait(HttpPrefixHeadersTrait.class))
.map(httpPrefixHeadersTrait -> keyNode.getValue().toLowerCase())
.orElse(keyValue);
}

writer.write("$S: ", keyNode.getValue());
valueGen.writeMemberValueInline(writer, member, valueNode);
writer.write("$S: ", keyValue);
valueGen.writeMemberValueInline(writer, mapShape.getValue(), valueNode);
writer.write(",");
break;

Expand Down Expand Up @@ -523,6 +630,15 @@ private void writeInlineBigIntegerInit(GoWriter writer, Object value) {
+ "}()",
value, value);
}

private <T extends Trait> Optional<T> getTrait(Class<T> traitClass) {
for (Trait trait : traits) {
if (traitClass.isInstance(trait)) {
return Optional.of((T) trait);
}
}
return Optional.empty();
}
}

}
Loading