Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 4 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ buildConfigPlugin = "5.7.1"

kotlinPoet = "2.2.0"

textCaseConverter = "2.0.0"

# Misc (Testing, Example Apps)
protobufJvm = "3.25.6"
protobufGradlePlugin = "0.9.5"
Expand Down Expand Up @@ -65,6 +67,8 @@ ktor-core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" }
squareup-kotlinpoet = { group = "com.squareup", name = "kotlinpoet", version.ref = "kotlinPoet" }
squareup-okio = { module = "com.squareup.okio:okio", version.ref = "okio" }

text-case-converter = { group = "dev.turingcomplete", name = "text-case-converter", version.ref = "textCaseConverter" }

kotlin-gradle-plugin = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-plugin", version.ref = "kotlin" }
android-gradle-plugin = { group = "com.android.tools.build", name = "gradle", version.ref = "androidGradlePlugin" }

Expand Down
1 change: 1 addition & 0 deletions kmp-grpc-internal-test/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import io.github.timortel.kmpgrpc.plugin.NamingStrategy
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
import org.gradle.api.tasks.testing.logging.TestLogEvent

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ fun createMessageWithAllTypes() = messageWithEverything {
field4 = 25L
field5 = 3f
field6 = 7.0
field7 = SimpleEnum.ONE
field7 = SimpleEnum.One
field8 = simpleMessage { field1 = "Foo" }

field9List += listOf("Foo", "Bar", "Baz")
Expand All @@ -39,11 +39,11 @@ fun createMessageWithAllTypes() = messageWithEverything {
field12List += listOf(12L, 23424L, 10312313L, -123131L)
field13List += listOf(-1f, 2f, 2.5f, -0.5f)
field14List += listOf(-0.5, 15.0)
field15List += listOf(SimpleEnum.ZERO, SimpleEnum.ZERO, SimpleEnum.ONE, SimpleEnum.TWO)
field15List += listOf(SimpleEnum.Zero, SimpleEnum.Zero, SimpleEnum.One, SimpleEnum.Two)

field16Map += mapOf("foo" to 1, "bar" to -13, "baz" to 112)
field17Map += mapOf(1 to simpleMessage { field1 = "Foo" }, 13 to simpleMessage { field1 = "Baz" })
field18Map += mapOf(-15 to SimpleEnum.ONE, 23 to SimpleEnum.TWO)
field18Map += mapOf(-15 to SimpleEnum.One, 23 to SimpleEnum.Two)

field19 = 12u
field20 = 14uL
Expand Down Expand Up @@ -74,16 +74,16 @@ fun createMessageWithAllExtensions() = ExtensionsTest.MessageWithEveryExtension(
set(ExtensionsTest.field4, 25L)
set(ExtensionsTest.field5, 3f)
set(ExtensionsTest.field6, 7.0)
set(ExtensionsTest.field7, SimpleEnum.ONE)
set(ExtensionsTest.field7, SimpleEnum.One)
set(ExtensionsTest.field8, simpleMessage { field1 = "Foo" })

set(ExtensionsTest.field9, listOf("Foo", "Bar", "Baz"))
set(ExtensionsTest.field10, listOf(true, false, true, true))
set(ExtensionsTest.field11, listOf(1, 2, 3, 4, -12, 1341))
set(ExtensionsTest.field12, listOf(12L, 23424L, 10312313L, -123131L))
set(ExtensionsTest.field13, listOf(-1f, 2f, 2.5f, -0.5f))
set(ExtensionsTest.field14, listOf(-0.5, 15.0))
set(ExtensionsTest.field15, listOf(SimpleEnum.ZERO, SimpleEnum.ZERO, SimpleEnum.ONE, SimpleEnum.TWO))
set(ExtensionsTest.field9List, listOf("Foo", "Bar", "Baz"))
set(ExtensionsTest.field10List, listOf(true, false, true, true))
set(ExtensionsTest.field11List, listOf(1, 2, 3, 4, -12, 1341))
set(ExtensionsTest.field12List, listOf(12L, 23424L, 10312313L, -123131L))
set(ExtensionsTest.field13List, listOf(-1f, 2f, 2.5f, -0.5f))
set(ExtensionsTest.field14List, listOf(-0.5, 15.0))
set(ExtensionsTest.field15List, listOf(SimpleEnum.Zero, SimpleEnum.Zero, SimpleEnum.One, SimpleEnum.Two))

set(ExtensionsTest.field19, 12u)
set(ExtensionsTest.field20, 14uL)
Expand All @@ -95,16 +95,16 @@ fun createMessageWithAllExtensions() = ExtensionsTest.MessageWithEveryExtension(
set(ExtensionsTest.field26, -1353532131L)
set(ExtensionsTest.field27, byteArrayOf(0, -13, 127))

set(ExtensionsTest.field28, listOf(0u, 134u, 35311u))
set(ExtensionsTest.field29, listOf(0uL, 134uL, 353111345134uL))
set(ExtensionsTest.field30, listOf(-134, -145129, 34521431))
set(ExtensionsTest.field31, listOf(-1L, 141341413413L, -134134314131L))
set(ExtensionsTest.field32, listOf(0u, 14234u, 1413413413u))
set(ExtensionsTest.field33, listOf(0uL, 134uL, 353111345134uL))
set(ExtensionsTest.field34, listOf(-14, 0, 1241522))
set(ExtensionsTest.field35, listOf(-154L, 0L, 4514124121L))
set(ExtensionsTest.field28List, listOf(0u, 134u, 35311u))
set(ExtensionsTest.field29List, listOf(0uL, 134uL, 353111345134uL))
set(ExtensionsTest.field30List, listOf(-134, -145129, 34521431))
set(ExtensionsTest.field31List, listOf(-1L, 141341413413L, -134134314131L))
set(ExtensionsTest.field32List, listOf(0u, 14234u, 1413413413u))
set(ExtensionsTest.field33List, listOf(0uL, 134uL, 353111345134uL))
set(ExtensionsTest.field34List, listOf(-14, 0, 1241522))
set(ExtensionsTest.field35List, listOf(-154L, 0L, 4514124121L))
set(
ExtensionsTest.field36, listOf(
ExtensionsTest.field36List, listOf(
byteArrayOf(0, -127, 127),
byteArrayOf(-123, 1, 2),
byteArrayOf(3, 3, -6)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ class DSLBuilderTest {

@Test
fun testCreateEnumMessage() {
val enum = SimpleEnum.ONE
val enum = SimpleEnum.One

val msg = messageWithEnum {
field1 = enum
Expand All @@ -128,7 +128,7 @@ class DSLBuilderTest {

@Test
fun testCreateRepeatedEnumMessage() {
val list = listOf(SimpleEnum.ONE, SimpleEnum.TWO, SimpleEnum.ZERO, SimpleEnum.ONE)
val list = listOf(SimpleEnum.One, SimpleEnum.Two, SimpleEnum.Zero, SimpleEnum.One)

val msg = messageWithRepeatedEnum {
field1List += list
Expand Down Expand Up @@ -191,11 +191,11 @@ class DSLBuilderTest {
@Test
fun testCreateMessageWithEnumMap() {
val map = mapOf(
33 to SimpleEnum.ONE,
12 to SimpleEnum.TWO,
13 to SimpleEnum.TWO,
-12 to SimpleEnum.ONE,
5 to SimpleEnum.ZERO
33 to SimpleEnum.One,
12 to SimpleEnum.Two,
13 to SimpleEnum.Two,
-12 to SimpleEnum.One,
5 to SimpleEnum.Zero
)

val msg = messageWithEnumMap {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ class OpenClosedEnumMapFieldSerializationTest {
2 to 1
),
expectedValues = mapOf(
1 to ClosedEnumTest.ClosedEnum.DEFAULT,
2 to ClosedEnumTest.ClosedEnum.ONE,
1 to ClosedEnumTest.ClosedEnum.Default,
2 to ClosedEnumTest.ClosedEnum.One,
),
expectedUnknownFieldCount = 0
)
Expand All @@ -28,7 +28,7 @@ class OpenClosedEnumMapFieldSerializationTest {
3 to 2
),
expectedValues = mapOf(
2 to ClosedEnumTest.ClosedEnum.ONE
2 to ClosedEnumTest.ClosedEnum.One
),
expectedUnknownFieldCount = 2
)
Expand Down Expand Up @@ -57,8 +57,8 @@ class OpenClosedEnumMapFieldSerializationTest {
2 to 1
),
expectedValues = mapOf(
1 to OpenEnumTest.OpenEnum.DEFAULT,
2 to OpenEnumTest.OpenEnum.ONE,
1 to OpenEnumTest.OpenEnum.Default,
2 to OpenEnumTest.OpenEnum.One,
),
expectedUnknownFieldCount = 0
)
Expand All @@ -71,7 +71,7 @@ class OpenClosedEnumMapFieldSerializationTest {
),
expectedValues = mapOf(
1 to OpenEnumTest.OpenEnum.Unrecognized(-1),
2 to OpenEnumTest.OpenEnum.ONE,
2 to OpenEnumTest.OpenEnum.One,
3 to OpenEnumTest.OpenEnum.Unrecognized(2)
),
expectedUnknownFieldCount = 0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@ class OpenClosedEnumOneOfSerializationTest {
runClosedOneOfDeserializationTest(
UnknownField.Varint(1, 0),
ClosedEnumTest.MessageWithClosedOneOf.A.B(
ClosedEnumTest.ClosedEnum.DEFAULT
ClosedEnumTest.ClosedEnum.Default
),
emptyList()
)
runClosedOneOfDeserializationTest(
UnknownField.Varint(1, 1),
ClosedEnumTest.MessageWithClosedOneOf.A.B(
ClosedEnumTest.ClosedEnum.ONE
ClosedEnumTest.ClosedEnum.One
),
emptyList()
)
Expand Down Expand Up @@ -49,14 +49,14 @@ class OpenClosedEnumOneOfSerializationTest {
runOpenOneOfDeserializationTest(
UnknownField.Varint(1, 0),
OpenEnumTest.MessageWithOpenOneOf.A.B(
OpenEnumTest.OpenEnum.DEFAULT
OpenEnumTest.OpenEnum.Default
),
emptyList()
)
runOpenOneOfDeserializationTest(
UnknownField.Varint(1, 1),
OpenEnumTest.MessageWithOpenOneOf.A.B(
OpenEnumTest.OpenEnum.ONE
OpenEnumTest.OpenEnum.One
),
emptyList()
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,24 @@ class OpenClosedEnumRepeatedFieldSerializationTest {
fun testClosedRepeatedDeserializationScenarios() {
runClosedRepeatedDeserializationTest(
fields = listOf(0),
expectedValues = listOf(ClosedEnumTest.ClosedEnum.DEFAULT),
expectedValues = listOf(ClosedEnumTest.ClosedEnum.Default),
expectedUnknownFields = emptyList()
)

runClosedRepeatedDeserializationTest(
fields = listOf(0, 1, 0),
expectedValues = listOf(
ClosedEnumTest.ClosedEnum.DEFAULT,
ClosedEnumTest.ClosedEnum.ONE,
ClosedEnumTest.ClosedEnum.DEFAULT
ClosedEnumTest.ClosedEnum.Default,
ClosedEnumTest.ClosedEnum.One,
ClosedEnumTest.ClosedEnum.Default
),
expectedUnknownFields = emptyList()
)

runClosedRepeatedDeserializationTest(
fields = listOf(-4, 3, 1),
expectedValues = listOf(
ClosedEnumTest.ClosedEnum.ONE
ClosedEnumTest.ClosedEnum.One
),
expectedUnknownFields = listOf(
UnknownField.Varint(1, -4),
Expand Down Expand Up @@ -68,16 +68,16 @@ class OpenClosedEnumRepeatedFieldSerializationTest {
fun testOpenRepeatedDeserializationScenarios() {
runOpenRepeatedDeserializationTest(
fields = listOf(0),
expectedValues = listOf(OpenEnumTest.OpenEnum.DEFAULT),
expectedValues = listOf(OpenEnumTest.OpenEnum.Default),
expectedUnknownFields = emptyList()
)

runOpenRepeatedDeserializationTest(
fields = listOf(0, 1, 0),
expectedValues = listOf(
OpenEnumTest.OpenEnum.DEFAULT,
OpenEnumTest.OpenEnum.ONE,
OpenEnumTest.OpenEnum.DEFAULT
OpenEnumTest.OpenEnum.Default,
OpenEnumTest.OpenEnum.One,
OpenEnumTest.OpenEnum.Default
),
expectedUnknownFields = emptyList()
)
Expand All @@ -87,7 +87,7 @@ class OpenClosedEnumRepeatedFieldSerializationTest {
expectedValues = listOf(
OpenEnumTest.OpenEnum.Unrecognized(-4),
OpenEnumTest.OpenEnum.Unrecognized(3),
OpenEnumTest.OpenEnum.ONE,
OpenEnumTest.OpenEnum.One,
),
expectedUnknownFields = emptyList()
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ class OpenClosedEnumScalarFieldSerializationTest {

@Test
fun testClosedScalarDeserializationScenarios() {
runClosedScalarDeserializationTest(UnknownField.Varint(1, 0), ClosedEnumTest.ClosedEnum.DEFAULT, emptyList())
runClosedScalarDeserializationTest(UnknownField.Varint(1, 1), ClosedEnumTest.ClosedEnum.ONE, emptyList())
runClosedScalarDeserializationTest(UnknownField.Varint(1, 0), ClosedEnumTest.ClosedEnum.Default, emptyList())
runClosedScalarDeserializationTest(UnknownField.Varint(1, 1), ClosedEnumTest.ClosedEnum.One, emptyList())
runClosedScalarDeserializationTest(
UnknownField.Varint(1, 2),
ClosedEnumTest.ClosedEnum.DEFAULT,
ClosedEnumTest.ClosedEnum.Default,
listOf(UnknownField.Varint(1, 2))
)
}
Expand All @@ -34,8 +34,8 @@ class OpenClosedEnumScalarFieldSerializationTest {

@Test
fun testOpenScalarDeserializationScenarios() {
runOpenScalarDeserializationTest(UnknownField.Varint(1, 0), OpenEnumTest.OpenEnum.DEFAULT, emptyList())
runOpenScalarDeserializationTest(UnknownField.Varint(1, 1), OpenEnumTest.OpenEnum.ONE, emptyList())
runOpenScalarDeserializationTest(UnknownField.Varint(1, 0), OpenEnumTest.OpenEnum.Default, emptyList())
runOpenScalarDeserializationTest(UnknownField.Varint(1, 1), OpenEnumTest.OpenEnum.One, emptyList())
runOpenScalarDeserializationTest(
UnknownField.Varint(1, 2),
OpenEnumTest.OpenEnum.Unrecognized(2),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,14 @@ abstract class DefaultCertificatesRpcTest : ServerTest {
val stub = HelloServiceStub(channelWithCertificate)

val msg = HelloRequest(greeting = "Hello World")
stub.SayHello(msg)
stub.sayHello(msg)
}

@Test
fun testConnectionFailsWithoutCertificates() = runTest {
val stub = HelloServiceStub(channelWithoutCertificates)

val msg = HelloRequest(greeting = "Hello World")
assertFailsWith<StatusException> { stub.SayHello(msg) }
assertFailsWith<StatusException> { stub.sayHello(msg) }
}
}
2 changes: 2 additions & 0 deletions kmp-grpc-plugin/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ dependencies {
implementation(libs.squareup.kotlinpoet)
compileOnly(libs.kotlin.gradle.plugin)

implementation(libs.text.case.converter)

testImplementation(platform(libs.junit.bom))
testImplementation(libs.junit.jupiter)
testRuntimeOnly(libs.junit.platform.launcher)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package io.github.timortel.kmpgrpc.plugin
import io.github.timortel.kmpgrpc.plugin.sourcegeneration.ProtoSourceGenerator
import io.github.timortel.kmpgrpc.plugin.sourcegeneration.SystemInputFile
import org.gradle.api.DefaultTask
import org.gradle.api.GradleException
import org.gradle.api.Project
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.DirectoryProperty
Expand Down Expand Up @@ -46,6 +47,9 @@ abstract class GenerateKmpGrpcSourcesTask : DefaultTask() {
@get:Input
abstract val skipWellKnownExtensions: Property<Boolean>

@get:Input
abstract val namingStrategy: Property<NamingStrategy>

@get:InputDirectory
abstract val generatedSourcesOutputFolder: DirectoryProperty

Expand Down Expand Up @@ -81,6 +85,13 @@ abstract class GenerateKmpGrpcSourcesTask : DefaultTask() {

@TaskAction
fun generateSources() {
if (namingStrategy.get() != NamingStrategy.KOTLIN_IDIOMATIC && includeWellKnownTypes.get()) {
throw GradleException(
"Invalid Configuration: 'includeWellKnownTypes' can only be set to true " +
"if 'namingStrategy' is set to KOTLIN_IDIOMATIC."
)
}

val tsm = targetSourcesMap.get()

val shouldGenerateTargetMap = KmpGrpcExtension.targets.associateWith { target ->
Expand All @@ -100,6 +111,7 @@ abstract class GenerateKmpGrpcSourcesTask : DefaultTask() {
logger = logger,
protoFolders = protoFolders,
shouldGenerateTargetMap = shouldGenerateTargetMap,
namingStrategy = namingStrategy.get(),
commonOutputFolder = getCommonOutputFolder(outputFolder),
jvmOutputFolder = getJVMOutputFolder(outputFolder),
jsOutputFolder = getJSOutputFolder(outputFolder),
Expand All @@ -108,7 +120,6 @@ abstract class GenerateKmpGrpcSourcesTask : DefaultTask() {
internalVisibility = internalVisibility.get()
)


if (includeWellKnownTypes.get() && skipWellKnownExtensions.get()) {
copyWellKnownExtensions()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ open class KmpGrpcExtension @Inject constructor(objects: ObjectFactory) {

/**
* Instructs the plugin to download and include the well known proto-types: https://protobuf.dev/reference/protobuf/google.protobuf/
*
* Can only be used in combination with `namingStrategy` `KOTLIN_IDIOMATIC`.
*/
val includeWellKnownTypes: Property<Boolean> = objects
.property(Boolean::class.java)
Expand All @@ -43,6 +45,24 @@ open class KmpGrpcExtension @Inject constructor(objects: ObjectFactory) {
.property(Boolean::class.java)
.convention(false)

/**
* Configures how Protobuf messages and fields are mapped to Kotlin identifiers.
*
* This property allows you to choose between standard Protobuf naming, idiomatic
* Kotlin style, or the legacy behavior. It affects the generated class names,
* property names, and method names.
*
* Supported strategies:
* - [NamingStrategy.KOTLIN_IDIOMATIC]: (Default) Transforms names to Kotlin conventions
* (e.g., `user_id` -> `userId`, `User_Message` -> `UserMessage`).
* - [NamingStrategy.PROTO_LITERAL]: Preserves the exact names defined in the .proto file.
* - [NamingStrategy.LEGACY]: Preserves .proto names but appends "List" or "Map"
* suffixes to collection types (Behavior from v1.x).
*/
val namingStrategy: Property<NamingStrategy> = objects
.property(NamingStrategy::class.java)
.convention(NamingStrategy.KOTLIN_IDIOMATIC)

fun common(targets: List<String> = listOf("commonMain")) {
targetSourcesMap.put(COMMON, targets)
}
Expand Down
Loading