in codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/integration/Waiters2.java [510:654]
private void generateRetryable(
GoCodegenContext ctx,
GoWriter writer,
OperationShape operationShape,
String waiterName,
Waiter waiter
) {
var model = ctx.model();
var symbolProvider = ctx.symbolProvider();
var serviceShape = ctx.settings().getService(model);
StructureShape inputShape = model.expectShape(
operationShape.getInput().get(), StructureShape.class
);
StructureShape outputShape = model.expectShape(
operationShape.getOutput().get(), StructureShape.class
);
Symbol inputSymbol = symbolProvider.toSymbol(inputShape);
Symbol outputSymbol = symbolProvider.toSymbol(outputShape);
writer.write("");
writer.openBlock("func $L(ctx context.Context, input $P, output $P, err error) (bool, error) {",
"}", generateRetryableName(waiterName), inputSymbol, outputSymbol, () -> {
waiter.getAcceptors().forEach(acceptor -> {
writer.write("");
// scope each acceptor to avoid name collisions
Matcher matcher = acceptor.getMatcher();
switch (matcher.getMemberName()) {
case "output":
writer.addUseImports(SmithyGoDependency.FMT);
Matcher.OutputMember outputMember = (Matcher.OutputMember) matcher;
String path = outputMember.getValue().getPath();
String expectedValue = outputMember.getValue().getExpected();
PathComparator comparator = outputMember.getValue().getComparator();
writer.openBlock("if err == nil {", "}", () -> {
var pathInput = new GoJmespathExpressionGenerator.Variable(outputShape, "output");
var searchResult = new GoJmespathExpressionGenerator(ctx, writer)
.generate(JmespathExpression.parse(path), pathInput);
writer.write("expectedValue := $S", expectedValue);
writeWaiterComparator(writer, acceptor, comparator, searchResult);
});
break;
case "inputOutput":
writer.addUseImports(SmithyGoDependency.FMT);
Matcher.InputOutputMember ioMember = (Matcher.InputOutputMember) matcher;
path = ioMember.getValue().getPath();
expectedValue = ioMember.getValue().getExpected();
comparator = ioMember.getValue().getComparator();
// inputOutput matchers operate on a synthetic structure with operation input and output
// as top-level fields - we set that up here both in codegen for jmespathing and for
// the actual generated code to work
var inputOutputShape = StructureShape.builder()
.addMember("input", inputShape.toShapeId())
.addMember("output", outputShape.toShapeId())
.build();
writer.write("""
inputOutput := struct{
Input $P
Output $P
}{
Input: input,
Output: output,
}
""");
writer.openBlock("if err == nil {", "}", () -> {
var pathInput = new GoJmespathExpressionGenerator.Variable(
inputOutputShape, "inputOutput");
var searchResult = new GoJmespathExpressionGenerator(ctx, writer)
.generate(JmespathExpression.parse(path), pathInput);
writer.write("expectedValue := $S", expectedValue);
writeWaiterComparator(writer, acceptor, comparator, searchResult);
});
break;
case "success":
Matcher.SuccessMember successMember = (Matcher.SuccessMember) matcher;
writer.openBlock("if err == nil {", "}",
() -> {
writeMatchedAcceptorReturn(writer, acceptor);
});
break;
case "errorType":
Matcher.ErrorTypeMember errorTypeMember = (Matcher.ErrorTypeMember) matcher;
String errorType = errorTypeMember.getValue();
writer.openBlock("if err != nil {", "}", () -> {
// identify if this is a modeled error shape
Optional<ShapeId> errorShapeId = operationShape.getErrors().stream().filter(
shapeId -> {
return shapeId.getName(serviceShape).equalsIgnoreCase(errorType);
}).findFirst();
// if modeled error shape
if (errorShapeId.isPresent()) {
Shape errorShape = model.expectShape(errorShapeId.get());
Symbol modeledErrorSymbol = symbolProvider.toSymbol(errorShape);
writer.addUseImports(SmithyGoDependency.ERRORS);
writer.write("var errorType *$T", modeledErrorSymbol);
writer.openBlock("if errors.As(err, &errorType) {", "}", () -> {
writeMatchedAcceptorReturn(writer, acceptor);
});
} else {
// fall back to un-modeled error shape matching
writer.addUseImports(SmithyGoDependency.SMITHY);
writer.addUseImports(SmithyGoDependency.ERRORS);
// assert unmodeled error to smithy's API error
writer.write("var apiErr smithy.APIError");
writer.write("ok := errors.As(err, &apiErr)");
writer.openBlock("if !ok {", "}", () -> {
writer.write("return false, "
+ "fmt.Errorf(\"expected err to be of type smithy.APIError, "
+ "got %w\", err)");
});
writer.write("");
writer.openBlock("if $S == apiErr.ErrorCode() {", "}",
errorType, () -> {
writeMatchedAcceptorReturn(writer, acceptor);
});
}
});
break;
default:
throw new CodegenException(
String.format("unknown waiter state : %v", matcher.getMemberName())
);
}
});
writer.write("");
writer.write("if err != nil { return false, err }");
writer.write("return true, nil");
});
}