From 59abfa855253fefd1670c02d9e7874a9c86cc56c Mon Sep 17 00:00:00 2001 From: Tom Daffurn Date: Mon, 14 Oct 2024 16:45:15 +1000 Subject: [PATCH] feat: Write comments into generated Java and Kotlin (#3104) Closes #2791 --- .../ftl/deployment/JVMCodeGenerator.java | 7 +- .../deployment/JavaCodeGenerator.java | 73 ++++++++++--------- .../deployment/KotlinCodeGenerator.java | 32 +++++--- jvm-runtime/testdata/go/gomodule/server.go | 29 ++++++++ 4 files changed, 94 insertions(+), 47 deletions(-) diff --git a/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/JVMCodeGenerator.java b/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/JVMCodeGenerator.java index 47a4f1ef44..f9fe90d1b3 100644 --- a/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/JVMCodeGenerator.java +++ b/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/JVMCodeGenerator.java @@ -22,6 +22,7 @@ import xyz.block.ftl.v1.schema.Module; import xyz.block.ftl.v1.schema.Topic; import xyz.block.ftl.v1.schema.Type; +import xyz.block.ftl.v1.schema.TypeAlias; import xyz.block.ftl.v1.schema.Verb; public abstract class JVMCodeGenerator implements CodeGenProvider { @@ -77,7 +78,7 @@ public boolean trigger(CodeGenContext context) throws CodeGenException { if (existing != null) { nativeTypeAliasMap.put(new DeclRef(module.getName(), data.getName()), nativeName); - generateTypeAliasMapper(module.getName(), data.getName(), packageName, + generateTypeAliasMapper(module.getName(), data, packageName, Optional.of(nativeName), context.outDir()); handled = true; @@ -87,7 +88,7 @@ public boolean trigger(CodeGenContext context) throws CodeGenException { } } if (!handled) { - generateTypeAliasMapper(module.getName(), data.getName(), packageName, Optional.empty(), + generateTypeAliasMapper(module.getName(), data, packageName, Optional.empty(), context.outDir()); typeAliasMap.put(new DeclRef(module.getName(), data.getName()), data.getType()); } @@ -141,7 +142,7 @@ public boolean trigger(CodeGenContext context) throws CodeGenException { return true; } - protected abstract void generateTypeAliasMapper(String module, String name, String packageName, + protected abstract void generateTypeAliasMapper(String module, TypeAlias typeAlias, String packageName, Optional nativeTypeAlias, Path outputDir) throws IOException; protected abstract void generateTopicSubscription(Module module, Topic data, String packageName, diff --git a/jvm-runtime/ftl-runtime/java/deployment/src/main/java/xyz/block/ftl/javalang/deployment/JavaCodeGenerator.java b/jvm-runtime/ftl-runtime/java/deployment/src/main/java/xyz/block/ftl/javalang/deployment/JavaCodeGenerator.java index 7123bf7d86..35fef14c77 100644 --- a/jvm-runtime/ftl-runtime/java/deployment/src/main/java/xyz/block/ftl/javalang/deployment/JavaCodeGenerator.java +++ b/jvm-runtime/ftl-runtime/java/deployment/src/main/java/xyz/block/ftl/javalang/deployment/JavaCodeGenerator.java @@ -54,14 +54,15 @@ public class JavaCodeGenerator extends JVMCodeGenerator { public static final String PACKAGE_PREFIX = "ftl."; @Override - protected void generateTypeAliasMapper(String module, String name, String packageName, Optional nativeTypeAlias, - Path outputDir) throws IOException { - TypeSpec.Builder typeBuilder = TypeSpec.interfaceBuilder(className(name) + TYPE_MAPPER) + protected void generateTypeAliasMapper(String module, xyz.block.ftl.v1.schema.TypeAlias typeAlias, + String packageName, Optional nativeTypeAlias, Path outputDir) throws IOException { + TypeSpec.Builder typeBuilder = TypeSpec.interfaceBuilder(className(typeAlias.getName()) + TYPE_MAPPER) .addAnnotation(AnnotationSpec.builder(TypeAlias.class) - .addMember("name", "\"" + name + "\"") + .addMember("name", "\"" + typeAlias.getName() + "\"") .addMember("module", "\"" + module + "\"") .build()) - .addModifiers(Modifier.PUBLIC); + .addModifiers(Modifier.PUBLIC) + .addJavadoc(String.join("\n", typeAlias.getCommentsList())); if (nativeTypeAlias.isEmpty()) { TypeVariableName finalType = TypeVariableName.get("T"); typeBuilder.addTypeVariable(finalType); @@ -71,12 +72,9 @@ protected void generateTypeAliasMapper(String module, String name, String packag typeBuilder.addSuperinterface(ParameterizedTypeName.get(ClassName.get(TypeAliasMapper.class), ClassName.bestGuess(nativeTypeAlias.get()), ClassName.get(String.class))); } - TypeSpec theType = typeBuilder - .build(); - + TypeSpec theType = typeBuilder.build(); JavaFile javaFile = JavaFile.builder(packageName, theType) .build(); - javaFile.writeTo(outputDir); } @@ -104,18 +102,19 @@ protected void generateTopicSubscription(Module module, Topic data, String packa javaFile.writeTo(outputDir); } - protected void generateEnum(Module module, Enum data, String packageName, Map typeAliasMap, + protected void generateEnum(Module module, Enum ennum, String packageName, Map typeAliasMap, Map nativeTypeAliasMap, Map> enumVariantInfoMap, Path outputDir) throws IOException { - String interfaceType = className(data.getName()); - if (data.hasType()) { + String interfaceType = className(ennum.getName()); + if (ennum.hasType()) { //Enums with a type are "value enums" - Java natively supports these TypeSpec.Builder dataBuilder = TypeSpec.enumBuilder(interfaceType) - .addAnnotation(getGeneratedRefAnnotation(module.getName(), data.getName())) + .addAnnotation(getGeneratedRefAnnotation(module.getName(), ennum.getName())) .addAnnotation(AnnotationSpec.builder(xyz.block.ftl.Enum.class).build()) - .addModifiers(Modifier.PUBLIC); + .addModifiers(Modifier.PUBLIC) + .addJavadoc(String.join("\n", ennum.getCommentsList())); - TypeName enumType = toAnnotatedJavaTypeName(data.getType(), typeAliasMap, nativeTypeAliasMap); + TypeName enumType = toAnnotatedJavaTypeName(ennum.getType(), typeAliasMap, nativeTypeAliasMap); dataBuilder.addField(enumType, "value", Modifier.PRIVATE, Modifier.FINAL); dataBuilder.addMethod(MethodSpec.constructorBuilder() .addParameter(enumType, "value") @@ -128,8 +127,8 @@ protected void generateEnum(Module module, Enum data, String packageName, Map variantValuesTypes = data.getVariantsList().stream().collect( + Map variantValuesTypes = ennum.getVariantsList().stream().collect( Collectors.toMap(EnumVariant::getName, v -> toAnnotatedJavaTypeName(v.getValue().getTypeValue().getValue(), typeAliasMap, nativeTypeAliasMap))); - for (var variant : data.getVariantsList()) { + for (var variant : ennum.getVariantsList()) { // Interface has isX and getX methods for each variant String name = variant.getName(); TypeName valueTypeName = variantValuesTypes.get(name); @@ -170,7 +170,7 @@ protected void generateEnum(Module module, Enum data, String packageName, Map variantInfos = enumVariantInfoMap.computeIfAbsent(key, k -> new ArrayList<>()); - variantInfos.add(new EnumInfo(interfaceType, variant, data.getVariantsList())); + variantInfos.add(new EnumInfo(interfaceType, variant, ennum.getVariantsList())); } else { // Value type isn't a Ref, so we make a wrapper class that implements our interface TypeSpec.Builder dataBuilder = TypeSpec.classBuilder(className(name)) @@ -206,7 +206,8 @@ protected void generateDataObject(Module module, Data data, String packageName, String thisType = className(data.getName()); TypeSpec.Builder dataBuilder = TypeSpec.classBuilder(thisType) .addAnnotation(getGeneratedRefAnnotation(module.getName(), data.getName())) - .addModifiers(Modifier.PUBLIC); + .addModifiers(Modifier.PUBLIC) + .addJavadoc(String.join("\n", data.getCommentsList())); // if data is part of a type enum, generate the interface methods for each variant DeclRef key = new DeclRef(module.getName(), data.getName()); @@ -284,20 +285,27 @@ protected void generateVerb(Module module, Verb verb, String packageName, Map nativeTypeAlias, + protected void generateTypeAliasMapper(String module, xyz.block.ftl.v1.schema.TypeAlias typeAlias, String packageName, + Optional nativeTypeAlias, Path outputDir) throws IOException { - String thisType = className(name) + TYPE_MAPPER; + String thisType = className(typeAlias.getName()) + TYPE_MAPPER; TypeSpec.Builder typeBuilder = TypeSpec.interfaceBuilder(thisType) .addAnnotation(AnnotationSpec.builder(TypeAlias.class) - .addMember("name=\"" + name + "\"") + .addMember("name=\"" + typeAlias.getName() + "\"") .addMember("module=\"" + module + "\"") .build()) - .addModifiers(KModifier.PUBLIC); + .addModifiers(KModifier.PUBLIC) + .addKdoc(String.join("\n", typeAlias.getCommentsList())); if (nativeTypeAlias.isEmpty()) { TypeVariableName finalType = TypeVariableName.get("T"); typeBuilder.addTypeVariable(finalType); @@ -101,7 +103,8 @@ protected void generateEnum(Module module, Enum data, String packageName, Map