in codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/integration/Paginators.java [97:310]
private void writePaginator(
GoWriter writer,
Model model,
SymbolProvider symbolProvider,
PaginationInfo paginationInfo,
Symbol interfaceSymbol,
Symbol paginatorSymbol,
Symbol optionsSymbol
) {
var inputMember = symbolProvider.toMemberName(paginationInfo.getInputTokenMember());
var operation = paginationInfo.getOperation();
var pagingExtensionTrait = operation.getTrait(PagingExtensionTrait.class);
var operationSymbol = symbolProvider.toSymbol(operation);
var inputSymbol = symbolProvider.toSymbol(paginationInfo.getInput());
var inputTokenSymbol = symbolProvider.toSymbol(paginationInfo.getInputTokenMember());
var inputTokenShape = model.expectShape(paginationInfo.getInputTokenMember().getTarget());
GoPointableIndex pointableIndex = GoPointableIndex.of(model);
writer.pushState();
writer.putContext("paginator", paginatorSymbol);
writer.putContext("options", optionsSymbol);
writer.putContext("client", interfaceSymbol);
writer.putContext("input", inputSymbol);
writer.putContext("token", inputTokenSymbol);
writer.putContext("inputMember", inputMember);
writer.writeDocs(String.format("%s is a paginator for %s", paginatorSymbol, operationSymbol.getName()));
writer.write("""
type $paginator:T struct {
options $options:T
client $client:T
params $input:P
nextToken $token:P
firstPage bool
}
""");
Symbol newPagiantor = SymbolUtils.createValueSymbolBuilder(String.format("New%s",
paginatorSymbol.getName())).build();
writer.putContext("newPaginator", newPagiantor);
writer.writeDocs(String.format("%s returns a new %s", newPagiantor.getName(), paginatorSymbol.getName()));
writer.openBlock("func $newPaginator:T(client $client:T, params $input:P, "
+ "optFns ...func($options:P)) $paginator:P {", "}",
() -> {
writer.write("""
if params == nil {
params = &$input:T{}
}
options := $options:T{}""");
paginationInfo.getPageSizeMember().ifPresent(memberShape -> {
GoValueAccessUtils.writeIfNonZeroValueMember(model, symbolProvider, writer, memberShape,
"params", op -> {
op = CodegenUtils.getAsValueIfDereferencable(pointableIndex, memberShape, op);
writer.write("options.Limit = $L", op);
});
});
writer.write("""
for _, fn := range optFns {
fn(&options)
}
return &$paginator:T{
options: options,
client: client,
params: params,
firstPage: true,
nextToken: params.$inputMember:L,
}""");
}).write("");
writer.writeDocs("HasMorePages returns a boolean indicating whether more pages are available");
writer.openBlock("func (p $paginator:P) HasMorePages() bool {", "}", () -> {
writer.writeInline("return p.firstPage || ");
Runnable checkNotNil = () -> writer.writeInline("p.nextToken != nil");
if (inputTokenShape.getType() == ShapeType.STRING) {
writer.writeInline("(");
checkNotNil.run();
writer.write(" && len(*p.nextToken) != 0 )");
} else {
checkNotNil.run();
}
}).write("");
var contextSymbol = SymbolUtils.createValueSymbolBuilder("Context", SmithyGoDependency.CONTEXT)
.build();
var outputSymbol = symbolProvider.toSymbol(paginationInfo.getOutput());
var pageSizeMember = paginationInfo.getPageSizeMember();
writer.putContext("context", contextSymbol);
writer.putContext("output", outputSymbol);
writer.writeDocs(String.format("NextPage retrieves the next %s page.", operationSymbol.getName()));
writer.openBlock("func (p $paginator:P) NextPage(ctx $context:T, optFns ...func(*Options)) "
+ "($output:P, error) {", "}",
() -> {
writer.putContext("errorf", SymbolUtils.createValueSymbolBuilder("Errorf",
SmithyGoDependency.FMT).build());
writer.write("""
if !p.HasMorePages() {
return nil, $errorf:T("no more pages available")
}
params := *p.params
params.$inputMember:L = p.nextToken
""");
pageSizeMember.ifPresent(memberShape -> {
if (pointableIndex.isPointable(memberShape)) {
writer.write("""
var limit $P
if p.options.Limit > 0 {
limit = &p.options.Limit
}
params.$L = limit
""",
symbolProvider.toSymbol(memberShape),
symbolProvider.toMemberName(memberShape));
} else {
writer.write("params.$L = p.options.Limit", symbolProvider.toMemberName(memberShape))
.write("");
}
});
var optFns = GoWriter.ChainWritable.of(
getAdditionalClientOptions().stream()
.map(it -> goTemplate("$T,", it))
.toList()
).compose(false);
writer.write("""
optFns = append([]func(*Options) {
$W
}, optFns...)
result, err := p.client.$L(ctx, ¶ms, optFns...)
if err != nil {
return nil, err
}
p.firstPage = false
""", optFns, operationSymbol.getName());
var outputMemberPath = paginationInfo.getOutputTokenMemberPath();
var tokenMember = outputMemberPath.get(outputMemberPath.size() - 1);
Consumer<String> setNextTokenFromOutput = (container) -> {
writer.write("p.nextToken = $L", container + "."
+ symbolProvider.toMemberName(tokenMember));
};
for (int i = outputMemberPath.size() - 2; i >= 0; i--) {
var memberShape = outputMemberPath.get(i);
Consumer<String> inner = setNextTokenFromOutput;
setNextTokenFromOutput = (container) -> {
GoValueAccessUtils.writeIfNonZeroValueMember(model, symbolProvider, writer, memberShape,
container, inner);
};
}
{
final Consumer<String> inner = setNextTokenFromOutput;
setNextTokenFromOutput = s -> {
if (outputMemberPath.size() > 1) {
writer.write("p.nextToken = nil");
}
inner.accept(s);
};
}
{
final Consumer<String> setToken = setNextTokenFromOutput;
writer.write("prevToken := p.nextToken");
Optional<MemberShape> moreResults = pagingExtensionTrait
.flatMap(PagingExtensionTrait::getMoreResults);
if (moreResults.isPresent()) {
MemberShape memberShape = moreResults.get();
model.expectShape(memberShape.getTarget(), BooleanShape.class); // Must be boolean
writer.write("p.nextToken = nil");
String memberName = symbolProvider.toMemberName(memberShape);
if (pointableIndex.isNillable(memberShape.getTarget())) {
writer.openBlock("if result.$L != nil && *result.$L {", "}", memberName, memberName,
() -> setToken.accept("result"));
} else {
writer.openBlock("if result.$L {", "}", memberName, () -> setToken.accept("result"));
}
} else {
setToken.accept("result");
}
}
writer.write("");
if (inputTokenShape.isStringShape()) {
writer.write("""
if p.options.StopOnDuplicateToken &&
prevToken != nil &&
p.nextToken != nil &&
*prevToken == *p.nextToken {
p.nextToken = nil
}
""");
} else {
writer.write("_ = prevToken").write("");
}
writer.write("return result, nil");
});
writer.popState();
}