Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

package kotlinx.rpc.protobuf.internal

import kotlinx.rpc.internal.utils.InternalRpcApi

public sealed class ProtobufException : RuntimeException {
protected constructor(message: String, cause: Throwable? = null) : super(message, cause)
}
Expand All @@ -13,6 +15,7 @@ public class ProtobufDecodingException : ProtobufException {
public constructor(message: String, cause: Throwable? = null) : super(message, cause)

public companion object Companion {
@InternalRpcApi
public fun missingRequiredField(messageName: String, fieldName: String): ProtobufDecodingException =
ProtobufDecodingException("Message '$messageName' is missing a required field: $fieldName")

Expand Down
3 changes: 2 additions & 1 deletion protobuf/protobuf-core/src/commonTest/proto/test_map.proto
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
syntax = "proto3";
//syntax = "proto3";

package kotlinx.rpc.protobuf.test;

Expand All @@ -7,4 +7,5 @@ import "presence_check.proto";
message TestMap {
map<string, int64> primitives = 1;
map<int32, kotlinx.rpc.protobuf.test.PresenceCheck> messages = 2;
map<int32, int32> map_int32_int32 = 3;
}
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,14 @@ data class FieldDeclaration(

val isPartOfOneof: Boolean = dec.realContainingOneof != null

val isPartOfMapEntry = dec.containingType.options.mapEntry

// aligns with edition settings and backward compatibility with proto2 and proto3
val nullable: Boolean = (dec.hasPresence() && !dec.isRequired && !dec.hasDefaultValue()
&& !dec.isRepeated // repeated fields cannot be nullable (just empty)
&& !isPartOfOneof // upper conditions would match oneof inner fields
&& type !is FieldType.Message // messages must not be null (to conform protobuf standards)
&& !isPartOfMapEntry // map entry fields cannot be null
)
|| type is FieldType.OneOf // all OneOf fields are nullable
val number: Int = dec.number
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -911,25 +911,53 @@ class ModelToProtobufKotlinCommonGenerator(
return type.defaultValue ?: error("No default value for field $name")
}

return when (val value = dec.defaultValue) {
is String -> {
val value = dec.defaultValue
return when {
value is String -> {
"\"$value\""
}

is ByteString -> {
value is ByteString -> {
"BytesDefaults.$name"
}

is Descriptors.EnumValueDescriptor -> {
value is Descriptors.EnumValueDescriptor -> {
value.fqName().safeFullName()
}

value is Int && (type == FieldType.IntegralType.UINT32 || type == FieldType.IntegralType.FIXED32) -> {
Integer.toUnsignedString(value) + "u"
}

value is Long && (type == FieldType.IntegralType.UINT64 || type == FieldType.IntegralType.FIXED64) -> {
java.lang.Long.toUnsignedString(value) + "uL"
}

value is Float -> {
when (value.toString()) {
"Infinity" -> "Float.POSITIVE_INFINITY"
"-Infinity" -> "Float.NEGATIVE_INFINITY"
"NaN" -> "Float.NaN"
else -> value.toString() + "f"
}
}

value is Double -> {
when (value.toString()) {
"Infinity" -> "Double.POSITIVE_INFINITY"
"-Infinity" -> "Double.NEGATIVE_INFINITY"
"NaN" -> "Double.NaN"
else -> value.toString()
}
}

else -> {
"${value}${type.scalarDefaultSuffix()}"
}
}
}


private fun FieldType.decodeEncodeFuncName(): String? = when (this) {
FieldType.IntegralType.STRING -> "String"
FieldType.IntegralType.BYTES -> "Bytes"
Expand Down
9 changes: 3 additions & 6 deletions tests/protobuf-conformance/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,7 @@ tasks.withType<BufGenerateTask>().configureEach {

protoSourceSets {
main {
proto {
exclude("**/test_messages_proto2.proto")
exclude("**/test_messages_proto2_editions.proto")
exclude("**/test_messages_edition2023.proto")
}
proto { }
}
}

Expand Down Expand Up @@ -91,7 +87,8 @@ val generateConformanceTests = tasks.register<JavaExec>("generateConformanceTest
}

val conformanceTest = properties.getOrDefault("conformance.test", "").toString()
val conformanceTestDebug = properties.getOrDefault("conformance.test.debug", "false").toString().toBooleanStrictOrNull() ?: false
val conformanceTestDebug =
properties.getOrDefault("conformance.test.debug", "false").toString().toBooleanStrictOrNull() ?: false

val generateConformanceFileDescriptorSet = tasks
.withType<GenerateConformanceFileDescriptorSet>()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
@file:OptIn(ExperimentalRpcApi::class, InternalRpcApi::class)
package com.google.protobuf_test_messages.edition2023

import kotlin.jvm.JvmInline
import kotlinx.rpc.internal.utils.*

@kotlinx.rpc.grpc.codec.WithCodec(com.google.protobuf_test_messages.edition2023.ComplexMessageInternal.CODEC::class)
interface ComplexMessage {
val d: Int?

companion object
}

@kotlinx.rpc.grpc.codec.WithCodec(com.google.protobuf_test_messages.edition2023.TestAllTypesEdition2023Internal.CODEC::class)
interface TestAllTypesEdition2023 {
val optionalInt32: Int?
val optionalInt64: Long?
val optionalUint32: UInt?
val optionalUint64: ULong?
val optionalSint32: Int?
val optionalSint64: Long?
val optionalFixed32: UInt?
val optionalFixed64: ULong?
val optionalSfixed32: Int?
val optionalSfixed64: Long?
val optionalFloat: Float?
val optionalDouble: Double?
val optionalBool: Boolean?
val optionalString: String?
val optionalBytes: ByteArray?
val optionalNestedMessage: com.google.protobuf_test_messages.edition2023.TestAllTypesEdition2023.NestedMessage
val optionalForeignMessage: com.google.protobuf_test_messages.edition2023.ForeignMessageEdition2023
val optionalNestedEnum: com.google.protobuf_test_messages.edition2023.TestAllTypesEdition2023.NestedEnum?
val optionalForeignEnum: com.google.protobuf_test_messages.edition2023.ForeignEnumEdition2023?
val optionalStringPiece: String?
val optionalCord: String?
val recursiveMessage: com.google.protobuf_test_messages.edition2023.TestAllTypesEdition2023
val repeatedInt32: List<kotlin.Int>
val repeatedInt64: List<kotlin.Long>
val repeatedUint32: List<kotlin.UInt>
val repeatedUint64: List<kotlin.ULong>
val repeatedSint32: List<kotlin.Int>
val repeatedSint64: List<kotlin.Long>
val repeatedFixed32: List<kotlin.UInt>
val repeatedFixed64: List<kotlin.ULong>
val repeatedSfixed32: List<kotlin.Int>
val repeatedSfixed64: List<kotlin.Long>
val repeatedFloat: List<kotlin.Float>
val repeatedDouble: List<kotlin.Double>
val repeatedBool: List<kotlin.Boolean>
val repeatedString: List<kotlin.String>
val repeatedBytes: List<kotlin.ByteArray>
val repeatedNestedMessage: List<com.google.protobuf_test_messages.edition2023.TestAllTypesEdition2023.NestedMessage>
val repeatedForeignMessage: List<com.google.protobuf_test_messages.edition2023.ForeignMessageEdition2023>
val repeatedNestedEnum: List<com.google.protobuf_test_messages.edition2023.TestAllTypesEdition2023.NestedEnum>
val repeatedForeignEnum: List<com.google.protobuf_test_messages.edition2023.ForeignEnumEdition2023>
val repeatedStringPiece: List<kotlin.String>
val repeatedCord: List<kotlin.String>
val packedInt32: List<kotlin.Int>
val packedInt64: List<kotlin.Long>
val packedUint32: List<kotlin.UInt>
val packedUint64: List<kotlin.ULong>
val packedSint32: List<kotlin.Int>
val packedSint64: List<kotlin.Long>
val packedFixed32: List<kotlin.UInt>
val packedFixed64: List<kotlin.ULong>
val packedSfixed32: List<kotlin.Int>
val packedSfixed64: List<kotlin.Long>
val packedFloat: List<kotlin.Float>
val packedDouble: List<kotlin.Double>
val packedBool: List<kotlin.Boolean>
val packedNestedEnum: List<com.google.protobuf_test_messages.edition2023.TestAllTypesEdition2023.NestedEnum>
val unpackedInt32: List<kotlin.Int>
val unpackedInt64: List<kotlin.Long>
val unpackedUint32: List<kotlin.UInt>
val unpackedUint64: List<kotlin.ULong>
val unpackedSint32: List<kotlin.Int>
val unpackedSint64: List<kotlin.Long>
val unpackedFixed32: List<kotlin.UInt>
val unpackedFixed64: List<kotlin.ULong>
val unpackedSfixed32: List<kotlin.Int>
val unpackedSfixed64: List<kotlin.Long>
val unpackedFloat: List<kotlin.Float>
val unpackedDouble: List<kotlin.Double>
val unpackedBool: List<kotlin.Boolean>
val unpackedNestedEnum: List<com.google.protobuf_test_messages.edition2023.TestAllTypesEdition2023.NestedEnum>
val mapInt32Int32: Map<kotlin.Int, kotlin.Int>
val mapInt64Int64: Map<kotlin.Long, kotlin.Long>
val mapUint32Uint32: Map<kotlin.UInt, kotlin.UInt>
val mapUint64Uint64: Map<kotlin.ULong, kotlin.ULong>
val mapSint32Sint32: Map<kotlin.Int, kotlin.Int>
val mapSint64Sint64: Map<kotlin.Long, kotlin.Long>
val mapFixed32Fixed32: Map<kotlin.UInt, kotlin.UInt>
val mapFixed64Fixed64: Map<kotlin.ULong, kotlin.ULong>
val mapSfixed32Sfixed32: Map<kotlin.Int, kotlin.Int>
val mapSfixed64Sfixed64: Map<kotlin.Long, kotlin.Long>
val mapInt32Float: Map<kotlin.Int, kotlin.Float>
val mapInt32Double: Map<kotlin.Int, kotlin.Double>
val mapBoolBool: Map<kotlin.Boolean, kotlin.Boolean>
val mapStringString: Map<kotlin.String, kotlin.String>
val mapStringBytes: Map<kotlin.String, kotlin.ByteArray>
val mapStringNestedMessage: Map<kotlin.String, com.google.protobuf_test_messages.edition2023.TestAllTypesEdition2023.NestedMessage>
val mapStringForeignMessage: Map<kotlin.String, com.google.protobuf_test_messages.edition2023.ForeignMessageEdition2023>
val mapStringNestedEnum: Map<kotlin.String, com.google.protobuf_test_messages.edition2023.TestAllTypesEdition2023.NestedEnum>
val mapStringForeignEnum: Map<kotlin.String, com.google.protobuf_test_messages.edition2023.ForeignEnumEdition2023>
val groupliketype: com.google.protobuf_test_messages.edition2023.TestAllTypesEdition2023.GroupLikeType
val delimitedField: com.google.protobuf_test_messages.edition2023.TestAllTypesEdition2023.GroupLikeType
val oneofField: com.google.protobuf_test_messages.edition2023.TestAllTypesEdition2023.OneofField?

sealed interface OneofField {
@JvmInline
value class OneofUint32(val value: UInt): OneofField

@JvmInline
value class OneofNestedMessage(
val value: com.google.protobuf_test_messages.edition2023.TestAllTypesEdition2023.NestedMessage,
): OneofField

@JvmInline
value class OneofString(val value: String): OneofField

@JvmInline
value class OneofBytes(val value: ByteArray): OneofField

@JvmInline
value class OneofBool(val value: Boolean): OneofField

@JvmInline
value class OneofUint64(val value: ULong): OneofField

@JvmInline
value class OneofFloat(val value: Float): OneofField

@JvmInline
value class OneofDouble(val value: Double): OneofField

@JvmInline
value class OneofEnum(
val value: com.google.protobuf_test_messages.edition2023.TestAllTypesEdition2023.NestedEnum,
): OneofField
}

@kotlinx.rpc.grpc.codec.WithCodec(com.google.protobuf_test_messages.edition2023.TestAllTypesEdition2023Internal.NestedMessageInternal.CODEC::class)
interface NestedMessage {
val a: Int?
val corecursive: com.google.protobuf_test_messages.edition2023.TestAllTypesEdition2023

companion object
}

interface GroupLikeType {
val groupInt32: Int?
val groupUint32: UInt?

companion object
}

sealed class NestedEnum(open val number: Int) {
object FOO: NestedEnum(number = 0)

object BAR: NestedEnum(number = 1)

object BAZ: NestedEnum(number = 2)

object NEG: NestedEnum(number = -1)

data class UNRECOGNIZED(override val number: Int): NestedEnum(number)

companion object {
val entries: List<NestedEnum> by lazy { listOf(NEG, FOO, BAR, BAZ) }
}
}

companion object
}

@kotlinx.rpc.grpc.codec.WithCodec(com.google.protobuf_test_messages.edition2023.ForeignMessageEdition2023Internal.CODEC::class)
interface ForeignMessageEdition2023 {
val c: Int?

companion object
}

@kotlinx.rpc.grpc.codec.WithCodec(com.google.protobuf_test_messages.edition2023.GroupLikeTypeInternal.CODEC::class)
interface GroupLikeType {
val c: Int?

companion object
}

sealed class ForeignEnumEdition2023(open val number: Int) {
object FOREIGN_FOO: ForeignEnumEdition2023(number = 0)

object FOREIGN_BAR: ForeignEnumEdition2023(number = 1)

object FOREIGN_BAZ: ForeignEnumEdition2023(number = 2)

data class UNRECOGNIZED(override val number: Int): ForeignEnumEdition2023(number)

companion object {
val entries: List<ForeignEnumEdition2023> by lazy { listOf(FOREIGN_FOO, FOREIGN_BAR, FOREIGN_BAZ) }
}
}

Loading
Loading