Skip to content

Commit

Permalink
Better method decomposition for client generator
Browse files Browse the repository at this point in the history
  • Loading branch information
Duzhinsky committed Sep 29, 2023
1 parent 786b8bb commit aa596d2
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 46 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.sudu.protogen.generator.client;

import com.squareup.javapoet.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.sudu.protogen.EmptyIfNotFound;
import org.sudu.protogen.NullifyIfNotFound;
Expand All @@ -14,6 +15,7 @@
import protogen.Options;

import java.util.List;
import java.util.Objects;

public class ApiMethodGeneratorBase {

Expand Down Expand Up @@ -68,36 +70,58 @@ private List<ParameterSpec> params() {
}

private CodeBlock body(List<ParameterSpec> params) {
CodeBlock returnExpr = CodeBlock.of("");
CodeBlock body = CodeBlock.of("");
CodeBlock returnExpr = CodeBlock.of("$LStubCall(grpcRequest)", method.generatedName());
if (returnType.getTypeName() != TypeName.VOID) {
returnExpr = CodeBlock.of("return $L", returnExpr);
}
return CodeBlock.builder().add(buildRequest(params)).addStatement(returnExpr).build();
}

private CodeBlock buildRequest(List<ParameterSpec> params) {
ClassName requestProtoType = method.getInputType().getProtobufTypeName();
if (requestType != null && !method.doUnfoldRequest()) {
body = CodeBlock.builder().addStatement("$T grpcRequest = $L", requestProtoType, requestType.toGrpcTransformer(CodeBlock.of("request"))).build();
} else {
if (requestType != null) {
ClassName inputType = method.getInputType().getDomainTypeName(context.configuration().namingManager());

List<CodeBlock> paramsBlocks = params.stream().map(p -> CodeBlock.of("$N", p)).toList();
CodeBlock paramsAsList = Poem.separatedSequence(paramsBlocks, ",$W");
body = CodeBlock.builder()
.addStatement("$T grpcRequest = new $T($L).toGrpc()", requestProtoType, inputType, paramsAsList)
.build();
if (requestType != null) {
if (!method.doUnfoldRequest()) {
return buildDomainRequest(requestProtoType);
} else {
List<FieldProcessingResult> processedFields = method.getInputType().getFields().stream()
.map(field -> new FieldGenerator(context, field).generate())
.filter(FieldProcessingResult::isNonVoid)
.toList();
CodeBlock builder = new ToGrpcMethodGenerator(context, requestProtoType, processedFields, false).builder("requestBuilder");
body = CodeBlock.builder()
.add(builder)
.addStatement("$T grpcRequest = requestBuilder.build()", requestProtoType)
.build();
return buildUnfoldedDomainRequest(params, requestProtoType);
}
} else {
return buildNonDomainRequest(requestProtoType);
}
returnExpr = CodeBlock.of("$LStubCall(grpcRequest)", method.generatedName());
if (returnType.getTypeName() != TypeName.VOID) {
returnExpr = CodeBlock.of("return $L", returnExpr);
}
return CodeBlock.builder().add(body).addStatement(returnExpr).build();
}

@NotNull
private CodeBlock buildNonDomainRequest(ClassName requestProtoType) {
List<FieldProcessingResult> processedFields = method.getInputType().getFields().stream()
.map(field -> new FieldGenerator(context, field).generate())
.filter(FieldProcessingResult::isNonVoid)
.toList();
CodeBlock builder = new ToGrpcMethodGenerator(context, requestProtoType, processedFields, false).builder("requestBuilder");
return CodeBlock.builder()
.add(builder)
.addStatement("$T grpcRequest = requestBuilder.build()", requestProtoType)
.build();
}

@NotNull
private CodeBlock buildUnfoldedDomainRequest(List<ParameterSpec> params, ClassName requestProtoType) {
ClassName inputType = method.getInputType().getDomainTypeName(context.configuration().namingManager());

List<CodeBlock> paramsBlocks = params.stream().map(p -> CodeBlock.of("$N", p)).toList();
CodeBlock paramsAsList = Poem.separatedSequence(paramsBlocks, ",$W");
return CodeBlock.of("$T grpcRequest = $L;\n",
requestProtoType,
Objects.requireNonNull(requestType).toGrpcTransformer(
CodeBlock.of("new $T($L)", inputType, paramsAsList)
)
);
}

@NotNull
private CodeBlock buildDomainRequest(ClassName requestProtoType) {
return CodeBlock.of("$T grpcRequest = $L;\n",
requestProtoType,
Objects.requireNonNull(requestType).toGrpcTransformer(CodeBlock.of("request"))
);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.sudu.protogen.generator.client;

import com.squareup.javapoet.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.sudu.protogen.descriptors.Method;
import org.sudu.protogen.descriptors.RepeatedContainer;
Expand Down Expand Up @@ -36,7 +37,7 @@ public MethodSpec generate() {
return MethodSpec.methodBuilder(method.generatedName() + "StubCall")
.addModifiers(Modifier.PRIVATE)
.addParameters(parameters())
.addCode(new BodyGenerator().withIfNotFound().get())
.addCode(new BodyGenerator().get())
.returns(returnType.getTypeName())
.addAnnotation(
method.ifNotFoundBehavior() == NULLIFY
Expand All @@ -50,10 +51,15 @@ private class BodyGenerator implements Supplier<CodeBlock> {

@Override
public CodeBlock get() {
return new IfNotFoundDecorator(getStrategy()).get();
}

@NotNull
private BodyGenerator getStrategy() {
if (method.isOutputStreaming()) {
return new StreamingBodyGenerator().get();
return new StreamingBodyGenerator();
} else {
return new CommonBodyGenerator().get();
return new CommonBodyGenerator();
}
}

Expand All @@ -64,7 +70,7 @@ public CodeBlock get() {
if (returnType.getTypeName() != TypeName.VOID) {
returnExpr = CodeBlock.of("return $L", returnType.fromGrpcTransformer(returnExpr));
}
return CodeBlock.builder().addStatement(returnExpr).build();
return returnExpr.toBuilder().add(";").build();
}
}

Expand All @@ -73,25 +79,22 @@ private class StreamingBodyGenerator extends BodyGenerator {
@Override
public CodeBlock get() {
if (!(returnType instanceof RepeatedType repType)) throw new IllegalArgumentException();
CodeBlock body = CodeBlock.of("var iterator = $N.$L(request);\n", stubField, method.getName());
// I write mapping here manually because input is always an Iterator<Grpc..> and output is specified by the RepeatedContainer option
// So RepeatedType.fromGrpcTransformer is not suitable because it does only T<U> <--> T<V> mappings
CodeBlock mappingExpr = CodeBlock.of("i -> $L", repType.getElementModel().fromGrpcTransformer(CodeBlock.of("i")));
return CodeBlock.builder()
.add(body)
.addStatement("return $L\n.map($L)$L",
RepeatedContainer.ITERATOR.getToStreamExpr(CodeBlock.of("iterator")),
mappingExpr,
repType.getRepeatedType().getCollectorExpr()
)
.build();
return CodeBlock.of("""
var iterator = $N.$L(request);
return $L
$>.map(i -> $L)$L;$<
""",
stubField,
method.getName(),
RepeatedContainer.ITERATOR.getToStreamExpr(CodeBlock.of("iterator")),
repType.getElementModel().fromGrpcTransformer(CodeBlock.of("i")),
repType.getRepeatedType().getCollectorExpr()
);
}
}

private BodyGenerator withIfNotFound() {
return new IfNotFoundDecorator(this);
}

private class IfNotFoundDecorator extends BodyGenerator {

private final BodyGenerator generator;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ private TypeModel responseTypeModel() {
private CodeBlock generateBody() {
return CodeBlock.of("""
try {
$L
$>$L$<
} catch (Throwable $$t) { responseObserver.onError($$t); }
finally { responseObserver.onCompleted(); }
""", generateAbstractMethodCall());
Expand Down

0 comments on commit aa596d2

Please sign in to comment.