From 38c054ba68c100badcf976a39132e4fe76dd3220 Mon Sep 17 00:00:00 2001 From: Alexander Sysoev Date: Fri, 19 Sep 2025 19:28:22 +0200 Subject: [PATCH 1/5] Added pb comments generation KRPC-149 --- .../src/main/kotlin/kotlinx/rpc/Extensions.kt | 4 +- .../kotlin/kotlinx/rpc/buf/BufExtensions.kt | 43 ++++++ .../rpc/protoc/DefaultProtoSourceSet.kt | 34 +++-- .../rpc/protoc/DefaultProtocExtension.kt | 5 +- .../src/commonTest/proto/comments.proto | 93 ++++++++++++ .../gen/core/AModelToKotlinCommonGenerator.kt | 15 +- .../rpc/protoc/gen/core/CodeGenerator.kt | 132 ++++++++++++++---- .../rpc/protoc/gen/core/ProtocGenPlugin.kt | 49 +++++-- .../rpc/protoc/gen/core/codeRequestToModel.kt | 126 ++++++++++------- .../kotlinx/rpc/protoc/gen/core/comments.kt | 102 ++++++++++++++ .../rpc/protoc/gen/core/model/FieldType.kt | 8 +- .../rpc/protoc/gen/core/model/model.kt | 20 +-- .../protoc/gen/grpc/GrpcProtocGenPlugin.kt | 6 +- .../grpc/ModelToGrpcKotlinCommonGenerator.kt | 13 +- .../ModelToProtobufKotlinCommonGenerator.kt | 39 ++++-- .../rpc/protoc/gen/ProtobufProtocGenPlugin.kt | 6 +- 16 files changed, 540 insertions(+), 155 deletions(-) create mode 100644 grpc/grpc-core/src/commonTest/proto/comments.proto create mode 100644 protoc-gen/common/src/main/kotlin/kotlinx/rpc/protoc/gen/core/comments.kt diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/Extensions.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/Extensions.kt index aab87ea0a..e0a595383 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/Extensions.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/Extensions.kt @@ -19,7 +19,9 @@ import org.gradle.kotlin.dsl.property import java.util.concurrent.atomic.AtomicBoolean import javax.inject.Inject -internal fun Project.rpcExtension(): RpcExtension = extensions.findByType() +internal fun Project.rpcExtensionOrNull(): RpcExtension? = extensions.findByType() + +internal fun Project.rpcExtension(): RpcExtension = rpcExtensionOrNull() ?: error("Rpc extension not found. Please apply the plugin to the project") public open class RpcExtension @Inject constructor(objects: ObjectFactory, private val project: Project) { diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/BufExtensions.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/BufExtensions.kt index 28941c66d..449ae074b 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/BufExtensions.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/buf/BufExtensions.kt @@ -68,6 +68,7 @@ public open class BufExtension @Inject constructor(objects: ObjectFactory) { * * @see "buf generate" command * @see [BUF_GEN_YAML] + * @see [BufGenerateExtension] */ public val generate: BufGenerateExtension = objects.newInstance(BufGenerateExtension::class.java) @@ -76,6 +77,7 @@ public open class BufExtension @Inject constructor(objects: ObjectFactory) { * * @see "buf generate" command * @see [BUF_GEN_YAML] + * @see [BufGenerateExtension] */ public fun generate(configure: Action) { configure.execute(generate) @@ -226,4 +228,45 @@ public open class BufGenerateExtension @Inject constructor(internal val project: Default(""), ; } + + /** + * Option to configure the indent sized for the generated code. + * + * Default value: `4`. + */ + public val indentSize: Property = project.objects.property().convention(4) + + /** + * Extension for configuring comments in the generated code. + * + * @see [BufCommentsExtension]. + */ + public val comments: BufCommentsExtension = project.objects.newInstance(BufCommentsExtension::class.java) + + /** + * Extension for configuring comments in the generated code. + * + * @see [BufCommentsExtension]. + */ + public fun comments(configure: Action) { + configure.execute(comments) + } +} + +/** + * Extension for configuring comments in the generated code. + */ +public open class BufCommentsExtension @Inject constructor(internal val project: Project) { + /** + * Whether to copy comments from the original source files. + */ + public val copyComments: Property = project.objects.property().convention(true) + + /** + * Whether to include file-level comments. This includes: + * - Comments on the `package` declaration. + * - Comments on the `syntax` declaration. + * - Comments on the `editions` declaration. + */ + public val includeFileLevelComments: Property = project.objects.property().convention(true) } diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtoSourceSet.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtoSourceSet.kt index 5c4d07697..e2cce027e 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtoSourceSet.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtoSourceSet.kt @@ -4,9 +4,13 @@ package kotlinx.rpc.protoc +import kotlinx.rpc.buf.BufCommentsExtension +import kotlinx.rpc.buf.BufGenerateExtension import kotlinx.rpc.buf.tasks.BufGenerateTask import kotlinx.rpc.protoc.ProtocPlugin.Companion.GRPC_KOTLIN_MULTIPLATFORM import kotlinx.rpc.protoc.ProtocPlugin.Companion.KOTLIN_MULTIPLATFORM +import kotlinx.rpc.rpcExtension +import kotlinx.rpc.rpcExtensionOrNull import kotlinx.rpc.util.findOrCreate import kotlinx.rpc.util.withKotlinJvmExtension import kotlinx.rpc.util.withKotlinKmpExtension @@ -14,14 +18,17 @@ import org.gradle.api.* import org.gradle.api.file.SourceDirectorySet import org.gradle.api.provider.ListProperty import org.gradle.api.provider.Property +import org.gradle.api.provider.Provider import org.gradle.api.tasks.SourceSet import org.gradle.api.tasks.SourceSetContainer import org.gradle.kotlin.dsl.add import org.gradle.kotlin.dsl.listProperty +import org.gradle.kotlin.dsl.newInstance import org.gradle.kotlin.dsl.property import org.gradle.kotlin.dsl.the import org.jetbrains.kotlin.gradle.dsl.ExplicitApiMode import org.jetbrains.kotlin.gradle.dsl.KotlinBaseExtension +import org.jetbrains.kotlin.gradle.targets.js.npm.includedRange import javax.inject.Inject @Suppress("UNCHECKED_CAST") @@ -58,11 +65,7 @@ internal open class DefaultProtoSourceSet @Inject constructor( javaJar(project.kotlinMultiplatformProtocPluginJarPath) } - options.put("debugOutput", "protoc-gen-kotlin-multiplatform.log") - - if (this@DefaultProtoSourceSet.name.lowercase().endsWith("main")) { - options.put("explicitApiModeEnabled", explicitApiModeEnabled) - } + defaultOptions(explicitApiModeEnabled) } plugins.create(GRPC_KOTLIN_MULTIPLATFORM) { @@ -70,12 +73,25 @@ internal open class DefaultProtoSourceSet @Inject constructor( javaJar(project.grpcKotlinMultiplatformProtocPluginJarPath) } - options.put("debugOutput", "protoc-gen-grpc-kotlin-multiplatform.log") + defaultOptions(explicitApiModeEnabled) + } + } - if (this@DefaultProtoSourceSet.name.lowercase().endsWith("main")) { - options.put("explicitApiModeEnabled", explicitApiModeEnabled) - } + private fun ProtocPlugin.defaultOptions(explicitApiModeEnabled: Provider) { + options.put("debugOutput", "protoc-gen-$name.log") + + if (this@DefaultProtoSourceSet.name.lowercase().endsWith("main")) { + options.put("explicitApiModeEnabled", explicitApiModeEnabled) + } + + val comments: Provider = project.provider { + project.rpcExtensionOrNull()?.run { protoc.buf.generate } + ?: project.objects.newInstance() } + + options.put("generateComments", comments.flatMap { it.comments.copyComments }) + options.put("generateFileLevelComments", comments.flatMap { it.comments.includeFileLevelComments }) + options.put("indentSize", comments.flatMap { it.indentSize }) } val languageSourceSets: ListProperty = project.objects.listProperty() diff --git a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtocExtension.kt b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtocExtension.kt index e2297cb4f..a491d32ba 100644 --- a/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtocExtension.kt +++ b/gradle-plugin/src/main/kotlin/kotlinx/rpc/protoc/DefaultProtocExtension.kt @@ -25,10 +25,7 @@ import org.gradle.api.tasks.TaskProvider import org.gradle.api.tasks.compile.JavaCompile import org.gradle.kotlin.dsl.findByType import org.gradle.kotlin.dsl.newInstance -import org.gradle.kotlin.dsl.the import org.gradle.kotlin.dsl.withType -import org.jetbrains.kotlin.gradle.dsl.ExplicitApiMode -import org.jetbrains.kotlin.gradle.dsl.KotlinBaseExtension import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask import java.io.File @@ -36,7 +33,7 @@ import javax.inject.Inject internal open class DefaultProtocExtension @Inject constructor( objects: ObjectFactory, - private val project: Project, + project: Project, ) : ProtocExtension { override val buf: BufExtension = project.objects.newInstance() override fun buf(action: Action) { diff --git a/grpc/grpc-core/src/commonTest/proto/comments.proto b/grpc/grpc-core/src/commonTest/proto/comments.proto new file mode 100644 index 000000000..08f606e38 --- /dev/null +++ b/grpc/grpc-core/src/commonTest/proto/comments.proto @@ -0,0 +1,93 @@ +// detached syntax + + +// leading syntax +syntax = "proto3"; + +// detached package + +// leading package +package some; + +// detached Some + +// leading Some 1 +// leading Some 2 +/* leading + + +Some + + +3 + +funky indents: + - option 1 + - suboption 2 +*/ +message Some { + // trailing Some + + // detached field1 1.1 + // detached field1 1.2 + + + // detached field1 2.1 + + // leading field1 + Nested.Nested.Nested field1 = 1; + // trailing field1 + + SomeEnum field2 = 2; // trailing field2 + + // leading SomeEnum + enum SomeEnum { + // trailing SomeEnum + + option allow_alias = true; + + // leading SomeEnum.value + value = 0; + + // leading SomeEnum.value_alias + value_alias = 0; + } + + // leading Nested + message Nested { + // leading Nested.Nested + message Nested { + message Nested { /* trailing Nested.Nested.Nested */ + // leading Nested.Nested.Nested.nested + string nested = 1; + } + } + } + + // leading choice + oneof choice { + // leading red + int32 red = 3; + // leading blue + int32 blue = 4; + + int32 yellow = 5; // leading blue + } +} + +// detached Service + +// leading Service +service Service { + // trailing Service + + // leading Method1 + rpc Method1(Some) returns (Some); + // trailing Method1 + + // leading Method2 + rpc Method2(Some) returns (Some); + // trailing Method2 + + rpc Method3(Some) returns (Some); // leading Method3 +} diff --git a/protoc-gen/common/src/main/kotlin/kotlinx/rpc/protoc/gen/core/AModelToKotlinCommonGenerator.kt b/protoc-gen/common/src/main/kotlin/kotlinx/rpc/protoc/gen/core/AModelToKotlinCommonGenerator.kt index 7ad517f14..07ed5ae24 100644 --- a/protoc-gen/common/src/main/kotlin/kotlinx/rpc/protoc/gen/core/AModelToKotlinCommonGenerator.kt +++ b/protoc-gen/common/src/main/kotlin/kotlinx/rpc/protoc/gen/core/AModelToKotlinCommonGenerator.kt @@ -4,7 +4,6 @@ package kotlinx.rpc.protoc.gen.core -import kotlinx.rpc.protoc.gen.core.model.EnumDeclaration import kotlinx.rpc.protoc.gen.core.model.FieldDeclaration import kotlinx.rpc.protoc.gen.core.model.FieldType import kotlinx.rpc.protoc.gen.core.model.FileDeclaration @@ -21,9 +20,8 @@ const val INTERNAL_RPC_API_ANNO = "kotlinx.rpc.internal.utils.InternalRpcApi" const val WITH_CODEC_ANNO = "kotlinx.rpc.grpc.codec.WithCodec" abstract class AModelToKotlinCommonGenerator( + protected val config: Config, protected val model: Model, - protected val logger: Logger, - private val explicitApiModeEnabled: Boolean, ) { protected abstract fun CodeGenerator.generatePublicDeclaredEntities(fileDeclaration: FileDeclaration) protected abstract fun CodeGenerator.generateInternalDeclaredEntities(fileDeclaration: FileDeclaration) @@ -52,10 +50,11 @@ abstract class AModelToKotlinCommonGenerator( private fun FileDeclaration.generatePublicKotlinFile(): FileGenerator { currentPackage = packageName - return file(logger = logger, explicitApiModeEnabled = explicitApiModeEnabled) { + return file(config) { filename = this@generatePublicKotlinFile.name packageName = this@generatePublicKotlinFile.packageName.safeFullName() packagePath = this@generatePublicKotlinFile.packageName.safeFullName() + comments = this@generatePublicKotlinFile.doc dependencies.forEach { dependency -> importPackage(dependency.packageName.safeFullName()) @@ -76,7 +75,7 @@ abstract class AModelToKotlinCommonGenerator( private fun FileDeclaration.generateInternalKotlinFile(): FileGenerator { currentPackage = packageName - return file(logger = logger, explicitApiModeEnabled = explicitApiModeEnabled) { + return file(config) { filename = this@generateInternalKotlinFile.name packageName = this@generateInternalKotlinFile.packageName.safeFullName() packagePath = @@ -106,7 +105,7 @@ abstract class AModelToKotlinCommonGenerator( type.dec.value.name.safeFullName() } - is FieldType.Enum -> type.dec.name.safeFullName() + is FieldType.Enum -> type.dec.value.name.safeFullName() is FieldType.OneOf -> type.dec.name.safeFullName() @@ -118,7 +117,7 @@ abstract class AModelToKotlinCommonGenerator( val fqValue = when (val value = type.value) { is FieldType.Message -> value.dec.value.name is FieldType.IntegralType -> value.fqName - is FieldType.Enum -> value.dec.name + is FieldType.Enum -> value.dec.value.name else -> error("Unsupported type: $value") } @@ -137,7 +136,7 @@ abstract class AModelToKotlinCommonGenerator( val fqValue = when (val value = entry.value) { is FieldType.Message -> value.dec.value.name is FieldType.IntegralType -> value.fqName - is FieldType.Enum -> value.dec.name + is FieldType.Enum -> value.dec.value.name else -> error("Unsupported type: $value") } diff --git a/protoc-gen/common/src/main/kotlin/kotlinx/rpc/protoc/gen/core/CodeGenerator.kt b/protoc-gen/common/src/main/kotlin/kotlinx/rpc/protoc/gen/core/CodeGenerator.kt index ef80a99cf..cfd63296b 100644 --- a/protoc-gen/common/src/main/kotlin/kotlinx/rpc/protoc/gen/core/CodeGenerator.kt +++ b/protoc-gen/common/src/main/kotlin/kotlinx/rpc/protoc/gen/core/CodeGenerator.kt @@ -4,14 +4,14 @@ package kotlinx.rpc.protoc.gen.core -import org.slf4j.Logger -import org.slf4j.helpers.NOPLogger +@DslMarker +annotation class CodegenDsl +@CodegenDsl open class CodeGenerator( private val indent: String, private val builder: StringBuilder = StringBuilder(), - protected val logger: Logger = NOPLogger.NOP_LOGGER, - protected val explicitApiModeEnabled: Boolean, + val config: Config, ) { private var isEmpty: Boolean = true private var result: String? = null @@ -60,7 +60,7 @@ open class CodeGenerator( _append(value) } - private fun addLine(value: String? = null) { + fun addLine(value: String? = null) { _append("$indent${value ?: ""}", newLineIfAbsent = true) } @@ -68,8 +68,12 @@ open class CodeGenerator( _append(newLineBefore = true) } + fun blankLine() { + _append(newLineBefore = true, newLineAfter = true) + } + private fun withNextIndent(block: CodeGenerator.() -> Unit) { - CodeGenerator("$indent$ONE_INDENT", builder, logger, explicitApiModeEnabled).block() + CodeGenerator("$indent$ONE_INDENT", builder, config).block() } fun scope( @@ -140,8 +144,7 @@ open class CodeGenerator( val nested = CodeGenerator( indent = "$indent$ONE_INDENT", - logger = logger, - explicitApiModeEnabled = explicitApiModeEnabled, + config = config, ).apply(block) if (nested.isEmpty) { @@ -169,6 +172,7 @@ open class CodeGenerator( fun property( name: String, + comment: Comment? = null, modifiers: String = "", contextReceiver: String = "", annotations: List = emptyList(), @@ -179,6 +183,7 @@ open class CodeGenerator( needsNewLineAfterDeclaration: Boolean = true, block: (CodeGenerator.() -> Unit)? = null, ) { + appendComment(comment) for (annotation in annotations) { addLine(annotation) } @@ -207,6 +212,7 @@ open class CodeGenerator( fun function( name: String, + comment: Comment? = null, modifiers: String = "", typeParameters: String = "", args: String = "", @@ -215,6 +221,7 @@ open class CodeGenerator( returnType: String, block: (CodeGenerator.() -> Unit)? = null, ) { + appendComment(comment) for (annotation in annotations) { addLine(annotation) } @@ -232,6 +239,7 @@ open class CodeGenerator( @JvmName("clazz_no_constructorArgs") fun clazz( name: String, + comment: Comment? = null, modifiers: String = "", superTypes: List = emptyList(), annotations: List = emptyList(), @@ -240,6 +248,7 @@ open class CodeGenerator( ) { clazz( name = name, + comment = comment, modifiers = modifiers, constructorArgs = emptyList(), superTypes = superTypes, @@ -252,6 +261,7 @@ open class CodeGenerator( @JvmName("clazz_constructorArgs_no_default") fun clazz( name: String, + comment: Comment? = null, modifiers: String = "", constructorArgs: List = emptyList(), superTypes: List = emptyList(), @@ -261,6 +271,7 @@ open class CodeGenerator( ) { clazz( name = name, + comment = comment, modifiers = modifiers, constructorArgs = constructorArgs.map { it to null }, superTypes = superTypes, @@ -272,6 +283,7 @@ open class CodeGenerator( fun clazz( name: String, + comment: Comment? = null, modifiers: String = "", constructorModifiers: String = "", constructorArgs: List> = emptyList(), @@ -280,6 +292,7 @@ open class CodeGenerator( declarationType: DeclarationType = DeclarationType.Class, block: (CodeGenerator.() -> Unit)? = null, ) { + appendComment(comment) for (annotation in annotations) { addLine(annotation) } @@ -297,7 +310,7 @@ open class CodeGenerator( val constructorArgsTransformed = constructorArgs.map { (arg, default) -> val defaultString = default?.let { " = $it" } ?: "" val modifierString = when { - !explicitApiModeEnabled -> "" + !config.explicitApiModeEnabled -> "" arg.contains("val") || arg.contains("var") -> when { modifiers.contains("internal") || @@ -365,7 +378,7 @@ open class CodeGenerator( } fun String.withVisibility(): String { - return if (explicitApiModeEnabled) { + return if (config.explicitApiModeEnabled) { when { contains("public") -> this contains("protected") -> this @@ -378,19 +391,65 @@ open class CodeGenerator( } } - companion object { - private const val ONE_INDENT = " " + fun appendComments(comments: List) { + val filteredComments = comments.filter { !it.isEmpty() } + + filteredComments.forEachIndexed { index, comment -> + appendComment(comment, first = index == 0, final = index == filteredComments.lastIndex) + } } + + fun appendComment(comment: Comment?, first: Boolean = true, final: Boolean = true) { + if (!config.generateComments || comment == null || comment.isEmpty()) { + return + } + + val leadingDetached = comment.leadingDetached + val leading = comment.leading + val trailing = comment.trailing + + if (first) { + addLine("/**") + } else { + addLine("* ") + } + + leadingDetached.forEach { + addLine("* $it") + } + + if (leadingDetached.isNotEmpty() && (leading.isNotEmpty() || trailing.isNotEmpty())) { + addLine("* ") + } + leading.forEach { + addLine("* $it") + } + + if ((leadingDetached.isNotEmpty() || leading.isNotEmpty()) && trailing.isNotEmpty()) { + addLine("* ") + } + trailing.forEach { + addLine("* $it") + } + + if (final) { + addLine("*/") + } + } + + @Suppress("PrivatePropertyName") + private val ONE_INDENT = " ".repeat(config.indentSize) } +@CodegenDsl class FileGenerator( var filename: String? = null, var packageName: String? = null, var packagePath: String? = packageName, + var comments: List = emptyList(), var fileOptIns: List = emptyList(), - logger: Logger = NOPLogger.NOP_LOGGER, - explicitApiModeEnabled: Boolean, -) : CodeGenerator("", logger = logger, explicitApiModeEnabled = explicitApiModeEnabled) { + config: Config, +) : CodeGenerator("", config = config) { private val imports = mutableListOf() fun importPackage(name: String) { @@ -404,44 +463,55 @@ class FileGenerator( } override fun build(): String { - val sortedImports = imports.toSortedSet() - val prefix = buildString { - if (fileOptIns.isNotEmpty()) { - appendLine("@file:OptIn(${fileOptIns.joinToString(", ")})") - newLine() + val builder = CodeGenerator( + indent = "", + config = config, + ).apply { + val sortedImports = this@FileGenerator.imports.toSortedSet() + + if (config.generateFileLevelComments) { + appendComments(this@FileGenerator.comments) + } + + if (config.generateComments && config.generateFileLevelComments && this@FileGenerator.comments.any { !it.isEmpty() }) { + blankLine() } - val packageName = packageName + if (this@FileGenerator.fileOptIns.isNotEmpty()) { + addLine("@file:OptIn(${this@FileGenerator.fileOptIns.joinToString(", ")})") + } + + val packageName = this@FileGenerator.packageName if (packageName != null && packageName.isNotEmpty()) { - appendLine("package $packageName") + addLine("package $packageName") } - appendLine() + if (this@FileGenerator.fileOptIns.isNotEmpty() || packageName != null && packageName.isNotEmpty()) { + blankLine() + } for (import in sortedImports) { - appendLine("import $import") + addLine("import $import") } - if (imports.isNotEmpty()) { - appendLine() + if (this@FileGenerator.imports.isNotEmpty()) { + blankLine() } } - return prefix + super.build() + return builder.build() + super.build() } } fun file( + config: Config, name: String? = null, packageName: String? = null, - logger: Logger = NOPLogger.NOP_LOGGER, - explicitApiModeEnabled: Boolean, block: FileGenerator.() -> Unit, ): FileGenerator = FileGenerator( filename = name, packageName = packageName, packagePath = packageName, fileOptIns = emptyList(), - logger = logger, - explicitApiModeEnabled = explicitApiModeEnabled, + config = config, ).apply(block) diff --git a/protoc-gen/common/src/main/kotlin/kotlinx/rpc/protoc/gen/core/ProtocGenPlugin.kt b/protoc-gen/common/src/main/kotlin/kotlinx/rpc/protoc/gen/core/ProtocGenPlugin.kt index fa59e88e0..39b6fc3ff 100644 --- a/protoc-gen/common/src/main/kotlin/kotlinx/rpc/protoc/gen/core/ProtocGenPlugin.kt +++ b/protoc-gen/common/src/main/kotlin/kotlinx/rpc/protoc/gen/core/ProtocGenPlugin.kt @@ -19,14 +19,26 @@ import org.slf4j.LoggerFactory import org.slf4j.helpers.NOPLogger import java.io.File +@Volatile +lateinit var globalLogger: Logger + +class Config( + val explicitApiModeEnabled: Boolean, + val generateComments: Boolean, + val generateFileLevelComments: Boolean, + val indentSize: Int, +) + abstract class ProtocGenPlugin { companion object { private const val DEBUG_OUTPUT_OPTION = "debugOutput" private const val EXPLICIT_API_MODE_ENABLED_OPTION = "explicitApiModeEnabled" + private const val GENERATE_COMMENTS_OPTION = "generateComments" + private const val GENERATE_FILE_LEVEL_COMMENTS_OPTION = "generateFileLevelComments" + private const val INDENT_SIZE_OPTION = "indentSize" } private var debugOutput: String? = null - private var explicitApiModeEnabled: Boolean = false private val logger: Logger by lazy { val debugOutput = debugOutput ?: return@lazy NOPLogger.NOP_LOGGER @@ -59,9 +71,22 @@ abstract class ProtocGenPlugin { } debugOutput = parameters[DEBUG_OUTPUT_OPTION] - explicitApiModeEnabled = parameters[EXPLICIT_API_MODE_ENABLED_OPTION]?.toBooleanStrictOrNull() ?: false + val explicitApiModeEnabled = parameters[EXPLICIT_API_MODE_ENABLED_OPTION]?.toBooleanStrictOrNull() ?: false + + val generateComments = parameters[GENERATE_COMMENTS_OPTION]?.toBooleanStrictOrNull() ?: true + val generateFileLevelComments = parameters[GENERATE_FILE_LEVEL_COMMENTS_OPTION]?.toBooleanStrictOrNull() ?: true + + val indentSize = parameters[INDENT_SIZE_OPTION]?.toIntOrNull() ?: 4 + + val config = Config( + explicitApiModeEnabled = explicitApiModeEnabled, + generateComments = generateComments, + generateFileLevelComments = generateFileLevelComments, + indentSize = indentSize, + ) - val files = input.runGeneration() + globalLogger = logger + val files = input.runGeneration(config) .map { file -> CodeGeneratorResponse.File.newBuilder() .apply { @@ -95,17 +120,19 @@ abstract class ProtocGenPlugin { .build() } - private fun CodeGeneratorRequest.runGeneration(): List { - return generateKotlinByModel( - model = this.toModel(), - logger = logger, - explicitApiModeEnabled = explicitApiModeEnabled, - ) + private fun CodeGeneratorRequest.runGeneration(config: Config): List { + return try { + generateKotlinByModel( + config = config, + model = this.toModel(), + ) + } finally { + (logger as ch.qos.logback.classic.Logger).detachAndStopAllAppenders() + } } protected abstract fun generateKotlinByModel( + config: Config, model: Model, - logger: Logger, - explicitApiModeEnabled: Boolean, ): List } diff --git a/protoc-gen/common/src/main/kotlin/kotlinx/rpc/protoc/gen/core/codeRequestToModel.kt b/protoc-gen/common/src/main/kotlin/kotlinx/rpc/protoc/gen/core/codeRequestToModel.kt index a40b3d191..5f890fb77 100644 --- a/protoc-gen/common/src/main/kotlin/kotlinx/rpc/protoc/gen/core/codeRequestToModel.kt +++ b/protoc-gen/common/src/main/kotlin/kotlinx/rpc/protoc/gen/core/codeRequestToModel.kt @@ -17,6 +17,7 @@ import kotlinx.rpc.protoc.gen.core.model.MethodDeclaration import kotlinx.rpc.protoc.gen.core.model.Model import kotlinx.rpc.protoc.gen.core.model.OneOfDeclaration import kotlinx.rpc.protoc.gen.core.model.ServiceDeclaration +import kotlin.collections.plus private val nameCache = mutableMapOf() private val modelCache = mutableMapOf() @@ -101,7 +102,7 @@ fun Descriptors.GenericDescriptor.fqName(): FqName { * Caches the `descriptor.toModel()` result in the [modelCache] to ensure that only a single object * per descriptor exists. */ -private inline fun D.cached(block: (D) -> T): T +private inline fun D.cached(crossinline block: (D) -> T): T where D : Descriptors.GenericDescriptor, T : Any { if (modelCache.containsKey(this)) { return modelCache[this] as T @@ -112,130 +113,149 @@ private inline fun D.cached(block: (D) -> T): T } private fun Descriptors.FileDescriptor.toModel(): FileDeclaration = cached { - return FileDeclaration( + val comments = Comments(extractComments(), ObjectPath.empty) + + FileDeclaration( name = kotlinFileName(), packageName = FqName.Package.fromString(kotlinPackage()), dependencies = dependencies.map { it.toModel() }, - messageDeclarations = messageTypes.map { it.toModel() }, - enumDeclarations = enumTypes.map { it.toModel() }, - serviceDeclarations = services.map { it.toModel() }, - doc = null, + messageDeclarations = messageTypes.map { it.toModel(comments + Paths.messageCommentPath + it.index) }, + enumDeclarations = enumTypes.map { it.toModel(comments + Paths.enumCommentPath + it.index) }, + serviceDeclarations = services.map { it.toModel(comments + Paths.serviceCommentPath + it.index) }, + doc = listOfNotNull( + (comments + Paths.syntaxCommentPath).get(), + (comments + Paths.editionsCommentPath).get(), + (comments + Paths.packageCommentPath).get() + ), dec = this, ) } -private fun Descriptors.Descriptor.toModel(): MessageDeclaration = cached { +private fun Descriptors.Descriptor.toModel(comments: Comments?): MessageDeclaration = cached { + requireNotNull(comments) { + "Comments are missing for message declaration: ${fqName()}" + } + var currPresenceIdx = 0 var regularFields = fields // only fields that are not part of a oneOf declaration .filter { field -> field.realContainingOneof == null } .map { val presenceIdx = if (it.hasPresence()) currPresenceIdx++ else null - it.toModel(presenceIdx = presenceIdx) + it.toModel(comments + Paths.messageFieldCommentPath + it.index, presenceIdx = presenceIdx) } - val oneOfs = oneofs.filter { it.fields[0].realContainingOneof != null }.map { it.toModel() } + + val oneOfs = oneofs + .filter { it.fields[0].realContainingOneof != null } + .map { it.toModel(comments) } regularFields = regularFields + oneOfs.map { FieldDeclaration( // TODO: Proper handling of this field name - it.name.simpleName.decapitalize(), - FieldType.OneOf(it), - doc = null, + name = it.name.simpleName.decapitalize(), + type = FieldType.OneOf(it), + doc = it.doc, dec = it.variants.first().dec, ) } - return MessageDeclaration( + MessageDeclaration( name = fqName(), presenceMaskSize = currPresenceIdx, actualFields = regularFields, // get all oneof declarations that are not created from an optional in proto3 https://github.com/googleapis/api-linter/issues/1323 oneOfDeclarations = oneOfs, - enumDeclarations = enumTypes.map { it.toModel() }, - nestedDeclarations = nestedTypes.map { it.toModel() }, - doc = null, - dec = this, - ) -} - -private fun Descriptors.FieldDescriptor.toModel(presenceIdx: Int? = null): FieldDeclaration = cached { - toProto().hasProto3Optional() - return FieldDeclaration( - name = fqName().simpleName, - type = modelType(), - presenceIdx = presenceIdx, - doc = null, + enumDeclarations = enumTypes.map { it.toModel(comments + Paths.messageEnumCommentPath + it.index) }, + nestedDeclarations = nestedTypes.map { it.toModel(comments + Paths.messageMessageCommentPath + it.index) }, + doc = comments.get(), dec = this, ) } +private fun Descriptors.FieldDescriptor.toModel(comments: Comments, presenceIdx: Int? = null): FieldDeclaration = + cached { + FieldDeclaration( + name = fqName().simpleName, + type = modelType(), + presenceIdx = presenceIdx, + doc = comments.get(), + dec = this, + ) + } -private fun Descriptors.OneofDescriptor.toModel(): OneOfDeclaration = cached { - return OneOfDeclaration( +private fun Descriptors.OneofDescriptor.toModel(parentComments: Comments): OneOfDeclaration = cached { + OneOfDeclaration( name = fqName(), - variants = fields.map { it.toModel() }, + variants = fields.map { it.toModel(parentComments + Paths.messageFieldCommentPath + it.index) }, + doc = (parentComments + Paths.messageOneOfCommentPath + index).get(), dec = this, ) } -private fun Descriptors.EnumDescriptor.toModel(): EnumDeclaration = cached { +private fun Descriptors.EnumDescriptor.toModel(comments: Comments?): EnumDeclaration = cached { + requireNotNull(comments) { + "Comments are missing for enum declaration: ${fqName()}" + } + val entriesMap = mutableMapOf() val aliases = mutableListOf() values.forEach { value -> if (entriesMap.containsKey(value.number)) { val original = entriesMap.getValue(value.number) - aliases.add(value.toAliasModel(original)) + aliases.add(value.toAliasModel(comments + Paths.enumValueCommentPath + value.index, original)) } else { - entriesMap[value.number] = value.toModel() + entriesMap[value.number] = value.toModel(comments + Paths.enumValueCommentPath + value.index) } } if (!options.allowAlias && aliases.isNotEmpty()) { - error("Enum ${fullName} has aliases: ${aliases.joinToString { it.name.simpleName }}") + error("Enum $fullName can't have aliases in current proto config: ${aliases.joinToString { it.name.simpleName }}") } - return EnumDeclaration( + EnumDeclaration( name = fqName(), originalEntries = entriesMap.values.toList(), aliases = aliases, - doc = null, + doc = comments.get(), dec = this, ) } -private fun Descriptors.EnumValueDescriptor.toModel(): EnumDeclaration.Entry = cached { - return EnumDeclaration.Entry( +private fun Descriptors.EnumValueDescriptor.toModel(comments: Comments): EnumDeclaration.Entry = cached { + EnumDeclaration.Entry( name = fqName(), - doc = null, + doc = comments.get(), dec = this, ) } // no caching, as it would conflict with .toModel -private fun Descriptors.EnumValueDescriptor.toAliasModel(original: EnumDeclaration.Entry): EnumDeclaration.Alias { - return EnumDeclaration.Alias( +private fun Descriptors.EnumValueDescriptor.toAliasModel(enumComments: Comments, original: EnumDeclaration.Entry): EnumDeclaration.Alias = cached { + EnumDeclaration.Alias( name = fqName(), original = original, - doc = null, + doc = enumComments.get(), dec = this, ) } -private fun Descriptors.ServiceDescriptor.toModel(): ServiceDeclaration = cached { - return ServiceDeclaration( +private fun Descriptors.ServiceDescriptor.toModel(comments: Comments): ServiceDeclaration = cached { + ServiceDeclaration( name = fqName(), - methods = methods.map { it.toModel() }, + methods = methods.map { it.toModel(comments + Paths.serviceMethodCommentPath + it.index) }, dec = this, + doc = comments.get(), ) } -private fun Descriptors.MethodDescriptor.toModel(): MethodDeclaration = cached { - return MethodDeclaration( +private fun Descriptors.MethodDescriptor.toModel(comments: Comments): MethodDeclaration = cached { + MethodDeclaration( name = name, - inputType = inputType.toModel(), - outputType = outputType.toModel(), + inputType = lazy { inputType.toModel(null) }, + outputType = lazy { outputType.toModel(null) }, dec = this, + doc = comments.get(), ) } @@ -258,15 +278,15 @@ private fun Descriptors.FieldDescriptor.modelType(): FieldType { Descriptors.FieldDescriptor.Type.SFIXED64 -> FieldType.IntegralType.SFIXED64 Descriptors.FieldDescriptor.Type.SINT32 -> FieldType.IntegralType.SINT32 Descriptors.FieldDescriptor.Type.SINT64 -> FieldType.IntegralType.SINT64 - Descriptors.FieldDescriptor.Type.ENUM -> FieldType.Enum(enumType.toModel()) - Descriptors.FieldDescriptor.Type.MESSAGE -> FieldType.Message(lazy { messageType!!.toModel() }) - Descriptors.FieldDescriptor.Type.GROUP -> FieldType.Message(lazy { messageType!!.toModel() }) + Descriptors.FieldDescriptor.Type.ENUM -> FieldType.Enum(lazy { enumType.toModel(null) }) + Descriptors.FieldDescriptor.Type.MESSAGE -> FieldType.Message(lazy { messageType!!.toModel(null) }) + Descriptors.FieldDescriptor.Type.GROUP -> FieldType.Message(lazy { messageType!!.toModel(null) }) } if (isMapField) { val keyType = messageType.findFieldByName("key").modelType() val valType = messageType.findFieldByName("value").modelType() - val mapEntryDec = messageType.toModel() + val mapEntryDec = lazy { messageType.toModel(null) } val mapEntry = FieldType.Map.Entry(mapEntryDec, keyType, valType) return FieldType.Map(mapEntry) } diff --git a/protoc-gen/common/src/main/kotlin/kotlinx/rpc/protoc/gen/core/comments.kt b/protoc-gen/common/src/main/kotlin/kotlinx/rpc/protoc/gen/core/comments.kt new file mode 100644 index 000000000..a016ea546 --- /dev/null +++ b/protoc-gen/common/src/main/kotlin/kotlinx/rpc/protoc/gen/core/comments.kt @@ -0,0 +1,102 @@ +/* + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.rpc.protoc.gen.core + +import com.google.protobuf.Descriptors + +@Suppress("ConstPropertyName") +object Paths { + // tag numbers in FileDescriptorProto + const val packageCommentPath = 2 + const val messageCommentPath = 4 + const val enumCommentPath = 5 + const val serviceCommentPath = 6 + const val extensionCommentPath = 7 + const val syntaxCommentPath = 12 + const val editionsCommentPath = 14 + + // tag numbers in DescriptorProto + const val messageFieldCommentPath = 2 // field + const val messageMessageCommentPath = 3 // nested_type + const val messageEnumCommentPath = 4 // enum_type + const val messageExtensionCommentPath = 6 // extension + const val messageOneOfCommentPath = 8 // oneof_decl + + // tag numbers in EnumDescriptorProto + const val enumValueCommentPath = 2 // value + + // tag numbers in ServiceDescriptorProto + const val serviceMethodCommentPath = 2 +} + +class ObjectPath( + val path: List, +) { + override fun toString(): String { + return path.joinToString(".") + } + + operator fun plus(pathElement: Int): ObjectPath { + return ObjectPath(path + pathElement) + } + + companion object { + val empty = ObjectPath(emptyList()) + } +} + +fun Map.at(path: Int): Comment? { + return this[path.toString()] +} + +fun Map.at(path: ObjectPath): Comment? { + return this[path.toString()] +} + +class Comments( + val comments: Map, + val parent: ObjectPath, +) { + fun get(): Comment? { + return comments.at(parent) + } +} + +operator fun Comments.plus(path: Int): Comments { + return this.run { Comments(comments, parent + path) } +} + +class Comment( + leadingDetached: List, + leading: List, + trailing: List, +) { + val leadingDetached: List = leadingDetached.protoCommentsToKotlin() + val leading: List = leading.protoCommentsToKotlin() + val trailing: List = trailing.protoCommentsToKotlin() + + fun isEmpty(): Boolean { + return leadingDetached.isEmpty() && + leading.isEmpty() && + trailing.isEmpty() + } +} + +fun Descriptors.FileDescriptor.extractComments(): Map { + return toProto().sourceCodeInfo.locationList.associate { + val leading = it.leadingComments ?: "" + val trailing = it.trailingComments ?: "" + val detached = it.leadingDetachedCommentsList.toList() + it.pathList.joinToString(".") to Comment(detached, listOf(leading), listOf(trailing)) + } +} + +private fun List.protoCommentsToKotlin(): List { + return flatMap { it.protoCommentToKotlin() }.dropLastWhile { it.isBlank() } +} + +private fun String.protoCommentToKotlin(): List { + return split("\n", "\r\n").map { it.trimEnd().removePrefix(" ") } +} diff --git a/protoc-gen/common/src/main/kotlin/kotlinx/rpc/protoc/gen/core/model/FieldType.kt b/protoc-gen/common/src/main/kotlin/kotlinx/rpc/protoc/gen/core/model/FieldType.kt index 541a959d2..de0f0b835 100644 --- a/protoc-gen/common/src/main/kotlin/kotlinx/rpc/protoc/gen/core/model/FieldType.kt +++ b/protoc-gen/common/src/main/kotlin/kotlinx/rpc/protoc/gen/core/model/FieldType.kt @@ -21,7 +21,7 @@ sealed interface FieldType { data class List(val value: FieldType) : FieldType { override val defaultValue: String = "mutableListOf()" - override val wireType: WireType = value.wireType + override val wireType: WireType by lazy { value.wireType } override val isPackable: Boolean = value.isPackable } @@ -29,11 +29,11 @@ sealed interface FieldType { override val defaultValue: String = "mutableMapOf()" override val wireType: WireType = WireType.LENGTH_DELIMITED - data class Entry(val dec: MessageDeclaration, val key: FieldType, val value: FieldType) + data class Entry(val dec: Lazy, val key: FieldType, val value: FieldType) } - data class Enum(val dec: EnumDeclaration) : FieldType { - override val defaultValue = dec.defaultEntry().name.fullName() + data class Enum(val dec: Lazy) : FieldType { + override val defaultValue by lazy { dec.value.defaultEntry().name.fullName() } override val wireType: WireType = WireType.VARINT override val isPackable: Boolean = true } diff --git a/protoc-gen/common/src/main/kotlin/kotlinx/rpc/protoc/gen/core/model/model.kt b/protoc-gen/common/src/main/kotlin/kotlinx/rpc/protoc/gen/core/model/model.kt index f830e1bd0..07933ae67 100644 --- a/protoc-gen/common/src/main/kotlin/kotlinx/rpc/protoc/gen/core/model/model.kt +++ b/protoc-gen/common/src/main/kotlin/kotlinx/rpc/protoc/gen/core/model/model.kt @@ -5,6 +5,7 @@ package kotlinx.rpc.protoc.gen.core.model import com.google.protobuf.Descriptors +import kotlinx.rpc.protoc.gen.core.Comment data class Model( val files: List, @@ -17,7 +18,7 @@ data class FileDeclaration( val messageDeclarations: List, val enumDeclarations: List, val serviceDeclarations: List, - val doc: String?, + val doc: List, val dec: Descriptors.FileDescriptor, ) @@ -28,7 +29,7 @@ data class MessageDeclaration( val oneOfDeclarations: List, val enumDeclarations: List, val nestedDeclarations: List, - val doc: String?, + val doc: Comment?, val dec: Descriptors.Descriptor, ) { val isMapEntry = dec.options.mapEntry @@ -46,7 +47,7 @@ data class EnumDeclaration( val name: FqName, val originalEntries: List, val aliases: List, - val doc: String?, + val doc: Comment?, val dec: Descriptors.EnumDescriptor, ) { @@ -60,14 +61,14 @@ data class EnumDeclaration( data class Entry( val name: FqName, - val doc: String?, + val doc: Comment?, val dec: Descriptors.EnumValueDescriptor, ) data class Alias( val name: FqName, val original: Entry, - val doc: String?, + val doc: Comment?, val dec: Descriptors.EnumValueDescriptor, ) } @@ -76,12 +77,13 @@ data class OneOfDeclaration( val name: FqName, val variants: List, val dec: Descriptors.OneofDescriptor, + val doc: Comment?, ) data class FieldDeclaration( val name: String, val type: FieldType, - val doc: String?, + val doc: Comment?, val dec: Descriptors.FieldDescriptor, // defines the index in the presenceMask of the Message. // this cannot be the number, as only fields with hasPresence == true are part of the presenceMask @@ -108,12 +110,14 @@ data class ServiceDeclaration( val name: FqName, val methods: List, val dec: Descriptors.ServiceDescriptor, + val doc: Comment?, ) data class MethodDeclaration( val name: String, - val inputType: MessageDeclaration, - val outputType: MessageDeclaration, + val inputType: Lazy, + val outputType: Lazy, val dec: Descriptors.MethodDescriptor, + val doc: Comment?, ) diff --git a/protoc-gen/grpc/src/main/kotlin/kotlinx/rpc/protoc/gen/grpc/GrpcProtocGenPlugin.kt b/protoc-gen/grpc/src/main/kotlin/kotlinx/rpc/protoc/gen/grpc/GrpcProtocGenPlugin.kt index a42a354ff..f68245a5f 100644 --- a/protoc-gen/grpc/src/main/kotlin/kotlinx/rpc/protoc/gen/grpc/GrpcProtocGenPlugin.kt +++ b/protoc-gen/grpc/src/main/kotlin/kotlinx/rpc/protoc/gen/grpc/GrpcProtocGenPlugin.kt @@ -4,6 +4,7 @@ package kotlinx.rpc.protoc.gen.grpc +import kotlinx.rpc.protoc.gen.core.Config import kotlinx.rpc.protoc.gen.core.FileGenerator import kotlinx.rpc.protoc.gen.core.ProtocGenPlugin import kotlinx.rpc.protoc.gen.core.model.Model @@ -11,10 +12,9 @@ import org.slf4j.Logger object GrpcProtocGenPlugin : ProtocGenPlugin() { override fun generateKotlinByModel( + config: Config, model: Model, - logger: Logger, - explicitApiModeEnabled: Boolean, ): List { - return ModelToGrpcKotlinCommonGenerator(model, logger, explicitApiModeEnabled).generateKotlinFiles() + return ModelToGrpcKotlinCommonGenerator(config, model).generateKotlinFiles() } } diff --git a/protoc-gen/grpc/src/main/kotlin/kotlinx/rpc/protoc/gen/grpc/ModelToGrpcKotlinCommonGenerator.kt b/protoc-gen/grpc/src/main/kotlin/kotlinx/rpc/protoc/gen/grpc/ModelToGrpcKotlinCommonGenerator.kt index dfb2f5c00..6acf1e4e2 100644 --- a/protoc-gen/grpc/src/main/kotlin/kotlinx/rpc/protoc/gen/grpc/ModelToGrpcKotlinCommonGenerator.kt +++ b/protoc-gen/grpc/src/main/kotlin/kotlinx/rpc/protoc/gen/grpc/ModelToGrpcKotlinCommonGenerator.kt @@ -7,17 +7,16 @@ package kotlinx.rpc.protoc.gen.grpc import com.google.protobuf.DescriptorProtos import kotlinx.rpc.protoc.gen.core.AModelToKotlinCommonGenerator import kotlinx.rpc.protoc.gen.core.CodeGenerator +import kotlinx.rpc.protoc.gen.core.Config import kotlinx.rpc.protoc.gen.core.model.FileDeclaration import kotlinx.rpc.protoc.gen.core.model.FqName import kotlinx.rpc.protoc.gen.core.model.Model import kotlinx.rpc.protoc.gen.core.model.ServiceDeclaration -import org.slf4j.Logger class ModelToGrpcKotlinCommonGenerator( + config: Config, model: Model, - logger: Logger, - explicitApiModeEnabled: Boolean, -) : AModelToKotlinCommonGenerator(model, logger, explicitApiModeEnabled) { +) : AModelToKotlinCommonGenerator(config, model) { override val FileDeclaration.hasPublicGeneratedContent: Boolean get() = serviceDeclarations.isNotEmpty() override val FileDeclaration.hasInternalGeneratedContent: Boolean get() = false @@ -35,6 +34,7 @@ class ModelToGrpcKotlinCommonGenerator( clazz( name = service.name.simpleName, + comment = service.doc, declarationType = CodeGenerator.DeclarationType.Interface, annotations = listOf("@kotlinx.rpc.grpc.annotations.Grpc$annotationParams") ) { @@ -49,10 +49,11 @@ class ModelToGrpcKotlinCommonGenerator( function( name = method.name, + comment = method.doc, modifiers = if (method.dec.isServerStreaming) "" else "suspend", - args = "message: ${inputType.name.safeFullName().wrapInFlowIf(method.dec.isClientStreaming)}", + args = "message: ${inputType.value.name.safeFullName().wrapInFlowIf(method.dec.isClientStreaming)}", annotations = annotations, - returnType = outputType.name.safeFullName().wrapInFlowIf(method.dec.isServerStreaming), + returnType = outputType.value.name.safeFullName().wrapInFlowIf(method.dec.isServerStreaming), ) } } diff --git a/protoc-gen/protobuf/src/main/kotlin/kotlinx/rpc/protoc/gen/ModelToProtobufKotlinCommonGenerator.kt b/protoc-gen/protobuf/src/main/kotlin/kotlinx/rpc/protoc/gen/ModelToProtobufKotlinCommonGenerator.kt index 14e3b974d..fd1c9404e 100644 --- a/protoc-gen/protobuf/src/main/kotlin/kotlinx/rpc/protoc/gen/ModelToProtobufKotlinCommonGenerator.kt +++ b/protoc-gen/protobuf/src/main/kotlin/kotlinx/rpc/protoc/gen/ModelToProtobufKotlinCommonGenerator.kt @@ -10,6 +10,7 @@ import com.google.protobuf.ByteString import com.google.protobuf.Descriptors import kotlinx.rpc.protoc.gen.core.AModelToKotlinCommonGenerator import kotlinx.rpc.protoc.gen.core.CodeGenerator +import kotlinx.rpc.protoc.gen.core.Config import kotlinx.rpc.protoc.gen.core.INTERNAL_RPC_API_ANNO import kotlinx.rpc.protoc.gen.core.PB_PKG import kotlinx.rpc.protoc.gen.core.WITH_CODEC_ANNO @@ -23,13 +24,11 @@ import kotlinx.rpc.protoc.gen.core.model.Model import kotlinx.rpc.protoc.gen.core.model.OneOfDeclaration import kotlinx.rpc.protoc.gen.core.model.WireType import kotlinx.rpc.protoc.gen.core.model.scalarDefaultSuffix -import org.slf4j.Logger class ModelToProtobufKotlinCommonGenerator( + config: Config, model: Model, - logger: Logger, - explicitApiModeEnabled: Boolean, -) : AModelToKotlinCommonGenerator(model, logger, explicitApiModeEnabled) { +) : AModelToKotlinCommonGenerator(config, model) { override val FileDeclaration.hasPublicGeneratedContent: Boolean get() = enumDeclarations.isNotEmpty() || messageDeclarations.isNotEmpty() @@ -79,12 +78,14 @@ class ModelToProtobufKotlinCommonGenerator( clazz( name = declaration.name.simpleName, + comment = declaration.doc, declarationType = CodeGenerator.DeclarationType.Interface, annotations = annotations ) { declaration.actualFields.forEachIndexed { i, field -> property( name = field.name, + comment = field.doc, type = field.typeFqName(), needsNewLineAfterDeclaration = i == declaration.actualFields.lastIndex, ) @@ -193,7 +194,7 @@ class ModelToProtobufKotlinCommonGenerator( return } - clazz("PresenceIndices", "private", declarationType = CodeGenerator.DeclarationType.Object) { + clazz("PresenceIndices", modifiers = "private", declarationType = CodeGenerator.DeclarationType.Object) { val fieldDeclarations = declaration.actualFields.filter { it.presenceIdx != null } fieldDeclarations.forEachIndexed { i, field -> property( @@ -219,7 +220,7 @@ class ModelToProtobufKotlinCommonGenerator( return } - clazz("BytesDefaults", "private", declarationType = CodeGenerator.DeclarationType.Object) { + clazz("BytesDefaults", modifiers = "private", declarationType = CodeGenerator.DeclarationType.Object) { fieldDeclarations.forEachIndexed { i, field -> val value = if (field.dec.hasDefaultValue()) { val stringValue = (field.dec.defaultValue as ByteString).toString(Charsets.UTF_8) @@ -441,7 +442,7 @@ class ModelToProtobufKotlinCommonGenerator( is FieldType.List -> if (isPacked) { val conversion = if (fieldType.value is FieldType.Enum) { - ".map { ${(fieldType.value as FieldType.Enum).dec.name.safeFullName()}.fromNumber(it) }" + ".map { ${(fieldType.value as FieldType.Enum).dec.value.name.safeFullName()}.fromNumber(it) }" } else { "" } @@ -464,7 +465,7 @@ class ModelToProtobufKotlinCommonGenerator( } is FieldType.Enum -> { - val fromNum = "${fieldType.dec.name.safeFullName()}.fromNumber" + val fromNum = "${fieldType.dec.value.name.safeFullName()}.fromNumber" val raw = "$fromNum(decoder.read${fieldType.decodeEncodeFuncName()}())" code("$lvalue = ${wrapperCtor(raw)}") } @@ -491,10 +492,10 @@ class ModelToProtobufKotlinCommonGenerator( } is FieldType.Map -> { - val entryClassName = fieldType.entry.dec.internalClassFullName() + val entryClassName = fieldType.entry.dec.value.internalClassFullName() scope("with($entryClassName())") { generateDecodeFieldValue( - fieldType = FieldType.Message(lazy { fieldType.entry.dec }), + fieldType = FieldType.Message(fieldType.entry.dec), lvalue = "this", isPacked = false, wrapperCtor = wrapperCtor @@ -608,7 +609,7 @@ class ModelToProtobufKotlinCommonGenerator( scope(".also", paramDecl = "entry ->") { generateEncodeFieldValue( valueVar = "entry", - type = FieldType.Message(lazy { type.entry.dec }), + type = FieldType.Message(type.entry.dec), number = number, isPacked = false, packedWithFixedSize = false, @@ -829,7 +830,7 @@ class ModelToProtobufKotlinCommonGenerator( keyVar: String, valueVar: String, ) { - val entryClass = map.entry.dec.internalClassFullName() + val entryClass = map.entry.dec.value.internalClassFullName() scope("$entryClass().apply", nlAfterClosed = false) { code("key = $keyVar") code("value = $valueVar") @@ -980,10 +981,16 @@ class ModelToProtobufKotlinCommonGenerator( private fun CodeGenerator.generateOneOfPublic(declaration: OneOfDeclaration) { val interfaceName = declaration.name.simpleName - clazz(interfaceName, "sealed", declarationType = CodeGenerator.DeclarationType.Interface) { + clazz( + name = interfaceName, + comment = declaration.doc, + modifiers = "sealed", + declarationType = CodeGenerator.DeclarationType.Interface + ) { declaration.variants.forEach { variant -> clazz( name = variant.name, + comment = variant.doc, modifiers = "value", constructorArgs = listOf("val value: ${variant.typeFqName()}"), annotations = listOf("@JvmInline"), @@ -1002,13 +1009,16 @@ class ModelToProtobufKotlinCommonGenerator( val entriesSorted = declaration.originalEntries.sortedBy { it.dec.number } clazz( - className, "sealed", + name = className, + comment = declaration.doc, + modifiers = "sealed", constructorArgs = listOf("open val number: Int"), ) { declaration.originalEntries.forEach { variant -> clazz( name = variant.name.simpleName, + comment = variant.doc, declarationType = CodeGenerator.DeclarationType.Object, superTypes = listOf("$className(number = ${variant.dec.number})"), ) @@ -1028,6 +1038,7 @@ class ModelToProtobufKotlinCommonGenerator( declaration.aliases.forEach { alias: EnumDeclaration.Alias -> property( name = alias.name.simpleName, + comment = alias.doc, type = className, propertyInitializer = CodeGenerator.PropertyInitializer.GETTER, value = alias.original.name.simpleName, diff --git a/protoc-gen/protobuf/src/main/kotlin/kotlinx/rpc/protoc/gen/ProtobufProtocGenPlugin.kt b/protoc-gen/protobuf/src/main/kotlin/kotlinx/rpc/protoc/gen/ProtobufProtocGenPlugin.kt index 9f73e9930..ccee381c2 100644 --- a/protoc-gen/protobuf/src/main/kotlin/kotlinx/rpc/protoc/gen/ProtobufProtocGenPlugin.kt +++ b/protoc-gen/protobuf/src/main/kotlin/kotlinx/rpc/protoc/gen/ProtobufProtocGenPlugin.kt @@ -4,6 +4,7 @@ package kotlinx.rpc.protoc.gen +import kotlinx.rpc.protoc.gen.core.Config import kotlinx.rpc.protoc.gen.core.FileGenerator import kotlinx.rpc.protoc.gen.core.ProtocGenPlugin import kotlinx.rpc.protoc.gen.core.model.Model @@ -11,10 +12,9 @@ import org.slf4j.Logger object ProtobufProtocGenPlugin : ProtocGenPlugin() { override fun generateKotlinByModel( + config: Config, model: Model, - logger: Logger, - explicitApiModeEnabled: Boolean, ): List { - return ModelToProtobufKotlinCommonGenerator(model, logger, explicitApiModeEnabled).generateKotlinFiles() + return ModelToProtobufKotlinCommonGenerator(config, model).generateKotlinFiles() } } From a8059854a8380869dfec902fb58f0d0b26f68eb4 Mon Sep 17 00:00:00 2001 From: Alexander Sysoev Date: Fri, 19 Sep 2025 19:28:44 +0200 Subject: [PATCH 2/5] Updated generated proto files --- protobuf/protobuf-core/build.gradle.kts | 6 + .../com/google/protobuf/kotlin/Any.kt | 121 ++++++++++- .../com/google/protobuf/kotlin/Api.kt | 164 +++++++++++++- .../com/google/protobuf/kotlin/Duration.kt | 74 ++++++- .../com/google/protobuf/kotlin/Empty.kt | 10 +- .../com/google/protobuf/kotlin/FieldMask.kt | 205 +++++++++++++++++- .../google/protobuf/kotlin/SourceContext.kt | 9 +- .../com/google/protobuf/kotlin/Struct.kt | 57 ++++- .../com/google/protobuf/kotlin/Timestamp.kt | 103 ++++++++- .../com/google/protobuf/kotlin/Type.kt | 196 ++++++++++++++++- .../com/google/protobuf/kotlin/Wrappers.kt | 100 ++++++++- .../protobuf/kotlin/_rpc_internal/Any.kt | 1 - .../protobuf/kotlin/_rpc_internal/Api.kt | 1 - .../protobuf/kotlin/_rpc_internal/Duration.kt | 1 - .../protobuf/kotlin/_rpc_internal/Empty.kt | 1 - .../kotlin/_rpc_internal/FieldMask.kt | 1 - .../kotlin/_rpc_internal/SourceContext.kt | 1 - .../protobuf/kotlin/_rpc_internal/Struct.kt | 1 - .../kotlin/_rpc_internal/Timestamp.kt | 1 - .../protobuf/kotlin/_rpc_internal/Type.kt | 1 - .../protobuf/kotlin/_rpc_internal/Wrappers.kt | 1 - tests/protobuf-conformance/build.gradle.kts | 6 + .../protobuf/conformance/Conformance.kt | 144 +++++++++++- .../conformance/_rpc_internal/Conformance.kt | 1 - .../edition2023/TestMessagesEdition2023.kt | 22 +- .../_rpc_internal/TestMessagesEdition2023.kt | 1 - .../proto2/TestMessagesProto2Editions.kt | 56 ++++- .../TestMessagesProto2Editions.kt | 1 - .../proto3/TestMessagesProto3Editions.kt | 36 ++- .../TestMessagesProto3Editions.kt | 1 - .../proto2/TestMessagesProto2.kt | 56 ++++- .../_rpc_internal/TestMessagesProto2.kt | 1 - .../proto3/TestMessagesProto3.kt | 36 ++- .../_rpc_internal/TestMessagesProto3.kt | 1 - 34 files changed, 1385 insertions(+), 32 deletions(-) diff --git a/protobuf/protobuf-core/build.gradle.kts b/protobuf/protobuf-core/build.gradle.kts index d421c7f0a..500d37acb 100644 --- a/protobuf/protobuf-core/build.gradle.kts +++ b/protobuf/protobuf-core/build.gradle.kts @@ -72,6 +72,12 @@ protoSourceSets { } } +rpc { + protoc.buf.generate.comments { + includeFileLevelComments = false + } +} + configureLocalProtocGenDevelopmentDependency("Main", "Test") val generatedCodeDir = layout.projectDirectory diff --git a/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/Any.kt b/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/Any.kt index a34ae1320..8aed599fa 100644 --- a/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/Any.kt +++ b/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/Any.kt @@ -3,11 +3,130 @@ package com.google.protobuf.kotlin import kotlinx.rpc.internal.utils.* +/** +* `Any` contains an arbitrary serialized protocol buffer message along with a +* URL that describes the type of the serialized message. +* +* Protobuf library provides support to pack/unpack Any values in the form +* of utility functions or additional generated methods of the Any type. +* +* Example 1: Pack and unpack a message in C++. +* +* Foo foo = ...; +* Any any; +* any.PackFrom(foo); +* ... +* if (any.UnpackTo(&foo)) { +* ... +* } +* +* Example 2: Pack and unpack a message in Java. +* +* Foo foo = ...; +* Any any = Any.pack(foo); +* ... +* if (any.is(Foo.class)) { +* foo = any.unpack(Foo.class); +* } +* // or ... +* if (any.isSameTypeAs(Foo.getDefaultInstance())) { +* foo = any.unpack(Foo.getDefaultInstance()); +* } +* +* Example 3: Pack and unpack a message in Python. +* +* foo = Foo(...) +* any = Any() +* any.Pack(foo) +* ... +* if any.Is(Foo.DESCRIPTOR): +* any.Unpack(foo) +* ... +* +* Example 4: Pack and unpack a message in Go +* +* foo := &pb.Foo{...} +* any, err := anypb.New(foo) +* if err != nil { +* ... +* } +* ... +* foo := &pb.Foo{} +* if err := any.UnmarshalTo(foo); err != nil { +* ... +* } +* +* The pack methods provided by protobuf library will by default use +* 'type.googleapis.com/full.type.name' as the type URL and the unpack +* methods only use the fully qualified type name after the last '/' +* in the type URL, for example "foo.bar.com/x/y.z" will yield type +* name "y.z". +* +* JSON +* ==== +* The JSON representation of an `Any` value uses the regular +* representation of the deserialized, embedded message, with an +* additional field `@type` which contains the type URL. Example: +* +* package google.profile; +* message Person { +* string first_name = 1; +* string last_name = 2; +* } +* +* { +* "@type": "type.googleapis.com/google.profile.Person", +* "firstName": , +* "lastName": +* } +* +* If the embedded message type is well-known and has a custom JSON +* representation, that representation will be embedded adding a field +* `value` which holds the custom JSON in addition to the `@type` +* field. Example (for message [google.protobuf.Duration][]): +* +* { +* "@type": "type.googleapis.com/google.protobuf.Duration", +* "value": "1.212s" +* } +*/ @kotlinx.rpc.grpc.codec.WithCodec(com.google.protobuf.kotlin.AnyInternal.CODEC::class) public interface Any { + /** + * A URL/resource name that uniquely identifies the type of the serialized + * protocol buffer message. This string must contain at least + * one "/" character. The last segment of the URL's path must represent + * the fully qualified name of the type (as in + * `path/google.protobuf.Duration`). The name should be in a canonical form + * (e.g., leading "." is not accepted). + * + * In practice, teams usually precompile into the binary all types that they + * expect it to use in the context of Any. However, for URLs which use the + * scheme `http`, `https`, or no scheme, one can optionally set up a type + * server that maps type URLs to message definitions as follows: + * + * * If no scheme is provided, `https` is assumed. + * * An HTTP GET on the URL must yield a [google.protobuf.Type][] + * value in binary format, or produce an error. + * * Applications are allowed to cache lookup results based on the + * URL, or have them precompiled into a binary to avoid any + * lookup. Therefore, binary compatibility needs to be preserved + * on changes to types. (Use versioned type names to manage + * breaking changes.) + * + * Note: this functionality is not currently available in the official + * protobuf release, and it is not used for type URLs beginning with + * type.googleapis.com. As of May 2023, there are no widely used type server + * implementations and no plans to implement one. + * + * Schemes other than `http`, `https` (or the empty scheme) might be + * used with implementation specific semantics. + */ public val typeUrl: String + /** + * Must be a valid serialized protocol buffer of the above specified type. + */ public val value: ByteArray public companion object } - diff --git a/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/Api.kt b/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/Api.kt index 12466025b..68ea99595 100644 --- a/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/Api.kt +++ b/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/Api.kt @@ -3,37 +3,199 @@ package com.google.protobuf.kotlin import kotlinx.rpc.internal.utils.* +/** +* Api is a light-weight descriptor for an API Interface. +* +* Interfaces are also described as "protocol buffer services" in some contexts, +* such as by the "service" keyword in a .proto file, but they are different +* from API Services, which represent a concrete implementation of an interface +* as opposed to simply a description of methods and bindings. They are also +* sometimes simply referred to as "APIs" in other contexts, such as the name of +* this message itself. See https://cloud.google.com/apis/design/glossary for +* detailed terminology. +*/ @kotlinx.rpc.grpc.codec.WithCodec(com.google.protobuf.kotlin.ApiInternal.CODEC::class) public interface Api { + /** + * The fully qualified name of this interface, including package name + * followed by the interface's simple name. + */ public val name: String + /** + * The methods of this interface, in unspecified order. + */ public val methods: List + /** + * Any metadata attached to the interface. + */ public val options: List + /** + * A version string for this interface. If specified, must have the form + * `major-version.minor-version`, as in `1.10`. If the minor version is + * omitted, it defaults to zero. If the entire version field is empty, the + * major version is derived from the package name, as outlined below. If the + * field is not empty, the version in the package name will be verified to be + * consistent with what is provided here. + * + * The versioning schema uses [semantic + * versioning](http://semver.org) where the major version number + * indicates a breaking change and the minor version an additive, + * non-breaking change. Both version numbers are signals to users + * what to expect from different versions, and should be carefully + * chosen based on the product plan. + * + * The major version is also reflected in the package name of the + * interface, which must end in `v`, as in + * `google.feature.v1`. For major versions 0 and 1, the suffix can + * be omitted. Zero major versions must only be used for + * experimental, non-GA interfaces. + */ public val version: String + /** + * Source context for the protocol buffer service represented by this + * message. + */ public val sourceContext: com.google.protobuf.kotlin.SourceContext + /** + * Included interfaces. See [Mixin][]. + */ public val mixins: List + /** + * The source syntax of the service. + */ public val syntax: com.google.protobuf.kotlin.Syntax public companion object } +/** +* Method represents a method of an API interface. +*/ @kotlinx.rpc.grpc.codec.WithCodec(com.google.protobuf.kotlin.MethodInternal.CODEC::class) public interface Method { + /** + * The simple name of this method. + */ public val name: String + /** + * A URL of the input message type. + */ public val requestTypeUrl: String + /** + * If true, the request is streamed. + */ public val requestStreaming: Boolean + /** + * The URL of the output message type. + */ public val responseTypeUrl: String + /** + * If true, the response is streamed. + */ public val responseStreaming: Boolean + /** + * Any metadata attached to the method. + */ public val options: List + /** + * The source syntax of this method. + */ public val syntax: com.google.protobuf.kotlin.Syntax public companion object } +/** +* Declares an API Interface to be included in this interface. The including +* interface must redeclare all the methods from the included interface, but +* documentation and options are inherited as follows: +* +* - If after comment and whitespace stripping, the documentation +* string of the redeclared method is empty, it will be inherited +* from the original method. +* +* - Each annotation belonging to the service config (http, +* visibility) which is not set in the redeclared method will be +* inherited. +* +* - If an http annotation is inherited, the path pattern will be +* modified as follows. Any version prefix will be replaced by the +* version of the including interface plus the [root][] path if +* specified. +* +* Example of a simple mixin: +* +* package google.acl.v1; +* service AccessControl { +* // Get the underlying ACL object. +* rpc GetAcl(GetAclRequest) returns (Acl) { +* option (google.api.http).get = "/v1/{resource=**}:getAcl"; +* } +* } +* +* package google.storage.v2; +* service Storage { +* rpc GetAcl(GetAclRequest) returns (Acl); +* +* // Get a data record. +* rpc GetData(GetDataRequest) returns (Data) { +* option (google.api.http).get = "/v2/{resource=**}"; +* } +* } +* +* Example of a mixin configuration: +* +* apis: +* - name: google.storage.v2.Storage +* mixins: +* - name: google.acl.v1.AccessControl +* +* The mixin construct implies that all methods in `AccessControl` are +* also declared with same name and request/response types in +* `Storage`. A documentation generator or annotation processor will +* see the effective `Storage.GetAcl` method after inheriting +* documentation and annotations as follows: +* +* service Storage { +* // Get the underlying ACL object. +* rpc GetAcl(GetAclRequest) returns (Acl) { +* option (google.api.http).get = "/v2/{resource=**}:getAcl"; +* } +* ... +* } +* +* Note how the version in the path pattern changed from `v1` to `v2`. +* +* If the `root` field in the mixin is specified, it should be a +* relative path under which inherited HTTP paths are placed. Example: +* +* apis: +* - name: google.storage.v2.Storage +* mixins: +* - name: google.acl.v1.AccessControl +* root: acls +* +* This implies the following inherited HTTP annotation: +* +* service Storage { +* // Get the underlying ACL object. +* rpc GetAcl(GetAclRequest) returns (Acl) { +* option (google.api.http).get = "/v2/acls/{resource=**}:getAcl"; +* } +* ... +* } +*/ @kotlinx.rpc.grpc.codec.WithCodec(com.google.protobuf.kotlin.MixinInternal.CODEC::class) public interface Mixin { + /** + * The fully qualified name of the interface which is included. + */ public val name: String + /** + * If non-empty specifies a path under which inherited HTTP paths + * are rooted. + */ public val root: String public companion object } - diff --git a/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/Duration.kt b/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/Duration.kt index f0fa480c3..b5fd74ea8 100644 --- a/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/Duration.kt +++ b/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/Duration.kt @@ -3,11 +3,83 @@ package com.google.protobuf.kotlin import kotlinx.rpc.internal.utils.* +/** +* A Duration represents a signed, fixed-length span of time represented +* as a count of seconds and fractions of seconds at nanosecond +* resolution. It is independent of any calendar and concepts like "day" +* or "month". It is related to Timestamp in that the difference between +* two Timestamp values is a Duration and it can be added or subtracted +* from a Timestamp. Range is approximately +-10,000 years. +* +* # Examples +* +* Example 1: Compute Duration from two Timestamps in pseudo code. +* +* Timestamp start = ...; +* Timestamp end = ...; +* Duration duration = ...; +* +* duration.seconds = end.seconds - start.seconds; +* duration.nanos = end.nanos - start.nanos; +* +* if (duration.seconds < 0 && duration.nanos > 0) { +* duration.seconds += 1; +* duration.nanos -= 1000000000; +* } else if (duration.seconds > 0 && duration.nanos < 0) { +* duration.seconds -= 1; +* duration.nanos += 1000000000; +* } +* +* Example 2: Compute Timestamp from Timestamp + Duration in pseudo code. +* +* Timestamp start = ...; +* Duration duration = ...; +* Timestamp end = ...; +* +* end.seconds = start.seconds + duration.seconds; +* end.nanos = start.nanos + duration.nanos; +* +* if (end.nanos < 0) { +* end.seconds -= 1; +* end.nanos += 1000000000; +* } else if (end.nanos >= 1000000000) { +* end.seconds += 1; +* end.nanos -= 1000000000; +* } +* +* Example 3: Compute Duration from datetime.timedelta in Python. +* +* td = datetime.timedelta(days=3, minutes=10) +* duration = Duration() +* duration.FromTimedelta(td) +* +* # JSON Mapping +* +* In JSON format, the Duration type is encoded as a string rather than an +* object, where the string ends in the suffix "s" (indicating seconds) and +* is preceded by the number of seconds, with nanoseconds expressed as +* fractional seconds. For example, 3 seconds with 0 nanoseconds should be +* encoded in JSON format as "3s", while 3 seconds and 1 nanosecond should +* be expressed in JSON format as "3.000000001s", and 3 seconds and 1 +* microsecond should be expressed in JSON format as "3.000001s". +*/ @kotlinx.rpc.grpc.codec.WithCodec(com.google.protobuf.kotlin.DurationInternal.CODEC::class) public interface Duration { + /** + * Signed seconds of the span of time. Must be from -315,576,000,000 + * to +315,576,000,000 inclusive. Note: these bounds are computed from: + * 60 sec/min * 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years + */ public val seconds: Long + /** + * Signed fractions of a second at nanosecond resolution of the span + * of time. Durations less than one second are represented with a 0 + * `seconds` field and a positive or negative `nanos` field. For durations + * of one second or more, a non-zero value for the `nanos` field must be + * of the same sign as the `seconds` field. Must be from -999,999,999 + * to +999,999,999 inclusive. + */ public val nanos: Int public companion object } - diff --git a/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/Empty.kt b/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/Empty.kt index 0f618e7fb..0ea1dfafa 100644 --- a/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/Empty.kt +++ b/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/Empty.kt @@ -3,8 +3,16 @@ package com.google.protobuf.kotlin import kotlinx.rpc.internal.utils.* +/** +* A generic empty message that you can re-use to avoid defining duplicated +* empty messages in your APIs. A typical example is to use it as the request +* or the response type of an API method. For instance: +* +* service Foo { +* rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); +* } +*/ @kotlinx.rpc.grpc.codec.WithCodec(com.google.protobuf.kotlin.EmptyInternal.CODEC::class) public interface Empty { public companion object } - diff --git a/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/FieldMask.kt b/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/FieldMask.kt index ea41057f4..4b50a3707 100644 --- a/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/FieldMask.kt +++ b/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/FieldMask.kt @@ -3,10 +3,213 @@ package com.google.protobuf.kotlin import kotlinx.rpc.internal.utils.* +/** +* `FieldMask` represents a set of symbolic field paths, for example: +* +* paths: "f.a" +* paths: "f.b.d" +* +* Here `f` represents a field in some root message, `a` and `b` +* fields in the message found in `f`, and `d` a field found in the +* message in `f.b`. +* +* Field masks are used to specify a subset of fields that should be +* returned by a get operation or modified by an update operation. +* Field masks also have a custom JSON encoding (see below). +* +* # Field Masks in Projections +* +* When used in the context of a projection, a response message or +* sub-message is filtered by the API to only contain those fields as +* specified in the mask. For example, if the mask in the previous +* example is applied to a response message as follows: +* +* f { +* a : 22 +* b { +* d : 1 +* x : 2 +* } +* y : 13 +* } +* z: 8 +* +* The result will not contain specific values for fields x,y and z +* (their value will be set to the default, and omitted in proto text +* output): +* +* +* f { +* a : 22 +* b { +* d : 1 +* } +* } +* +* A repeated field is not allowed except at the last position of a +* paths string. +* +* If a FieldMask object is not present in a get operation, the +* operation applies to all fields (as if a FieldMask of all fields +* had been specified). +* +* Note that a field mask does not necessarily apply to the +* top-level response message. In case of a REST get operation, the +* field mask applies directly to the response, but in case of a REST +* list operation, the mask instead applies to each individual message +* in the returned resource list. In case of a REST custom method, +* other definitions may be used. Where the mask applies will be +* clearly documented together with its declaration in the API. In +* any case, the effect on the returned resource/resources is required +* behavior for APIs. +* +* # Field Masks in Update Operations +* +* A field mask in update operations specifies which fields of the +* targeted resource are going to be updated. The API is required +* to only change the values of the fields as specified in the mask +* and leave the others untouched. If a resource is passed in to +* describe the updated values, the API ignores the values of all +* fields not covered by the mask. +* +* If a repeated field is specified for an update operation, new values will +* be appended to the existing repeated field in the target resource. Note that +* a repeated field is only allowed in the last position of a `paths` string. +* +* If a sub-message is specified in the last position of the field mask for an +* update operation, then new value will be merged into the existing sub-message +* in the target resource. +* +* For example, given the target message: +* +* f { +* b { +* d: 1 +* x: 2 +* } +* c: [1] +* } +* +* And an update message: +* +* f { +* b { +* d: 10 +* } +* c: [2] +* } +* +* then if the field mask is: +* +* paths: ["f.b", "f.c"] +* +* then the result will be: +* +* f { +* b { +* d: 10 +* x: 2 +* } +* c: [1, 2] +* } +* +* An implementation may provide options to override this default behavior for +* repeated and message fields. +* +* In order to reset a field's value to the default, the field must +* be in the mask and set to the default value in the provided resource. +* Hence, in order to reset all fields of a resource, provide a default +* instance of the resource and set all fields in the mask, or do +* not provide a mask as described below. +* +* If a field mask is not present on update, the operation applies to +* all fields (as if a field mask of all fields has been specified). +* Note that in the presence of schema evolution, this may mean that +* fields the client does not know and has therefore not filled into +* the request will be reset to their default. If this is unwanted +* behavior, a specific service may require a client to always specify +* a field mask, producing an error if not. +* +* As with get operations, the location of the resource which +* describes the updated values in the request message depends on the +* operation kind. In any case, the effect of the field mask is +* required to be honored by the API. +* +* ## Considerations for HTTP REST +* +* The HTTP kind of an update operation which uses a field mask must +* be set to PATCH instead of PUT in order to satisfy HTTP semantics +* (PUT must only be used for full updates). +* +* # JSON Encoding of Field Masks +* +* In JSON, a field mask is encoded as a single string where paths are +* separated by a comma. Fields name in each path are converted +* to/from lower-camel naming conventions. +* +* As an example, consider the following message declarations: +* +* message Profile { +* User user = 1; +* Photo photo = 2; +* } +* message User { +* string display_name = 1; +* string address = 2; +* } +* +* In proto a field mask for `Profile` may look as such: +* +* mask { +* paths: "user.display_name" +* paths: "photo" +* } +* +* In JSON, the same mask is represented as below: +* +* { +* mask: "user.displayName,photo" +* } +* +* # Field Masks and Oneof Fields +* +* Field masks treat fields in oneofs just as regular fields. Consider the +* following message: +* +* message SampleMessage { +* oneof test_oneof { +* string name = 4; +* SubMessage sub_message = 9; +* } +* } +* +* The field mask can be: +* +* mask { +* paths: "name" +* } +* +* Or: +* +* mask { +* paths: "sub_message" +* } +* +* Note that oneof type names ("test_oneof" in this case) cannot be used in +* paths. +* +* ## Field Mask Verification +* +* The implementation of any API method which has a FieldMask type field in the +* request should verify the included field paths, and return an +* `INVALID_ARGUMENT` error if any path is unmappable. +*/ @kotlinx.rpc.grpc.codec.WithCodec(com.google.protobuf.kotlin.FieldMaskInternal.CODEC::class) public interface FieldMask { + /** + * The set of field mask paths. + */ public val paths: List public companion object } - diff --git a/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/SourceContext.kt b/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/SourceContext.kt index ebc8e81a0..6756c3269 100644 --- a/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/SourceContext.kt +++ b/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/SourceContext.kt @@ -3,10 +3,17 @@ package com.google.protobuf.kotlin import kotlinx.rpc.internal.utils.* +/** +* `SourceContext` represents information about the source of a +* protobuf element, like the file in which it is defined. +*/ @kotlinx.rpc.grpc.codec.WithCodec(com.google.protobuf.kotlin.SourceContextInternal.CODEC::class) public interface SourceContext { + /** + * The path-qualified name of the .proto file that contained the associated + * protobuf element. For example: `"google/protobuf/source_context.proto"`. + */ public val fileName: String public companion object } - diff --git a/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/Struct.kt b/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/Struct.kt index 1469b0ecc..52cbed8a7 100644 --- a/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/Struct.kt +++ b/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/Struct.kt @@ -4,37 +4,76 @@ package com.google.protobuf.kotlin import kotlin.jvm.JvmInline import kotlinx.rpc.internal.utils.* +/** +* `Struct` represents a structured data value, consisting of fields +* which map to dynamically typed values. In some languages, `Struct` +* might be supported by a native representation. For example, in +* scripting languages like JS a struct is represented as an +* object. The details of that representation are described together +* with the proto support for the language. +* +* The JSON representation for `Struct` is JSON object. +*/ @kotlinx.rpc.grpc.codec.WithCodec(com.google.protobuf.kotlin.StructInternal.CODEC::class) public interface Struct { + /** + * Unordered map of dynamically typed values. + */ public val fields: Map public companion object } +/** +* `Value` represents a dynamically typed value which can be either +* null, a number, a string, a boolean, a recursive struct value, or a +* list of values. A producer of value is expected to set one of these +* variants. Absence of any variant indicates an error. +* +* The JSON representation for `Value` is JSON value. +*/ @kotlinx.rpc.grpc.codec.WithCodec(com.google.protobuf.kotlin.ValueInternal.CODEC::class) public interface Value { public val kind: com.google.protobuf.kotlin.Value.Kind? public sealed interface Kind { + /** + * Represents a null value. + */ @JvmInline public value class NullValue( public val value: com.google.protobuf.kotlin.NullValue, ): Kind + /** + * Represents a double value. + */ @JvmInline public value class NumberValue(public val value: Double): Kind + /** + * Represents a string value. + */ @JvmInline public value class StringValue(public val value: String): Kind + /** + * Represents a boolean value. + */ @JvmInline public value class BoolValue(public val value: Boolean): Kind + /** + * Represents a structured value. + */ @JvmInline public value class StructValue( public val value: com.google.protobuf.kotlin.Struct, ): Kind + /** + * Represents a repeated `Value`. + */ @JvmInline public value class ListValue( public val value: com.google.protobuf.kotlin.ListValue, @@ -44,14 +83,31 @@ public interface Value { public companion object } +/** +* `ListValue` is a wrapper around a repeated field of values. +* +* The JSON representation for `ListValue` is JSON array. +*/ @kotlinx.rpc.grpc.codec.WithCodec(com.google.protobuf.kotlin.ListValueInternal.CODEC::class) public interface ListValue { + /** + * Repeated field of dynamically typed values. + */ public val values: List public companion object } +/** +* `NullValue` is a singleton enumeration to represent the null value for the +* `Value` type union. +* +* The JSON representation for `NullValue` is JSON `null`. +*/ public sealed class NullValue(public open val number: Int) { + /** + * Null value. + */ public object NULL_VALUE: NullValue(number = 0) public data class UNRECOGNIZED(override val number: Int): NullValue(number) @@ -60,4 +116,3 @@ public sealed class NullValue(public open val number: Int) { public val entries: List by lazy { listOf(NULL_VALUE) } } } - diff --git a/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/Timestamp.kt b/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/Timestamp.kt index f05bbf2b4..ea8d32b7f 100644 --- a/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/Timestamp.kt +++ b/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/Timestamp.kt @@ -3,11 +3,112 @@ package com.google.protobuf.kotlin import kotlinx.rpc.internal.utils.* +/** +* A Timestamp represents a point in time independent of any time zone or local +* calendar, encoded as a count of seconds and fractions of seconds at +* nanosecond resolution. The count is relative to an epoch at UTC midnight on +* January 1, 1970, in the proleptic Gregorian calendar which extends the +* Gregorian calendar backwards to year one. +* +* All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap +* second table is needed for interpretation, using a [24-hour linear +* smear](https://developers.google.com/time/smear). +* +* The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By +* restricting to that range, we ensure that we can convert to and from [RFC +* 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings. +* +* # Examples +* +* Example 1: Compute Timestamp from POSIX `time()`. +* +* Timestamp timestamp; +* timestamp.set_seconds(time(NULL)); +* timestamp.set_nanos(0); +* +* Example 2: Compute Timestamp from POSIX `gettimeofday()`. +* +* struct timeval tv; +* gettimeofday(&tv, NULL); +* +* Timestamp timestamp; +* timestamp.set_seconds(tv.tv_sec); +* timestamp.set_nanos(tv.tv_usec * 1000); +* +* Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`. +* +* FILETIME ft; +* GetSystemTimeAsFileTime(&ft); +* UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime; +* +* // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z +* // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z. +* Timestamp timestamp; +* timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL)); +* timestamp.set_nanos((INT32) ((ticks % 10000000) * 100)); +* +* Example 4: Compute Timestamp from Java `System.currentTimeMillis()`. +* +* long millis = System.currentTimeMillis(); +* +* Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000) +* .setNanos((int) ((millis % 1000) * 1000000)).build(); +* +* Example 5: Compute Timestamp from Java `Instant.now()`. +* +* Instant now = Instant.now(); +* +* Timestamp timestamp = +* Timestamp.newBuilder().setSeconds(now.getEpochSecond()) +* .setNanos(now.getNano()).build(); +* +* Example 6: Compute Timestamp from current time in Python. +* +* timestamp = Timestamp() +* timestamp.GetCurrentTime() +* +* # JSON Mapping +* +* In JSON format, the Timestamp type is encoded as a string in the +* [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the +* format is "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z" +* where {year} is always expressed using four digits while {month}, {day}, +* {hour}, {min}, and {sec} are zero-padded to two digits each. The fractional +* seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution), +* are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone +* is required. A proto3 JSON serializer should always use UTC (as indicated by +* "Z") when printing the Timestamp type and a proto3 JSON parser should be +* able to accept both UTC and other timezones (as indicated by an offset). +* +* For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past +* 01:30 UTC on January 15, 2017. +* +* In JavaScript, one can convert a Date object to this format using the +* standard +* [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString) +* method. In Python, a standard `datetime.datetime` object can be converted +* to this format using +* [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with +* the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use +* the Joda Time's [`ISODateTimeFormat.dateTime()`]( +* http://joda-time.sourceforge.net/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime() +* ) to obtain a formatter capable of generating timestamps in this format. +*/ @kotlinx.rpc.grpc.codec.WithCodec(com.google.protobuf.kotlin.TimestampInternal.CODEC::class) public interface Timestamp { + /** + * Represents seconds of UTC time since Unix epoch + * 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to + * 9999-12-31T23:59:59Z inclusive. + */ public val seconds: Long + /** + * Non-negative fractions of a second at nanosecond resolution. Negative + * second values with fractions must still have non-negative nanos values + * that count forward in time. Must be from 0 to 999,999,999 + * inclusive. + */ public val nanos: Int public companion object } - diff --git a/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/Type.kt b/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/Type.kt index a58b713f8..8eba847cb 100644 --- a/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/Type.kt +++ b/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/Type.kt @@ -3,69 +3,188 @@ package com.google.protobuf.kotlin import kotlinx.rpc.internal.utils.* +/** +* A protocol buffer message type. +*/ @kotlinx.rpc.grpc.codec.WithCodec(com.google.protobuf.kotlin.TypeInternal.CODEC::class) public interface Type { + /** + * The fully qualified message name. + */ public val name: String + /** + * The list of fields. + */ public val fields: List + /** + * The list of types appearing in `oneof` definitions in this type. + */ public val oneofs: List + /** + * The protocol buffer options. + */ public val options: List + /** + * The source context. + */ public val sourceContext: com.google.protobuf.kotlin.SourceContext + /** + * The source syntax. + */ public val syntax: com.google.protobuf.kotlin.Syntax + /** + * The source edition string, only valid when syntax is SYNTAX_EDITIONS. + */ public val edition: String public companion object } +/** +* A single field of a message type. +*/ @kotlinx.rpc.grpc.codec.WithCodec(com.google.protobuf.kotlin.FieldInternal.CODEC::class) public interface Field { + /** + * The field type. + */ public val kind: com.google.protobuf.kotlin.Field.Kind + /** + * The field cardinality. + */ public val cardinality: com.google.protobuf.kotlin.Field.Cardinality + /** + * The field number. + */ public val number: Int + /** + * The field name. + */ public val name: String + /** + * The field type URL, without the scheme, for message or enumeration + * types. Example: `"type.googleapis.com/google.protobuf.Timestamp"`. + */ public val typeUrl: String + /** + * The index of the field type in `Type.oneofs`, for message or enumeration + * types. The first type has index 1; zero means the type is not in the list. + */ public val oneofIndex: Int + /** + * Whether to use alternative packed wire representation. + */ public val packed: Boolean + /** + * The protocol buffer options. + */ public val options: List + /** + * The field JSON name. + */ public val jsonName: String + /** + * The string value of the default value of this field. Proto2 syntax only. + */ public val defaultValue: String + /** + * Basic field types. + */ public sealed class Kind(public open val number: Int) { + /** + * Field type unknown. + */ public object TYPE_UNKNOWN: Kind(number = 0) + /** + * Field type double. + */ public object TYPE_DOUBLE: Kind(number = 1) + /** + * Field type float. + */ public object TYPE_FLOAT: Kind(number = 2) + /** + * Field type int64. + */ public object TYPE_INT64: Kind(number = 3) + /** + * Field type uint64. + */ public object TYPE_UINT64: Kind(number = 4) + /** + * Field type int32. + */ public object TYPE_INT32: Kind(number = 5) + /** + * Field type fixed64. + */ public object TYPE_FIXED64: Kind(number = 6) + /** + * Field type fixed32. + */ public object TYPE_FIXED32: Kind(number = 7) + /** + * Field type bool. + */ public object TYPE_BOOL: Kind(number = 8) + /** + * Field type string. + */ public object TYPE_STRING: Kind(number = 9) + /** + * Field type group. Proto2 syntax only, and deprecated. + */ public object TYPE_GROUP: Kind(number = 10) + /** + * Field type message. + */ public object TYPE_MESSAGE: Kind(number = 11) + /** + * Field type bytes. + */ public object TYPE_BYTES: Kind(number = 12) + /** + * Field type uint32. + */ public object TYPE_UINT32: Kind(number = 13) + /** + * Field type enum. + */ public object TYPE_ENUM: Kind(number = 14) + /** + * Field type sfixed32. + */ public object TYPE_SFIXED32: Kind(number = 15) + /** + * Field type sfixed64. + */ public object TYPE_SFIXED64: Kind(number = 16) + /** + * Field type sint32. + */ public object TYPE_SINT32: Kind(number = 17) + /** + * Field type sint64. + */ public object TYPE_SINT64: Kind(number = 18) public data class UNRECOGNIZED(override val number: Int): Kind(number) @@ -75,13 +194,28 @@ public interface Field { } } + /** + * Whether a field is optional, required, or repeated. + */ public sealed class Cardinality(public open val number: Int) { + /** + * For fields with unknown cardinality. + */ public object CARDINALITY_UNKNOWN: Cardinality(number = 0) + /** + * For optional fields. + */ public object CARDINALITY_OPTIONAL: Cardinality(number = 1) + /** + * For required fields. Proto2 syntax only. + */ public object CARDINALITY_REQUIRED: Cardinality(number = 2) + /** + * For repeated fields. + */ public object CARDINALITY_REPEATED: Cardinality(number = 3) public data class UNRECOGNIZED(override val number: Int): Cardinality(number) @@ -94,40 +228,101 @@ public interface Field { public companion object } +/** +* Enum type definition. +*/ @kotlinx.rpc.grpc.codec.WithCodec(com.google.protobuf.kotlin.EnumInternal.CODEC::class) public interface Enum { + /** + * Enum type name. + */ public val name: String + /** + * Enum value definitions. + */ public val enumvalue: List + /** + * Protocol buffer options. + */ public val options: List + /** + * The source context. + */ public val sourceContext: com.google.protobuf.kotlin.SourceContext + /** + * The source syntax. + */ public val syntax: com.google.protobuf.kotlin.Syntax + /** + * The source edition string, only valid when syntax is SYNTAX_EDITIONS. + */ public val edition: String public companion object } +/** +* Enum value definition. +*/ @kotlinx.rpc.grpc.codec.WithCodec(com.google.protobuf.kotlin.EnumValueInternal.CODEC::class) public interface EnumValue { + /** + * Enum value name. + */ public val name: String + /** + * Enum value number. + */ public val number: Int + /** + * Protocol buffer options. + */ public val options: List public companion object } +/** +* A protocol buffer option, which can be attached to a message, field, +* enumeration, etc. +*/ @kotlinx.rpc.grpc.codec.WithCodec(com.google.protobuf.kotlin.OptionInternal.CODEC::class) public interface Option { + /** + * The option's name. For protobuf built-in options (options defined in + * descriptor.proto), this is the short name. For example, `"map_entry"`. + * For custom options, it should be the fully-qualified name. For example, + * `"google.api.http"`. + */ public val name: String + /** + * The option's value packed in an Any message. If the value is a primitive, + * the corresponding wrapper type defined in google/protobuf/wrappers.proto + * should be used. If the value is an enum, it should be stored as an int32 + * value using the google.protobuf.Int32Value type. + */ public val value: com.google.protobuf.kotlin.Any public companion object } +/** +* The syntax in which a protocol buffer element is defined. +*/ public sealed class Syntax(public open val number: Int) { + /** + * Syntax `proto2`. + */ public object SYNTAX_PROTO2: Syntax(number = 0) + /** + * Syntax `proto3`. + */ public object SYNTAX_PROTO3: Syntax(number = 1) + /** + * Syntax `editions`. + */ public object SYNTAX_EDITIONS: Syntax(number = 2) public data class UNRECOGNIZED(override val number: Int): Syntax(number) @@ -136,4 +331,3 @@ public sealed class Syntax(public open val number: Int) { public val entries: List by lazy { listOf(SYNTAX_PROTO2, SYNTAX_PROTO3, SYNTAX_EDITIONS) } } } - diff --git a/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/Wrappers.kt b/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/Wrappers.kt index b5b8dab74..da349669f 100644 --- a/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/Wrappers.kt +++ b/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/Wrappers.kt @@ -3,66 +3,164 @@ package com.google.protobuf.kotlin import kotlinx.rpc.internal.utils.* +/** +* Wrapper message for `double`. +* +* The JSON representation for `DoubleValue` is JSON number. +* +* Not recommended for use in new APIs, but still useful for legacy APIs and +* has no plan to be removed. +*/ @kotlinx.rpc.grpc.codec.WithCodec(com.google.protobuf.kotlin.DoubleValueInternal.CODEC::class) public interface DoubleValue { + /** + * The double value. + */ public val value: Double public companion object } +/** +* Wrapper message for `float`. +* +* The JSON representation for `FloatValue` is JSON number. +* +* Not recommended for use in new APIs, but still useful for legacy APIs and +* has no plan to be removed. +*/ @kotlinx.rpc.grpc.codec.WithCodec(com.google.protobuf.kotlin.FloatValueInternal.CODEC::class) public interface FloatValue { + /** + * The float value. + */ public val value: Float public companion object } +/** +* Wrapper message for `int64`. +* +* The JSON representation for `Int64Value` is JSON string. +* +* Not recommended for use in new APIs, but still useful for legacy APIs and +* has no plan to be removed. +*/ @kotlinx.rpc.grpc.codec.WithCodec(com.google.protobuf.kotlin.Int64ValueInternal.CODEC::class) public interface Int64Value { + /** + * The int64 value. + */ public val value: Long public companion object } +/** +* Wrapper message for `uint64`. +* +* The JSON representation for `UInt64Value` is JSON string. +* +* Not recommended for use in new APIs, but still useful for legacy APIs and +* has no plan to be removed. +*/ @kotlinx.rpc.grpc.codec.WithCodec(com.google.protobuf.kotlin.UInt64ValueInternal.CODEC::class) public interface UInt64Value { + /** + * The uint64 value. + */ public val value: ULong public companion object } +/** +* Wrapper message for `int32`. +* +* The JSON representation for `Int32Value` is JSON number. +* +* Not recommended for use in new APIs, but still useful for legacy APIs and +* has no plan to be removed. +*/ @kotlinx.rpc.grpc.codec.WithCodec(com.google.protobuf.kotlin.Int32ValueInternal.CODEC::class) public interface Int32Value { + /** + * The int32 value. + */ public val value: Int public companion object } +/** +* Wrapper message for `uint32`. +* +* The JSON representation for `UInt32Value` is JSON number. +* +* Not recommended for use in new APIs, but still useful for legacy APIs and +* has no plan to be removed. +*/ @kotlinx.rpc.grpc.codec.WithCodec(com.google.protobuf.kotlin.UInt32ValueInternal.CODEC::class) public interface UInt32Value { + /** + * The uint32 value. + */ public val value: UInt public companion object } +/** +* Wrapper message for `bool`. +* +* The JSON representation for `BoolValue` is JSON `true` and `false`. +* +* Not recommended for use in new APIs, but still useful for legacy APIs and +* has no plan to be removed. +*/ @kotlinx.rpc.grpc.codec.WithCodec(com.google.protobuf.kotlin.BoolValueInternal.CODEC::class) public interface BoolValue { + /** + * The bool value. + */ public val value: Boolean public companion object } +/** +* Wrapper message for `string`. +* +* The JSON representation for `StringValue` is JSON string. +* +* Not recommended for use in new APIs, but still useful for legacy APIs and +* has no plan to be removed. +*/ @kotlinx.rpc.grpc.codec.WithCodec(com.google.protobuf.kotlin.StringValueInternal.CODEC::class) public interface StringValue { + /** + * The string value. + */ public val value: String public companion object } +/** +* Wrapper message for `bytes`. +* +* The JSON representation for `BytesValue` is JSON string. +* +* Not recommended for use in new APIs, but still useful for legacy APIs and +* has no plan to be removed. +*/ @kotlinx.rpc.grpc.codec.WithCodec(com.google.protobuf.kotlin.BytesValueInternal.CODEC::class) public interface BytesValue { + /** + * The bytes value. + */ public val value: ByteArray public companion object } - diff --git a/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/_rpc_internal/Any.kt b/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/_rpc_internal/Any.kt index f900fabf0..9a603ad93 100644 --- a/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/_rpc_internal/Any.kt +++ b/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/_rpc_internal/Any.kt @@ -104,4 +104,3 @@ private fun com.google.protobuf.kotlin.AnyInternal.computeSize(): Int { public fun com.google.protobuf.kotlin.Any.asInternal(): com.google.protobuf.kotlin.AnyInternal { return this as? com.google.protobuf.kotlin.AnyInternal ?: error("Message ${this::class.simpleName} is a non-internal message type.") } - diff --git a/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/_rpc_internal/Api.kt b/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/_rpc_internal/Api.kt index f1cc4a6b9..ac55d87a5 100644 --- a/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/_rpc_internal/Api.kt +++ b/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/_rpc_internal/Api.kt @@ -476,4 +476,3 @@ private fun com.google.protobuf.kotlin.MixinInternal.computeSize(): Int { public fun com.google.protobuf.kotlin.Mixin.asInternal(): com.google.protobuf.kotlin.MixinInternal { return this as? com.google.protobuf.kotlin.MixinInternal ?: error("Message ${this::class.simpleName} is a non-internal message type.") } - diff --git a/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/_rpc_internal/Duration.kt b/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/_rpc_internal/Duration.kt index 11effb913..824c79d6c 100644 --- a/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/_rpc_internal/Duration.kt +++ b/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/_rpc_internal/Duration.kt @@ -104,4 +104,3 @@ private fun com.google.protobuf.kotlin.DurationInternal.computeSize(): Int { public fun com.google.protobuf.kotlin.Duration.asInternal(): com.google.protobuf.kotlin.DurationInternal { return this as? com.google.protobuf.kotlin.DurationInternal ?: error("Message ${this::class.simpleName} is a non-internal message type.") } - diff --git a/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/_rpc_internal/Empty.kt b/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/_rpc_internal/Empty.kt index 9bb601255..7ce0336c9 100644 --- a/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/_rpc_internal/Empty.kt +++ b/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/_rpc_internal/Empty.kt @@ -79,4 +79,3 @@ private fun com.google.protobuf.kotlin.EmptyInternal.computeSize(): Int { public fun com.google.protobuf.kotlin.Empty.asInternal(): com.google.protobuf.kotlin.EmptyInternal { return this as? com.google.protobuf.kotlin.EmptyInternal ?: error("Message ${this::class.simpleName} is a non-internal message type.") } - diff --git a/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/_rpc_internal/FieldMask.kt b/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/_rpc_internal/FieldMask.kt index b7e8411cb..c04bb2e0b 100644 --- a/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/_rpc_internal/FieldMask.kt +++ b/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/_rpc_internal/FieldMask.kt @@ -94,4 +94,3 @@ private fun com.google.protobuf.kotlin.FieldMaskInternal.computeSize(): Int { public fun com.google.protobuf.kotlin.FieldMask.asInternal(): com.google.protobuf.kotlin.FieldMaskInternal { return this as? com.google.protobuf.kotlin.FieldMaskInternal ?: error("Message ${this::class.simpleName} is a non-internal message type.") } - diff --git a/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/_rpc_internal/SourceContext.kt b/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/_rpc_internal/SourceContext.kt index 6b4c60a32..037ff21cb 100644 --- a/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/_rpc_internal/SourceContext.kt +++ b/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/_rpc_internal/SourceContext.kt @@ -91,4 +91,3 @@ private fun com.google.protobuf.kotlin.SourceContextInternal.computeSize(): Int public fun com.google.protobuf.kotlin.SourceContext.asInternal(): com.google.protobuf.kotlin.SourceContextInternal { return this as? com.google.protobuf.kotlin.SourceContextInternal ?: error("Message ${this::class.simpleName} is a non-internal message type.") } - diff --git a/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/_rpc_internal/Struct.kt b/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/_rpc_internal/Struct.kt index a0cf20b59..d00cc6742 100644 --- a/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/_rpc_internal/Struct.kt +++ b/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/_rpc_internal/Struct.kt @@ -473,4 +473,3 @@ public fun com.google.protobuf.kotlin.NullValue.Companion.fromNumber(number: Int } } } - diff --git a/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/_rpc_internal/Timestamp.kt b/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/_rpc_internal/Timestamp.kt index 1a78799e5..3ff3a1d19 100644 --- a/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/_rpc_internal/Timestamp.kt +++ b/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/_rpc_internal/Timestamp.kt @@ -104,4 +104,3 @@ private fun com.google.protobuf.kotlin.TimestampInternal.computeSize(): Int { public fun com.google.protobuf.kotlin.Timestamp.asInternal(): com.google.protobuf.kotlin.TimestampInternal { return this as? com.google.protobuf.kotlin.TimestampInternal ?: error("Message ${this::class.simpleName} is a non-internal message type.") } - diff --git a/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/_rpc_internal/Type.kt b/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/_rpc_internal/Type.kt index d688524dc..b84a764c3 100644 --- a/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/_rpc_internal/Type.kt +++ b/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/_rpc_internal/Type.kt @@ -951,4 +951,3 @@ public fun com.google.protobuf.kotlin.Field.Cardinality.Companion.fromNumber(num } } } - diff --git a/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/_rpc_internal/Wrappers.kt b/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/_rpc_internal/Wrappers.kt index f50f6c53f..b6b1c48bf 100644 --- a/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/_rpc_internal/Wrappers.kt +++ b/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/_rpc_internal/Wrappers.kt @@ -787,4 +787,3 @@ private fun com.google.protobuf.kotlin.BytesValueInternal.computeSize(): Int { public fun com.google.protobuf.kotlin.BytesValue.asInternal(): com.google.protobuf.kotlin.BytesValueInternal { return this as? com.google.protobuf.kotlin.BytesValueInternal ?: error("Message ${this::class.simpleName} is a non-internal message type.") } - diff --git a/tests/protobuf-conformance/build.gradle.kts b/tests/protobuf-conformance/build.gradle.kts index c25cc5cf4..265437f26 100644 --- a/tests/protobuf-conformance/build.gradle.kts +++ b/tests/protobuf-conformance/build.gradle.kts @@ -36,6 +36,12 @@ dependencies { setupProtobufConformanceResources() configureLocalProtocGenDevelopmentDependency("Main") +rpc { + protoc.buf.generate.comments { + includeFileLevelComments = false + } +} + val generatedCodeDir = project.layout.projectDirectory .dir("src") .dir("main") diff --git a/tests/protobuf-conformance/src/main/generated-code/kotlin-multiplatform/com/google/protobuf/conformance/Conformance.kt b/tests/protobuf-conformance/src/main/generated-code/kotlin-multiplatform/com/google/protobuf/conformance/Conformance.kt index 1f6b6008a..8a2bc4343 100644 --- a/tests/protobuf-conformance/src/main/generated-code/kotlin-multiplatform/com/google/protobuf/conformance/Conformance.kt +++ b/tests/protobuf-conformance/src/main/generated-code/kotlin-multiplatform/com/google/protobuf/conformance/Conformance.kt @@ -4,15 +4,29 @@ package com.google.protobuf.conformance import kotlin.jvm.JvmInline import kotlinx.rpc.internal.utils.* +/** +* Meant to encapsulate all types of tests: successes, skips, failures, etc. +* Therefore, this may or may not have a failure message. Failure messages +* may be truncated for our failure lists. +*/ @kotlinx.rpc.grpc.codec.WithCodec(com.google.protobuf.conformance.TestStatusInternal.CODEC::class) interface TestStatus { val name: String val failureMessage: String + /** + * What an actual test name matched to in a failure list. Can be wildcarded or + * an exact match without wildcards. + */ val matchedName: String companion object } +/** +* The conformance runner will request a list of failures as the first request. +* This will be known by message_type == "conformance.FailureSet", a conformance +* test should return a serialized FailureSet in protobuf_payload. +*/ @kotlinx.rpc.grpc.codec.WithCodec(com.google.protobuf.conformance.FailureSetInternal.CODEC::class) interface FailureSet { val test: List @@ -20,12 +34,42 @@ interface FailureSet { companion object } +/** +* Represents a single test case's input. The testee should: +* +* 1. parse this proto (which should always succeed) +* 2. parse the protobuf or JSON payload in "payload" (which may fail) +* 3. if the parse succeeded, serialize the message in the requested format. +*/ @kotlinx.rpc.grpc.codec.WithCodec(com.google.protobuf.conformance.ConformanceRequestInternal.CODEC::class) interface ConformanceRequest { + /** + * Which format should the testee serialize its message to? + */ val requestedOutputFormat: com.google.protobuf.conformance.WireFormat + /** + * The full name for the test message to use; for the moment, either: + * protobuf_test_messages.proto3.TestAllTypesProto3 or + * protobuf_test_messages.proto2.TestAllTypesProto2 or + * protobuf_test_messages.editions.proto2.TestAllTypesProto2 or + * protobuf_test_messages.editions.proto3.TestAllTypesProto3 or + * protobuf_test_messages.editions.TestAllTypesEdition2023. + */ val messageType: String + /** + * Each test is given a specific test category. Some category may need + * specific support in testee programs. Refer to the definition of + * TestCategory for more information. + */ val testCategory: com.google.protobuf.conformance.TestCategory + /** + * Specify details for how to encode jspb. + */ val jspbEncodingOptions: com.google.protobuf.conformance.JspbEncodingConfig + /** + * This can be used in json and text format. If true, testee should print + * unknown fields instead of ignore. This feature is optional. + */ val printUnknownFields: Boolean val payload: com.google.protobuf.conformance.ConformanceRequest.Payload? @@ -36,6 +80,9 @@ interface ConformanceRequest { @JvmInline value class JsonPayload(val value: String): Payload + /** + * Only used inside Google. Opensource testees just skip it. + */ @JvmInline value class JspbPayload(val value: String): Payload @@ -46,35 +93,81 @@ interface ConformanceRequest { companion object } +/** +* Represents a single test case's output. +*/ @kotlinx.rpc.grpc.codec.WithCodec(com.google.protobuf.conformance.ConformanceResponseInternal.CODEC::class) interface ConformanceResponse { val result: com.google.protobuf.conformance.ConformanceResponse.Result? sealed interface Result { + /** + * This string should be set to indicate parsing failed. The string can + * provide more information about the parse error if it is available. + * + * Setting this string does not necessarily mean the testee failed the + * test. Some of the test cases are intentionally invalid input. + */ @JvmInline value class ParseError(val value: String): Result + /** + * If the input was successfully parsed but errors occurred when + * serializing it to the requested output format, set the error message in + * this field. + */ @JvmInline value class SerializeError(val value: String): Result + /** + * This should be set if the test program timed out. The string should + * provide more information about what the child process was doing when it + * was killed. + */ @JvmInline value class TimeoutError(val value: String): Result + /** + * This should be set if some other error occurred. This will always + * indicate that the test failed. The string can provide more information + * about the failure. + */ @JvmInline value class RuntimeError(val value: String): Result + /** + * If the input was successfully parsed and the requested output was + * protobuf, serialize it to protobuf and set it in this field. + */ @JvmInline value class ProtobufPayload(val value: ByteArray): Result + /** + * If the input was successfully parsed and the requested output was JSON, + * serialize to JSON and set it in this field. + */ @JvmInline value class JsonPayload(val value: String): Result + /** + * For when the testee skipped the test, likely because a certain feature + * wasn't supported, like JSON input/output. + */ @JvmInline value class Skipped(val value: String): Result + /** + * If the input was successfully parsed and the requested output was JSPB, + * serialize to JSPB and set it in this field. JSPB is only used inside + * Google. Opensource testees can just skip it. + */ @JvmInline value class JspbPayload(val value: String): Result + /** + * If the input was successfully parsed and the requested output was + * TEXT_FORMAT, serialize to TEXT_FORMAT and set it in this field. + */ @JvmInline value class TextPayload(val value: String): Result } @@ -82,13 +175,39 @@ interface ConformanceResponse { companion object } +/** +* Encoding options for jspb format. +*/ @kotlinx.rpc.grpc.codec.WithCodec(com.google.protobuf.conformance.JspbEncodingConfigInternal.CODEC::class) interface JspbEncodingConfig { + /** + * Encode the value field of Any as jspb array if true, otherwise binary. + */ val useJspbArrayAnyFormat: Boolean companion object } +/** +* This defines the conformance testing protocol. This protocol exists between +* the conformance test suite itself and the code being tested. For each test, +* the suite will send a ConformanceRequest message and expect a +* ConformanceResponse message. +* +* You can either run the tests in two different ways: +* +* 1. in-process (using the interface in conformance_test.h). +* +* 2. as a sub-process communicating over a pipe. Information about how to +* do this is in conformance_test_runner.cc. +* +* Pros/cons of the two approaches: +* +* - running as a sub-process is much simpler for languages other than C/C++. +* +* - running as a sub-process may be more tricky in unusual environments like +* iOS apps, where fork/stdin/stdout are not available. +*/ sealed class WireFormat(open val number: Int) { object UNSPECIFIED: WireFormat(number = 0) @@ -96,6 +215,9 @@ sealed class WireFormat(open val number: Int) { object JSON: WireFormat(number = 2) + /** + * Only used inside Google. Opensource testees just skip it. + */ object JSPB: WireFormat(number = 3) object TEXT_FORMAT: WireFormat(number = 4) @@ -110,14 +232,35 @@ sealed class WireFormat(open val number: Int) { sealed class TestCategory(open val number: Int) { object UNSPECIFIED_TEST: TestCategory(number = 0) + /** + * Test binary wire format. + */ object BINARY_TEST: TestCategory(number = 1) + /** + * Test json wire format. + */ object JSON_TEST: TestCategory(number = 2) + /** + * Similar to JSON_TEST. However, during parsing json, testee should ignore + * unknown fields. This feature is optional. Each implementation can decide + * whether to support it. See + * https://developers.google.com/protocol-buffers/docs/proto3#json_options + * for more detail. + */ object JSON_IGNORE_UNKNOWN_PARSING_TEST: TestCategory(number = 3) + /** + * Test jspb wire format. Only used inside Google. Opensource testees just + * skip it. + */ object JSPB_TEST: TestCategory(number = 4) + /** + * Test text format. For cpp, java and python, testees can already deal with + * this type. Testees of other languages can simply skip it. + */ object TEXT_FORMAT_TEST: TestCategory(number = 5) data class UNRECOGNIZED(override val number: Int): TestCategory(number) @@ -126,4 +269,3 @@ sealed class TestCategory(open val number: Int) { val entries: List by lazy { listOf(UNSPECIFIED_TEST, BINARY_TEST, JSON_TEST, JSON_IGNORE_UNKNOWN_PARSING_TEST, JSPB_TEST, TEXT_FORMAT_TEST) } } } - diff --git a/tests/protobuf-conformance/src/main/generated-code/kotlin-multiplatform/com/google/protobuf/conformance/_rpc_internal/Conformance.kt b/tests/protobuf-conformance/src/main/generated-code/kotlin-multiplatform/com/google/protobuf/conformance/_rpc_internal/Conformance.kt index 0756143b7..05f52db45 100644 --- a/tests/protobuf-conformance/src/main/generated-code/kotlin-multiplatform/com/google/protobuf/conformance/_rpc_internal/Conformance.kt +++ b/tests/protobuf-conformance/src/main/generated-code/kotlin-multiplatform/com/google/protobuf/conformance/_rpc_internal/Conformance.kt @@ -758,4 +758,3 @@ fun com.google.protobuf.conformance.TestCategory.Companion.fromNumber(number: In } } } - diff --git a/tests/protobuf-conformance/src/main/generated-code/kotlin-multiplatform/com/google/protobuf_test_messages/edition2023/TestMessagesEdition2023.kt b/tests/protobuf-conformance/src/main/generated-code/kotlin-multiplatform/com/google/protobuf_test_messages/edition2023/TestMessagesEdition2023.kt index b63084cdf..4cf77ae78 100644 --- a/tests/protobuf-conformance/src/main/generated-code/kotlin-multiplatform/com/google/protobuf_test_messages/edition2023/TestMessagesEdition2023.kt +++ b/tests/protobuf-conformance/src/main/generated-code/kotlin-multiplatform/com/google/protobuf_test_messages/edition2023/TestMessagesEdition2023.kt @@ -13,6 +13,9 @@ interface ComplexMessage { @kotlinx.rpc.grpc.codec.WithCodec(com.google.protobuf_test_messages.edition2023.TestAllTypesEdition2023Internal.CODEC::class) interface TestAllTypesEdition2023 { + /** + * Singular + */ val optionalInt32: Int? val optionalInt64: Long? val optionalUint32: UInt? @@ -35,6 +38,9 @@ interface TestAllTypesEdition2023 { val optionalStringPiece: String? val optionalCord: String? val recursiveMessage: com.google.protobuf_test_messages.edition2023.TestAllTypesEdition2023 + /** + * Repeated + */ val repeatedInt32: List val repeatedInt64: List val repeatedUint32: List @@ -56,6 +62,9 @@ interface TestAllTypesEdition2023 { val repeatedForeignEnum: List val repeatedStringPiece: List val repeatedCord: List + /** + * Packed + */ val packedInt32: List val packedInt64: List val packedUint32: List @@ -70,6 +79,9 @@ interface TestAllTypesEdition2023 { val packedDouble: List val packedBool: List val packedNestedEnum: List + /** + * Unpacked + */ val unpackedInt32: List val unpackedInt64: List val unpackedUint32: List @@ -84,6 +96,9 @@ interface TestAllTypesEdition2023 { val unpackedDouble: List val unpackedBool: List val unpackedNestedEnum: List + /** + * Map + */ val mapInt32Int32: Map val mapInt64Int64: Map val mapUint32Uint32: Map @@ -148,6 +163,9 @@ interface TestAllTypesEdition2023 { companion object } + /** + * groups + */ interface GroupLikeType { val groupInt32: Int? val groupUint32: UInt? @@ -162,6 +180,9 @@ interface TestAllTypesEdition2023 { object BAZ: NestedEnum(number = 2) + /** + * Intentionally negative. + */ object NEG: NestedEnum(number = -1) data class UNRECOGNIZED(override val number: Int): NestedEnum(number) @@ -201,4 +222,3 @@ sealed class ForeignEnumEdition2023(open val number: Int) { val entries: List by lazy { listOf(FOREIGN_FOO, FOREIGN_BAR, FOREIGN_BAZ) } } } - diff --git a/tests/protobuf-conformance/src/main/generated-code/kotlin-multiplatform/com/google/protobuf_test_messages/edition2023/_rpc_internal/TestMessagesEdition2023.kt b/tests/protobuf-conformance/src/main/generated-code/kotlin-multiplatform/com/google/protobuf_test_messages/edition2023/_rpc_internal/TestMessagesEdition2023.kt index 9034d52b4..953cce500 100644 --- a/tests/protobuf-conformance/src/main/generated-code/kotlin-multiplatform/com/google/protobuf_test_messages/edition2023/_rpc_internal/TestMessagesEdition2023.kt +++ b/tests/protobuf-conformance/src/main/generated-code/kotlin-multiplatform/com/google/protobuf_test_messages/edition2023/_rpc_internal/TestMessagesEdition2023.kt @@ -4026,4 +4026,3 @@ fun com.google.protobuf_test_messages.edition2023.TestAllTypesEdition2023.Nested } } } - diff --git a/tests/protobuf-conformance/src/main/generated-code/kotlin-multiplatform/com/google/protobuf_test_messages/editions/proto2/TestMessagesProto2Editions.kt b/tests/protobuf-conformance/src/main/generated-code/kotlin-multiplatform/com/google/protobuf_test_messages/editions/proto2/TestMessagesProto2Editions.kt index ffe935288..f6e640aea 100644 --- a/tests/protobuf-conformance/src/main/generated-code/kotlin-multiplatform/com/google/protobuf_test_messages/editions/proto2/TestMessagesProto2Editions.kt +++ b/tests/protobuf-conformance/src/main/generated-code/kotlin-multiplatform/com/google/protobuf_test_messages/editions/proto2/TestMessagesProto2Editions.kt @@ -4,8 +4,20 @@ package com.google.protobuf_test_messages.editions.proto2 import kotlin.jvm.JvmInline import kotlinx.rpc.internal.utils.* +/** +* This proto includes every type of field in both singular and repeated +* forms. +* +* Also, crucially, all messages and enums in this file are eventually +* submessages of this message. So for example, a fuzz test of TestAllTypes +* could trigger bugs that occur in any message type in this file. We verify +* this stays true in a unit test. +*/ @kotlinx.rpc.grpc.codec.WithCodec(com.google.protobuf_test_messages.editions.proto2.TestAllTypesProto2Internal.CODEC::class) interface TestAllTypesProto2 { + /** + * Singular + */ val optionalInt32: Int? val optionalInt64: Long? val optionalUint32: UInt? @@ -28,6 +40,9 @@ interface TestAllTypesProto2 { val optionalStringPiece: String? val optionalCord: String? val recursiveMessage: com.google.protobuf_test_messages.editions.proto2.TestAllTypesProto2 + /** + * Repeated + */ val repeatedInt32: List val repeatedInt64: List val repeatedUint32: List @@ -49,6 +64,9 @@ interface TestAllTypesProto2 { val repeatedForeignEnum: List val repeatedStringPiece: List val repeatedCord: List + /** + * Packed + */ val packedInt32: List val packedInt64: List val packedUint32: List @@ -63,6 +81,9 @@ interface TestAllTypesProto2 { val packedDouble: List val packedBool: List val packedNestedEnum: List + /** + * Unpacked + */ val unpackedInt32: List val unpackedInt64: List val unpackedUint32: List @@ -77,6 +98,9 @@ interface TestAllTypesProto2 { val unpackedDouble: List val unpackedBool: List val unpackedNestedEnum: List + /** + * Map + */ val mapInt32Int32: Map val mapInt64Int64: Map val mapUint32Uint32: Map @@ -100,6 +124,9 @@ interface TestAllTypesProto2 { val mapStringForeignEnum: Map val data: com.google.protobuf_test_messages.editions.proto2.TestAllTypesProto2.Data val multiwordgroupfield: com.google.protobuf_test_messages.editions.proto2.TestAllTypesProto2.MultiWordGroupField + /** + * default values + */ val defaultInt32: Int val defaultInt64: Long val defaultUint32: UInt @@ -115,6 +142,10 @@ interface TestAllTypesProto2 { val defaultBool: Boolean val defaultString: String val defaultBytes: ByteArray + /** + * Test field-name-to-JSON-name convention. + * (protobuf says names can be any valid C/C++ identifier.) + */ val fieldname1: Int? val fieldName2: Int? val FieldName3: Int? @@ -177,6 +208,9 @@ interface TestAllTypesProto2 { companion object } + /** + * groups + */ interface Data { val groupInt32: Int? val groupUint32: UInt? @@ -191,6 +225,9 @@ interface TestAllTypesProto2 { companion object } + /** + * message_set test case. + */ @kotlinx.rpc.grpc.codec.WithCodec(com.google.protobuf_test_messages.editions.proto2.TestAllTypesProto2Internal.MessageSetCorrectInternal.CODEC::class) interface MessageSetCorrect { companion object @@ -232,6 +269,9 @@ interface TestAllTypesProto2 { object BAZ: NestedEnum(number = 2) + /** + * Intentionally negative. + */ object NEG: NestedEnum(number = -1) data class UNRECOGNIZED(override val number: Int): NestedEnum(number) @@ -317,6 +357,9 @@ interface ProtoWithKeywords { @kotlinx.rpc.grpc.codec.WithCodec(com.google.protobuf_test_messages.editions.proto2.TestAllRequiredTypesProto2Internal.CODEC::class) interface TestAllRequiredTypesProto2 { + /** + * Singular + */ val requiredInt32: Int val requiredInt64: Long val requiredUint32: UInt @@ -341,6 +384,9 @@ interface TestAllRequiredTypesProto2 { val recursiveMessage: com.google.protobuf_test_messages.editions.proto2.TestAllRequiredTypesProto2 val optionalRecursiveMessage: com.google.protobuf_test_messages.editions.proto2.TestAllRequiredTypesProto2 val data: com.google.protobuf_test_messages.editions.proto2.TestAllRequiredTypesProto2.Data + /** + * default values + */ val defaultInt32: Int val defaultInt64: Long val defaultUint32: UInt @@ -366,6 +412,9 @@ interface TestAllRequiredTypesProto2 { companion object } + /** + * groups + */ interface Data { val groupInt32: Int val groupUint32: UInt @@ -373,6 +422,9 @@ interface TestAllRequiredTypesProto2 { companion object } + /** + * message_set test case. + */ @kotlinx.rpc.grpc.codec.WithCodec(com.google.protobuf_test_messages.editions.proto2.TestAllRequiredTypesProto2Internal.MessageSetCorrectInternal.CODEC::class) interface MessageSetCorrect { companion object @@ -399,6 +451,9 @@ interface TestAllRequiredTypesProto2 { object BAZ: NestedEnum(number = 2) + /** + * Intentionally negative. + */ object NEG: NestedEnum(number = -1) data class UNRECOGNIZED(override val number: Int): NestedEnum(number) @@ -483,4 +538,3 @@ sealed class ForeignEnumProto2(open val number: Int) { val entries: List by lazy { listOf(FOREIGN_FOO, FOREIGN_BAR, FOREIGN_BAZ) } } } - diff --git a/tests/protobuf-conformance/src/main/generated-code/kotlin-multiplatform/com/google/protobuf_test_messages/editions/proto2/_rpc_internal/TestMessagesProto2Editions.kt b/tests/protobuf-conformance/src/main/generated-code/kotlin-multiplatform/com/google/protobuf_test_messages/editions/proto2/_rpc_internal/TestMessagesProto2Editions.kt index da51cfc01..4c6d138d2 100644 --- a/tests/protobuf-conformance/src/main/generated-code/kotlin-multiplatform/com/google/protobuf_test_messages/editions/proto2/_rpc_internal/TestMessagesProto2Editions.kt +++ b/tests/protobuf-conformance/src/main/generated-code/kotlin-multiplatform/com/google/protobuf_test_messages/editions/proto2/_rpc_internal/TestMessagesProto2Editions.kt @@ -7665,4 +7665,3 @@ fun com.google.protobuf_test_messages.editions.proto2.TestAllRequiredTypesProto2 } } } - diff --git a/tests/protobuf-conformance/src/main/generated-code/kotlin-multiplatform/com/google/protobuf_test_messages/editions/proto3/TestMessagesProto3Editions.kt b/tests/protobuf-conformance/src/main/generated-code/kotlin-multiplatform/com/google/protobuf_test_messages/editions/proto3/TestMessagesProto3Editions.kt index 7298f4a98..0b25ee4cf 100644 --- a/tests/protobuf-conformance/src/main/generated-code/kotlin-multiplatform/com/google/protobuf_test_messages/editions/proto3/TestMessagesProto3Editions.kt +++ b/tests/protobuf-conformance/src/main/generated-code/kotlin-multiplatform/com/google/protobuf_test_messages/editions/proto3/TestMessagesProto3Editions.kt @@ -5,8 +5,21 @@ import com.google.protobuf.kotlin.* import kotlin.jvm.JvmInline import kotlinx.rpc.internal.utils.* +/** +* This proto includes every type of field in both singular and repeated +* forms. +* +* Also, crucially, all messages and enums in this file are eventually +* submessages of this message. So for example, a fuzz test of TestAllTypes +* could trigger bugs that occur in any message type in this file. We verify +* this stays true in a unit test. +*/ @kotlinx.rpc.grpc.codec.WithCodec(com.google.protobuf_test_messages.editions.proto3.TestAllTypesProto3Internal.CODEC::class) interface TestAllTypesProto3 { + /** + * Singular + * test [kotlin] comment + */ val optionalInt32: Int val optionalInt64: Long val optionalUint32: UInt @@ -30,6 +43,9 @@ interface TestAllTypesProto3 { val optionalStringPiece: String val optionalCord: String val recursiveMessage: com.google.protobuf_test_messages.editions.proto3.TestAllTypesProto3 + /** + * Repeated + */ val repeatedInt32: List val repeatedInt64: List val repeatedUint32: List @@ -51,6 +67,9 @@ interface TestAllTypesProto3 { val repeatedForeignEnum: List val repeatedStringPiece: List val repeatedCord: List + /** + * Packed + */ val packedInt32: List val packedInt64: List val packedUint32: List @@ -65,6 +84,9 @@ interface TestAllTypesProto3 { val packedDouble: List val packedBool: List val packedNestedEnum: List + /** + * Unpacked + */ val unpackedInt32: List val unpackedInt64: List val unpackedUint32: List @@ -79,6 +101,9 @@ interface TestAllTypesProto3 { val unpackedDouble: List val unpackedBool: List val unpackedNestedEnum: List + /** + * Map + */ val mapInt32Int32: Map val mapInt64Int64: Map val mapUint32Uint32: Map @@ -98,6 +123,9 @@ interface TestAllTypesProto3 { val mapStringForeignMessage: Map val mapStringNestedEnum: Map val mapStringForeignEnum: Map + /** + * Well-known types + */ val optionalBoolWrapper: com.google.protobuf.kotlin.BoolValue val optionalInt32Wrapper: com.google.protobuf.kotlin.Int32Value val optionalInt64Wrapper: com.google.protobuf.kotlin.Int64Value @@ -130,6 +158,10 @@ interface TestAllTypesProto3 { val repeatedAny: List val repeatedValue: List val repeatedListValue: List + /** + * Test field-name-to-JSON-name convention. + * (protobuf says names can be any valid C/C++ identifier.) + */ val fieldname1: Int val fieldName2: Int val FieldName3: Int @@ -203,6 +235,9 @@ interface TestAllTypesProto3 { object BAZ: NestedEnum(number = 2) + /** + * Intentionally negative. + */ object NEG: NestedEnum(number = -1) data class UNRECOGNIZED(override val number: Int): NestedEnum(number) @@ -277,4 +312,3 @@ sealed class ForeignEnum(open val number: Int) { val entries: List by lazy { listOf(FOREIGN_FOO, FOREIGN_BAR, FOREIGN_BAZ) } } } - diff --git a/tests/protobuf-conformance/src/main/generated-code/kotlin-multiplatform/com/google/protobuf_test_messages/editions/proto3/_rpc_internal/TestMessagesProto3Editions.kt b/tests/protobuf-conformance/src/main/generated-code/kotlin-multiplatform/com/google/protobuf_test_messages/editions/proto3/_rpc_internal/TestMessagesProto3Editions.kt index fb1f82144..a612d1155 100644 --- a/tests/protobuf-conformance/src/main/generated-code/kotlin-multiplatform/com/google/protobuf_test_messages/editions/proto3/_rpc_internal/TestMessagesProto3Editions.kt +++ b/tests/protobuf-conformance/src/main/generated-code/kotlin-multiplatform/com/google/protobuf_test_messages/editions/proto3/_rpc_internal/TestMessagesProto3Editions.kt @@ -4731,4 +4731,3 @@ fun com.google.protobuf_test_messages.editions.proto3.EnumOnlyProto3.Bool.Compan } } } - diff --git a/tests/protobuf-conformance/src/main/generated-code/kotlin-multiplatform/com/google/protobuf_test_messages/proto2/TestMessagesProto2.kt b/tests/protobuf-conformance/src/main/generated-code/kotlin-multiplatform/com/google/protobuf_test_messages/proto2/TestMessagesProto2.kt index f2cf4f773..4de9607b0 100644 --- a/tests/protobuf-conformance/src/main/generated-code/kotlin-multiplatform/com/google/protobuf_test_messages/proto2/TestMessagesProto2.kt +++ b/tests/protobuf-conformance/src/main/generated-code/kotlin-multiplatform/com/google/protobuf_test_messages/proto2/TestMessagesProto2.kt @@ -4,8 +4,20 @@ package com.google.protobuf_test_messages.proto2 import kotlin.jvm.JvmInline import kotlinx.rpc.internal.utils.* +/** +* This proto includes every type of field in both singular and repeated +* forms. +* +* Also, crucially, all messages and enums in this file are eventually +* submessages of this message. So for example, a fuzz test of TestAllTypes +* could trigger bugs that occur in any message type in this file. We verify +* this stays true in a unit test. +*/ @kotlinx.rpc.grpc.codec.WithCodec(com.google.protobuf_test_messages.proto2.TestAllTypesProto2Internal.CODEC::class) interface TestAllTypesProto2 { + /** + * Singular + */ val optionalInt32: Int? val optionalInt64: Long? val optionalUint32: UInt? @@ -28,6 +40,9 @@ interface TestAllTypesProto2 { val optionalStringPiece: String? val optionalCord: String? val recursiveMessage: com.google.protobuf_test_messages.proto2.TestAllTypesProto2 + /** + * Repeated + */ val repeatedInt32: List val repeatedInt64: List val repeatedUint32: List @@ -49,6 +64,9 @@ interface TestAllTypesProto2 { val repeatedForeignEnum: List val repeatedStringPiece: List val repeatedCord: List + /** + * Packed + */ val packedInt32: List val packedInt64: List val packedUint32: List @@ -63,6 +81,9 @@ interface TestAllTypesProto2 { val packedDouble: List val packedBool: List val packedNestedEnum: List + /** + * Unpacked + */ val unpackedInt32: List val unpackedInt64: List val unpackedUint32: List @@ -77,6 +98,9 @@ interface TestAllTypesProto2 { val unpackedDouble: List val unpackedBool: List val unpackedNestedEnum: List + /** + * Map + */ val mapInt32Int32: Map val mapInt64Int64: Map val mapUint32Uint32: Map @@ -100,6 +124,9 @@ interface TestAllTypesProto2 { val mapStringForeignEnum: Map val data: com.google.protobuf_test_messages.proto2.TestAllTypesProto2.Data val multiwordgroupfield: com.google.protobuf_test_messages.proto2.TestAllTypesProto2.MultiWordGroupField + /** + * default values + */ val defaultInt32: Int val defaultInt64: Long val defaultUint32: UInt @@ -115,6 +142,10 @@ interface TestAllTypesProto2 { val defaultBool: Boolean val defaultString: String val defaultBytes: ByteArray + /** + * Test field-name-to-JSON-name convention. + * (protobuf says names can be any valid C/C++ identifier.) + */ val fieldname1: Int? val fieldName2: Int? val FieldName3: Int? @@ -177,6 +208,9 @@ interface TestAllTypesProto2 { companion object } + /** + * groups + */ interface Data { val groupInt32: Int? val groupUint32: UInt? @@ -191,6 +225,9 @@ interface TestAllTypesProto2 { companion object } + /** + * message_set test case. + */ @kotlinx.rpc.grpc.codec.WithCodec(com.google.protobuf_test_messages.proto2.TestAllTypesProto2Internal.MessageSetCorrectInternal.CODEC::class) interface MessageSetCorrect { companion object @@ -232,6 +269,9 @@ interface TestAllTypesProto2 { object BAZ: NestedEnum(number = 2) + /** + * Intentionally negative. + */ object NEG: NestedEnum(number = -1) data class UNRECOGNIZED(override val number: Int): NestedEnum(number) @@ -317,6 +357,9 @@ interface ProtoWithKeywords { @kotlinx.rpc.grpc.codec.WithCodec(com.google.protobuf_test_messages.proto2.TestAllRequiredTypesProto2Internal.CODEC::class) interface TestAllRequiredTypesProto2 { + /** + * Singular + */ val requiredInt32: Int val requiredInt64: Long val requiredUint32: UInt @@ -341,6 +384,9 @@ interface TestAllRequiredTypesProto2 { val recursiveMessage: com.google.protobuf_test_messages.proto2.TestAllRequiredTypesProto2 val optionalRecursiveMessage: com.google.protobuf_test_messages.proto2.TestAllRequiredTypesProto2 val data: com.google.protobuf_test_messages.proto2.TestAllRequiredTypesProto2.Data + /** + * default values + */ val defaultInt32: Int val defaultInt64: Long val defaultUint32: UInt @@ -366,6 +412,9 @@ interface TestAllRequiredTypesProto2 { companion object } + /** + * groups + */ interface Data { val groupInt32: Int val groupUint32: UInt @@ -373,6 +422,9 @@ interface TestAllRequiredTypesProto2 { companion object } + /** + * message_set test case. + */ @kotlinx.rpc.grpc.codec.WithCodec(com.google.protobuf_test_messages.proto2.TestAllRequiredTypesProto2Internal.MessageSetCorrectInternal.CODEC::class) interface MessageSetCorrect { companion object @@ -399,6 +451,9 @@ interface TestAllRequiredTypesProto2 { object BAZ: NestedEnum(number = 2) + /** + * Intentionally negative. + */ object NEG: NestedEnum(number = -1) data class UNRECOGNIZED(override val number: Int): NestedEnum(number) @@ -483,4 +538,3 @@ sealed class ForeignEnumProto2(open val number: Int) { val entries: List by lazy { listOf(FOREIGN_FOO, FOREIGN_BAR, FOREIGN_BAZ) } } } - diff --git a/tests/protobuf-conformance/src/main/generated-code/kotlin-multiplatform/com/google/protobuf_test_messages/proto2/_rpc_internal/TestMessagesProto2.kt b/tests/protobuf-conformance/src/main/generated-code/kotlin-multiplatform/com/google/protobuf_test_messages/proto2/_rpc_internal/TestMessagesProto2.kt index ee0d4f805..d8b2b8a03 100644 --- a/tests/protobuf-conformance/src/main/generated-code/kotlin-multiplatform/com/google/protobuf_test_messages/proto2/_rpc_internal/TestMessagesProto2.kt +++ b/tests/protobuf-conformance/src/main/generated-code/kotlin-multiplatform/com/google/protobuf_test_messages/proto2/_rpc_internal/TestMessagesProto2.kt @@ -7665,4 +7665,3 @@ fun com.google.protobuf_test_messages.proto2.TestAllRequiredTypesProto2.NestedEn } } } - diff --git a/tests/protobuf-conformance/src/main/generated-code/kotlin-multiplatform/com/google/protobuf_test_messages/proto3/TestMessagesProto3.kt b/tests/protobuf-conformance/src/main/generated-code/kotlin-multiplatform/com/google/protobuf_test_messages/proto3/TestMessagesProto3.kt index 34653a157..74d236b77 100644 --- a/tests/protobuf-conformance/src/main/generated-code/kotlin-multiplatform/com/google/protobuf_test_messages/proto3/TestMessagesProto3.kt +++ b/tests/protobuf-conformance/src/main/generated-code/kotlin-multiplatform/com/google/protobuf_test_messages/proto3/TestMessagesProto3.kt @@ -5,8 +5,21 @@ import com.google.protobuf.kotlin.* import kotlin.jvm.JvmInline import kotlinx.rpc.internal.utils.* +/** +* This proto includes every type of field in both singular and repeated +* forms. +* +* Also, crucially, all messages and enums in this file are eventually +* submessages of this message. So for example, a fuzz test of TestAllTypes +* could trigger bugs that occur in any message type in this file. We verify +* this stays true in a unit test. +*/ @kotlinx.rpc.grpc.codec.WithCodec(com.google.protobuf_test_messages.proto3.TestAllTypesProto3Internal.CODEC::class) interface TestAllTypesProto3 { + /** + * Singular + * test [kotlin] comment + */ val optionalInt32: Int val optionalInt64: Long val optionalUint32: UInt @@ -30,6 +43,9 @@ interface TestAllTypesProto3 { val optionalStringPiece: String val optionalCord: String val recursiveMessage: com.google.protobuf_test_messages.proto3.TestAllTypesProto3 + /** + * Repeated + */ val repeatedInt32: List val repeatedInt64: List val repeatedUint32: List @@ -51,6 +67,9 @@ interface TestAllTypesProto3 { val repeatedForeignEnum: List val repeatedStringPiece: List val repeatedCord: List + /** + * Packed + */ val packedInt32: List val packedInt64: List val packedUint32: List @@ -65,6 +84,9 @@ interface TestAllTypesProto3 { val packedDouble: List val packedBool: List val packedNestedEnum: List + /** + * Unpacked + */ val unpackedInt32: List val unpackedInt64: List val unpackedUint32: List @@ -79,6 +101,9 @@ interface TestAllTypesProto3 { val unpackedDouble: List val unpackedBool: List val unpackedNestedEnum: List + /** + * Map + */ val mapInt32Int32: Map val mapInt64Int64: Map val mapUint32Uint32: Map @@ -98,6 +123,9 @@ interface TestAllTypesProto3 { val mapStringForeignMessage: Map val mapStringNestedEnum: Map val mapStringForeignEnum: Map + /** + * Well-known types + */ val optionalBoolWrapper: com.google.protobuf.kotlin.BoolValue val optionalInt32Wrapper: com.google.protobuf.kotlin.Int32Value val optionalInt64Wrapper: com.google.protobuf.kotlin.Int64Value @@ -130,6 +158,10 @@ interface TestAllTypesProto3 { val repeatedAny: List val repeatedValue: List val repeatedListValue: List + /** + * Test field-name-to-JSON-name convention. + * (protobuf says names can be any valid C/C++ identifier.) + */ val fieldname1: Int val fieldName2: Int val FieldName3: Int @@ -203,6 +235,9 @@ interface TestAllTypesProto3 { object BAZ: NestedEnum(number = 2) + /** + * Intentionally negative. + */ object NEG: NestedEnum(number = -1) data class UNRECOGNIZED(override val number: Int): NestedEnum(number) @@ -277,4 +312,3 @@ sealed class ForeignEnum(open val number: Int) { val entries: List by lazy { listOf(FOREIGN_FOO, FOREIGN_BAR, FOREIGN_BAZ) } } } - diff --git a/tests/protobuf-conformance/src/main/generated-code/kotlin-multiplatform/com/google/protobuf_test_messages/proto3/_rpc_internal/TestMessagesProto3.kt b/tests/protobuf-conformance/src/main/generated-code/kotlin-multiplatform/com/google/protobuf_test_messages/proto3/_rpc_internal/TestMessagesProto3.kt index 3def5c591..09b8cb04c 100644 --- a/tests/protobuf-conformance/src/main/generated-code/kotlin-multiplatform/com/google/protobuf_test_messages/proto3/_rpc_internal/TestMessagesProto3.kt +++ b/tests/protobuf-conformance/src/main/generated-code/kotlin-multiplatform/com/google/protobuf_test_messages/proto3/_rpc_internal/TestMessagesProto3.kt @@ -4731,4 +4731,3 @@ fun com.google.protobuf_test_messages.proto3.EnumOnlyProto3.Bool.Companion.fromN } } } - From de53ff94f610a9728cdc158211bfdd3abfcf2d52 Mon Sep 17 00:00:00 2001 From: Alexander Sysoev Date: Fri, 19 Sep 2025 19:30:28 +0200 Subject: [PATCH 3/5] abiDump --- gradle-plugin/api/gradle-plugin.api | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/gradle-plugin/api/gradle-plugin.api b/gradle-plugin/api/gradle-plugin.api index a53d83edc..ea2c953c4 100644 --- a/gradle-plugin/api/gradle-plugin.api +++ b/gradle-plugin/api/gradle-plugin.api @@ -42,6 +42,12 @@ public final class kotlinx/rpc/VersionsKt { public static final field LIBRARY_VERSION Ljava/lang/String; } +public class kotlinx/rpc/buf/BufCommentsExtension { + public fun (Lorg/gradle/api/Project;)V + public final fun getCopyComments ()Lorg/gradle/api/provider/Property; + public final fun getIncludeFileLevelComments ()Lorg/gradle/api/provider/Property; +} + public class kotlinx/rpc/buf/BufExtension { public fun (Lorg/gradle/api/model/ObjectFactory;)V public final fun generate (Lorg/gradle/api/Action;)V @@ -65,9 +71,12 @@ public final class kotlinx/rpc/buf/BufExtension$LogFormat : java/lang/Enum { public class kotlinx/rpc/buf/BufGenerateExtension { public fun (Lorg/gradle/api/Project;)V + public final fun comments (Lorg/gradle/api/Action;)V + public final fun getComments ()Lkotlinx/rpc/buf/BufCommentsExtension; public final fun getErrorFormat ()Lorg/gradle/api/provider/Property; public final fun getIncludeImports ()Lorg/gradle/api/provider/Property; public final fun getIncludeWkt ()Lorg/gradle/api/provider/Property; + public final fun getIndentSize ()Lorg/gradle/api/provider/Property; } public final class kotlinx/rpc/buf/BufGenerateExtension$ErrorFormat : java/lang/Enum { From 6121f879d102c12a3950186054a1dc2700ca06d5 Mon Sep 17 00:00:00 2001 From: Alexander Sysoev Date: Fri, 19 Sep 2025 19:42:34 +0200 Subject: [PATCH 4/5] Update workflows to print diff --- .github/workflows/protobuf-conformance.yml | 2 ++ .github/workflows/protobuf-well-known-types.yml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/.github/workflows/protobuf-conformance.yml b/.github/workflows/protobuf-conformance.yml index 038868999..fb7eff26d 100644 --- a/.github/workflows/protobuf-conformance.yml +++ b/.github/workflows/protobuf-conformance.yml @@ -23,5 +23,7 @@ jobs: run: | if [[ -n "$(git status --porcelain | grep tests/protobuf-conformance/)" ]]; then echo "Protobuf conformance test is not up to date. Please run './gradlew tests:protobuf-conformance:bufGenerateMain' and commit changes" + git status --porcelain + git diff --minimal | cat exit 1 fi diff --git a/.github/workflows/protobuf-well-known-types.yml b/.github/workflows/protobuf-well-known-types.yml index 286e2494b..2e2667743 100644 --- a/.github/workflows/protobuf-well-known-types.yml +++ b/.github/workflows/protobuf-well-known-types.yml @@ -23,5 +23,7 @@ jobs: run: | if [[ -n "$(git status --porcelain | grep protobuf/protobuf-core/)" ]]; then echo "Well-Known Types are not up-to-date. Please run './gradlew :protobuf:protobuf-core:bufGenerateCommonMain' and commit changes" + git status --porcelain + git diff --minimal | cat exit 1 fi From 7bbd1d38505809b22f9afae9a282f14be0393d38 Mon Sep 17 00:00:00 2001 From: Alexander Sysoev Date: Fri, 19 Sep 2025 19:48:34 +0200 Subject: [PATCH 5/5] leftovers --- .../com/google/protobuf/kotlin/Struct.kt | 6 ++++++ .../com/google/protobuf/conformance/Conformance.kt | 10 ++++++++++ 2 files changed, 16 insertions(+) diff --git a/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/Struct.kt b/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/Struct.kt index 52cbed8a7..63608a75d 100644 --- a/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/Struct.kt +++ b/protobuf/protobuf-core/src/commonMain/generated-code/kotlin-multiplatform/com/google/protobuf/kotlin/Struct.kt @@ -34,8 +34,14 @@ public interface Struct { */ @kotlinx.rpc.grpc.codec.WithCodec(com.google.protobuf.kotlin.ValueInternal.CODEC::class) public interface Value { + /** + * The kind of value. + */ public val kind: com.google.protobuf.kotlin.Value.Kind? + /** + * The kind of value. + */ public sealed interface Kind { /** * Represents a null value. diff --git a/tests/protobuf-conformance/src/main/generated-code/kotlin-multiplatform/com/google/protobuf/conformance/Conformance.kt b/tests/protobuf-conformance/src/main/generated-code/kotlin-multiplatform/com/google/protobuf/conformance/Conformance.kt index 8a2bc4343..e9cc2b401 100644 --- a/tests/protobuf-conformance/src/main/generated-code/kotlin-multiplatform/com/google/protobuf/conformance/Conformance.kt +++ b/tests/protobuf-conformance/src/main/generated-code/kotlin-multiplatform/com/google/protobuf/conformance/Conformance.kt @@ -71,8 +71,18 @@ interface ConformanceRequest { * unknown fields instead of ignore. This feature is optional. */ val printUnknownFields: Boolean + /** + * The payload (whether protobuf of JSON) is always for a + * protobuf_test_messages.proto3.TestAllTypes proto (as defined in + * src/google/protobuf/proto3_test_messages.proto). + */ val payload: com.google.protobuf.conformance.ConformanceRequest.Payload? + /** + * The payload (whether protobuf of JSON) is always for a + * protobuf_test_messages.proto3.TestAllTypes proto (as defined in + * src/google/protobuf/proto3_test_messages.proto). + */ sealed interface Payload { @JvmInline value class ProtobufPayload(val value: ByteArray): Payload