From 9e3509d9384a4e9408f40d78ed45010cd342f40a Mon Sep 17 00:00:00 2001 From: Tim Ortel <100865202+TimOrtel@users.noreply.github.com> Date: Sun, 25 Feb 2024 20:35:38 +0100 Subject: [PATCH] Upgrade project (#24) * Expose grpc dependencies as API. * Added documentation to the library. * Replace version management with version catalogs. * Update kotlin and agp. * Update to ios 16.4. Apply kotlin 1.9.0 to ios target. * Added docker tests for javascript. * Unify ios/jvm + js. Lib. * More unification. * JS serialization tests pass. * JVM serialization tests pass. * Remove unnecessary js files. * Update to new kotlin source set setup. * Made all tests run * update readme. * Updated dependencies. Removed unnecessary dependencies. * Fix js tests. * Bump to version 0.4.0 --- .dockerignore | 12 + .gitignore | 3 +- Dockerfile | 11 + buildSrc/build.gradle.kts | 34 +- buildSrc/settings.gradle.kts | 9 + buildSrc/src/main/kotlin/PatchedPodGenTask.kt | 310 ------- buildSrc/src/main/kotlin/Versions.kt | 7 - buildSrc/test-server/build.gradle.kts | 94 ++ buildSrc/test-server/settings.gradle.kts | 7 + .../timortel/kmpgrpc/testserver/Main.kt | 5 + .../kmpgrpc/testserver}/TestServer.kt | 13 +- docker-tests/envoy.yml | 61 ++ docker-tests/js-tests.yml | 33 + gradle/libs.versions.toml | 45 + gradle/wrapper/gradle-wrapper.properties | 2 +- grpc-mp-test/build.gradle.kts | 124 +-- .../test/RpcTest.kt | 96 +-- .../test/defaults.kt | 14 +- .../test/IOSSerializationTest.kt | 2 + .../test/IosRpcTest.kt | 6 + .../test/JSSerializationTest.kt | 24 +- .../test/JsRpcTest.kt | 6 + .../test/JVMSerializationTest.kt | 2 + .../test/JvmRpcTest.kt | 6 + .../test/SerializationTest.kt | 16 +- .../test-android-protos/build.gradle.kts | 90 -- .../src/main/AndroidManifest.xml | 2 - grpc-mp-test/test-jvm-protos/build.gradle.kts | 84 -- grpc-multiplatform-lib/build.gradle.kts | 87 +- .../KMChannel.kt | 3 + .../message/DataType.kt | 3 + .../message/KMMessage.kt | 12 + .../metadata_util.kt | 3 + .../stub/AndroidJvmKMStub.kt | 6 +- .../util/TimeUnit.kt | 3 + .../KMChannel.kt | 12 +- .../kotlin_multiplatform_grpc_lib/KMCode.kt | 3 + .../KMMetadata.kt | 14 +- .../kotlin_multiplatform_grpc_lib/KMStatus.kt | 7 +- .../KMStatusException.kt | 6 +- .../io/CodedInputStream.kt | 10 + .../io/CodedOutputStream.kt | 4 + .../io/ParseException.kt | 0 .../kotlin_multiplatform_grpc_lib/io/const.kt | 2 +- .../io/message_reader.kt | 3 + .../io/wireformat.kt | 0 .../message/DataType.kt | 25 + .../message/KMMessage.kt | 5 +- .../message/MessageDeserializer.kt | 3 + .../stub/KMStub.kt | 7 + .../util/TimeUnit.kt | 3 + .../io/map_writer.kt | 24 + .../io/message_writer.kt | 22 - .../KMChannel.kt | 4 + .../io/CodedInputStream.kt | 11 +- .../io/CodedOutputStream.kt | 5 +- .../io/GPBCodedInputStreamWrapper.kt | 3 + .../message/DataType.kt | 3 + .../message/KMMessage.kt | 9 + .../rpc/rpc_implementation.kt | 7 + .../stub/IOSKMStub.kt | 19 + .../util/TimeUnit.kt | 3 + .../kotlin_multiplatform_grpc_lib/JSPB.kt | 126 ++- .../KMChannel.kt | 5 +- .../io/CodedInputStream.kt | 140 +++ .../io/CodedOutputStream.kt | 313 +++++++ .../io/TypeUtil.kt | 5 + .../io/map_writer.kt | 18 + .../io/message_writer.kt | 35 + .../message/DataType.kt | 2 +- .../message/JSImpl.kt | 5 - .../message/KMMessage.kt | 24 +- .../message/MessageDeserializer.kt | 5 - .../rpc/rpc_implementation.kt | 12 +- plugin/build.gradle.kts | 25 +- plugin/settings.gradle.kts | 7 + .../GrpcMultiplatformPlugin.kt | 13 - .../generators/Const.kt | 16 +- .../common/CommonFunctionGenerator.kt | 45 - .../common/JsCommonFunctionGenerator.kt | 14 - .../common/JvmCommonFunctionGenerator.kt | 14 - ...osJvmDslBuilder.kt => ActualDslBuilder.kt} | 7 +- .../generators/dsl/CommonDslBuilder.kt | 2 - .../generators/dsl/JsDslBuilder.kt | 105 --- .../generators/dsl/SubDslBuilder.kt | 171 ---- ....kt => ActualMapMessageMethodGenerator.kt} | 5 +- .../map/CommonMapMessageMethodGenerator.kt | 2 +- .../map/JsMapMessageMethodGenerator.kt | 44 - .../map/MapMessageMethodGenerator.kt | 2 +- .../map/mapper/CommonToJsMapMapper.kt | 45 - .../map/mapper/CommonToJvmMapMapper.kt | 43 - .../map/mapper/JsToCommonMapMapper.kt | 41 - .../map/mapper/JvmToCommonMapMapper.kt | 41 - .../generators/map/mapper/MapMapper.kt | 90 -- .../ActualOneOfMethodAndClassGenerator.kt | 66 ++ .../CommonOneOfMethodAndClassGenerator.kt | 1 - .../IosJvmOneOfMethodAndClassGenerator.kt | 51 +- .../oneof/JsOneOfMethodAndClassGenerator.kt | 54 +- .../oneof/OneOfMethodAndClassGenerator.kt | 3 +- .../generators/proto3_ios_file_writer.kt | 8 +- .../generators/proto3_ios_service_writer.kt | 4 +- .../generators/proto3_js_file_writer.kt | 796 +---------------- .../generators/proto3_js_service_writer.kt | 39 +- .../generators/proto3_jvm_file_writer.kt | 4 +- .../proto_file/ActualProtoFileWriter.kt | 810 ++++++++++++++++++ .../proto_file/CommonProtoFileWriter.kt | 6 +- .../proto_file/IosJvmProtoFileWriteBase.kt | 773 +---------------- ...otoFileWriter.kt => IosProtoFileWriter.kt} | 16 +- .../proto_file/JsProtoFileWriter.kt | 92 +- .../proto_file/JvmProtoFileWriter.kt | 17 +- .../generators/proto_file/ProtoFileWriter.kt | 47 +- ...> ActualRepeatedMessageMethodGenerator.kt} | 2 +- .../JsRepeatedMessageMethodGenerator.kt | 42 - ... => ActualScalarMessageMethodGenerator.kt} | 2 +- .../scalar/JsScalarMessageMethodGenerator.kt | 75 -- ...OSServiceWriter.kt => IosServiceWriter.kt} | 2 +- .../generators/service/JsServiceWriter.kt | 20 +- .../libClasses.kt | 7 +- readme.md | 19 +- settings.gradle.kts | 11 +- 120 files changed, 2553 insertions(+), 3285 deletions(-) create mode 100644 .dockerignore create mode 100644 Dockerfile create mode 100644 buildSrc/settings.gradle.kts delete mode 100644 buildSrc/src/main/kotlin/PatchedPodGenTask.kt delete mode 100644 buildSrc/src/main/kotlin/Versions.kt create mode 100644 buildSrc/test-server/build.gradle.kts create mode 100644 buildSrc/test-server/settings.gradle.kts create mode 100644 buildSrc/test-server/src/main/kotlin/io/github/timortel/kmpgrpc/testserver/Main.kt rename buildSrc/{src/main/kotlin => test-server/src/main/kotlin/io/github/timortel/kmpgrpc/testserver}/TestServer.kt (88%) create mode 100644 docker-tests/envoy.yml create mode 100644 docker-tests/js-tests.yml create mode 100644 gradle/libs.versions.toml create mode 100644 grpc-mp-test/src/iosTest/kotlin/io/github/timortel/kotlin_multiplatform_grpc_plugin/test/IosRpcTest.kt create mode 100644 grpc-mp-test/src/jsTest/kotlin/io/github/timortel/kotlin_multiplatform_grpc_plugin/test/JsRpcTest.kt create mode 100644 grpc-mp-test/src/jvmTest/kotlin/io/github/timortel/kotlin_multiplatform_grpc_plugin/test/JvmRpcTest.kt delete mode 100644 grpc-mp-test/test-android-protos/build.gradle.kts delete mode 100644 grpc-mp-test/test-android-protos/src/main/AndroidManifest.xml delete mode 100644 grpc-mp-test/test-jvm-protos/build.gradle.kts rename grpc-multiplatform-lib/src/{iosJvmCommon => commonMain}/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/CodedInputStream.kt (77%) rename grpc-multiplatform-lib/src/{iosJvmCommon => commonMain}/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/CodedOutputStream.kt (92%) rename grpc-multiplatform-lib/src/{iosJvmCommon => commonMain}/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/ParseException.kt (100%) rename grpc-multiplatform-lib/src/{iosJvmCommon => commonMain}/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/const.kt (50%) rename grpc-multiplatform-lib/src/{iosJvmCommon => commonMain}/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/message_reader.kt (96%) rename grpc-multiplatform-lib/src/{iosJvmCommon => commonMain}/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/wireformat.kt (100%) create mode 100644 grpc-multiplatform-lib/src/commonMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/message/DataType.kt rename grpc-multiplatform-lib/src/{iosJvmCommon => commonMain}/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/message/MessageDeserializer.kt (65%) create mode 100644 grpc-multiplatform-lib/src/iosJvmCommon/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/map_writer.kt create mode 100644 grpc-multiplatform-lib/src/jsMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/CodedInputStream.kt create mode 100644 grpc-multiplatform-lib/src/jsMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/CodedOutputStream.kt create mode 100644 grpc-multiplatform-lib/src/jsMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/TypeUtil.kt create mode 100644 grpc-multiplatform-lib/src/jsMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/map_writer.kt create mode 100644 grpc-multiplatform-lib/src/jsMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/message_writer.kt rename grpc-multiplatform-lib/src/{iosJvmCommon => jsMain}/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/message/DataType.kt (90%) delete mode 100644 grpc-multiplatform-lib/src/jsMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/message/JSImpl.kt delete mode 100644 grpc-multiplatform-lib/src/jsMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/message/MessageDeserializer.kt create mode 100644 plugin/settings.gradle.kts delete mode 100644 plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/common/CommonFunctionGenerator.kt delete mode 100644 plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/common/JsCommonFunctionGenerator.kt delete mode 100644 plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/common/JvmCommonFunctionGenerator.kt rename plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/dsl/{IosJvmDslBuilder.kt => ActualDslBuilder.kt} (89%) delete mode 100644 plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/dsl/JsDslBuilder.kt delete mode 100644 plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/dsl/SubDslBuilder.kt rename plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/map/{IosJvmMapMessageMethodGenerator.kt => ActualMapMessageMethodGenerator.kt} (89%) delete mode 100644 plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/map/JsMapMessageMethodGenerator.kt delete mode 100644 plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/map/mapper/CommonToJsMapMapper.kt delete mode 100644 plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/map/mapper/CommonToJvmMapMapper.kt delete mode 100644 plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/map/mapper/JsToCommonMapMapper.kt delete mode 100644 plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/map/mapper/JvmToCommonMapMapper.kt delete mode 100644 plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/map/mapper/MapMapper.kt create mode 100644 plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/oneof/ActualOneOfMethodAndClassGenerator.kt create mode 100644 plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/proto_file/ActualProtoFileWriter.kt rename plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/proto_file/{IOSProtoFileWriter.kt => IosProtoFileWriter.kt} (84%) rename plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/repeated/{IosJvmRepeatedMessageMethodGenerator.kt => ActualRepeatedMessageMethodGenerator.kt} (93%) delete mode 100644 plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/repeated/JsRepeatedMessageMethodGenerator.kt rename plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/scalar/{IosJvmScalarMessageMethodGenerator.kt => ActualScalarMessageMethodGenerator.kt} (97%) delete mode 100644 plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/scalar/JsScalarMessageMethodGenerator.kt rename plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/service/{IOSServiceWriter.kt => IosServiceWriter.kt} (98%) diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..12079bc --- /dev/null +++ b/.dockerignore @@ -0,0 +1,12 @@ +**/build +**/.gradle +**/.idea +/docker +kotlin-js-store/yarn.lock +.gitignore +jitpack.yml +LICENSE.md +local.properties +readme.md +example +test-outputs \ No newline at end of file diff --git a/.gitignore b/.gitignore index 105545e..bb84a55 100644 --- a/.gitignore +++ b/.gitignore @@ -31,4 +31,5 @@ build generated local.properties *.podspec -*.lock \ No newline at end of file +*.lock +test-outputs \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..ac11193 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,11 @@ +FROM markhobson/maven-chrome:jdk-17 + +WORKDIR /tests +COPY gradlew gradlew +COPY gradle gradle +RUN ./gradlew --version + +COPY . . + +ENTRYPOINT ["./gradlew"] +RUN ./gradlew :grpc-mp-test:jsTestClasses \ No newline at end of file diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 1c883ef..ad887ff 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -2,10 +2,10 @@ import com.google.protobuf.gradle.* plugins { `kotlin-dsl` - kotlin("jvm") version "1.7.20" + kotlin("jvm") version libs.versions.kotlin.get() id("java") - id("com.google.protobuf") version "0.8.18" + id("com.google.protobuf") version libs.versions.protobufGradlePlugin.get() } repositories { @@ -14,23 +14,25 @@ repositories { } dependencies { - implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.20") { + implementation(libs.kotlin.gradle.plugin) { exclude("org.antlr") } - implementation("com.android.tools.build:gradle:7.0.4") { + implementation(libs.android.gradle.plugin) { exclude("org.antlr") } - implementation("com.google.protobuf:protobuf-kotlin:3.20.1") - implementation("com.google.protobuf:protobuf-java-util:3.20.1") - implementation("io.grpc:grpc-protobuf:1.47.0") - implementation("io.grpc:grpc-stub:1.47.0") - implementation("io.grpc:grpc-kotlin-stub:1.2.1") + implementation(libs.google.protobuf.kotlin) + implementation(libs.google.protobuf.java.util) + implementation(libs.grpc.protobuf) + implementation(libs.grpc.stub) + implementation(libs.grpc.kotlin.stub) - implementation("io.grpc:grpc-netty-shaded:1.48.1") + implementation(libs.grpc.netty.shaded) - implementation("com.google.guava:guava:31.1-jre") - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4") + implementation(libs.google.guava) + implementation(libs.kotlinx.coroutines.core) + + implementation(project(":test-server")) } sourceSets { @@ -45,7 +47,6 @@ sourceSets { } } - protobuf { protoc { artifact = "com.google.protobuf:protoc:3.21.1" @@ -78,9 +79,10 @@ protobuf { } tasks.withType { - kotlinOptions.jvmTarget = "1.8" + kotlinOptions.jvmTarget = "17" } + java { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } \ No newline at end of file diff --git a/buildSrc/settings.gradle.kts b/buildSrc/settings.gradle.kts new file mode 100644 index 0000000..dcb375f --- /dev/null +++ b/buildSrc/settings.gradle.kts @@ -0,0 +1,9 @@ +dependencyResolutionManagement { + versionCatalogs { + create("libs") { + from(files("../gradle/libs.versions.toml")) + } + } +} + +include(":test-server") \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/PatchedPodGenTask.kt b/buildSrc/src/main/kotlin/PatchedPodGenTask.kt deleted file mode 100644 index afc19ba..0000000 --- a/buildSrc/src/main/kotlin/PatchedPodGenTask.kt +++ /dev/null @@ -1,310 +0,0 @@ -import org.gradle.api.Project -import org.gradle.api.logging.Logger -import org.gradle.api.provider.Provider -import org.gradle.api.tasks.* -import java.io.File -import java.io.IOException -import org.jetbrains.kotlin.konan.target.* -import org.jetbrains.kotlin.gradle.targets.native.tasks.PodGenTask -import org.jetbrains.kotlin.gradle.plugin.cocoapods.CocoapodsExtension.SpecRepos -import org.jetbrains.kotlin.gradle.plugin.cocoapods.CocoapodsExtension.CocoapodsDependency.PodLocation.* -import org.jetbrains.kotlin.gradle.plugin.cocoapods.CocoapodsExtension.CocoapodsDependency.PodLocation -import org.jetbrains.kotlin.gradle.plugin.mpp.NativeBuildType -import kotlin.concurrent.thread -import kotlin.reflect.KClass - -/** - * Adapted version from here: - * https://youtrack.jetbrains.com/issue/KT-54161/Support-adding-extra-code-to-generated-Podfile-from-the-Kotlin-gradle-plugin#focus=Comments-27-6551141.0-0 - */ - -private val Family.platformLiteral: String - get() = when (this) { - Family.OSX -> "macos" - Family.IOS -> "ios" - Family.TVOS -> "tvos" - Family.WATCHOS -> "watchos" - else -> throw IllegalArgumentException("Bad family ${this.name}") - } - -internal val Project.cocoapodsBuildDirs: CocoapodsBuildDirs - get() = CocoapodsBuildDirs(this) - -internal class CocoapodsBuildDirs(val project: Project) { - val root: File - get() = project.buildDir.resolve("cocoapods") - - val framework: File - get() = root.resolve("framework") - - val defs: File - get() = root.resolve("defs") - - val buildSettings: File - get() = root.resolve("buildSettings") - - val synthetic: File - get() = root.resolve("synthetic") - - fun synthetic(family: Family) = synthetic.resolve(family.name) - - val externalSources: File - get() = root.resolve("externalSources") - - val publish: File = root.resolve("publish") - - fun externalSources(fileName: String) = externalSources.resolve(fileName) - - fun fatFramework(buildType: NativeBuildType) = - root.resolve("fat-frameworks/${buildType.getName()}") -} - - -/** - * The task generates a synthetic project with all cocoapods dependencies - */ -open class PatchedPodGenTask : PodGenTask() { - private val PODFILE_SUFFIX = """ - post_install do |installer| - installer.pods_project.targets.each do |target| - target.build_configurations.each do |config| - config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '11.0' - end - end - - installer.pods_project.targets.each do |target| - target.build_configurations.each do |config| - config.build_settings['CODE_SIGN_IDENTITY'] = '' - end - end - end - """.trimIndent() - - @TaskAction - fun patchedGenerate() { - val syntheticDir = project.cocoapodsBuildDirs.synthetic(family).apply { mkdirs() } - val specRepos: Collection = specReposAccessor.get().getAllAccessor() - - val projResource = "/cocoapods/project.pbxproj" - val projDestination = syntheticDir.resolve("synthetic.xcodeproj").resolve("project.pbxproj") - - projDestination.parentFile.mkdirs() - projDestination.outputStream().use { file -> - javaClass.getResourceAsStream(projResource).use { resource -> - resource.copyTo(file) - } - } - - val podfile = syntheticDir.resolve("Podfile") - podfile.createNewFile() - - val podfileContent = getPodfileContent(specRepos, family.platformLiteral) + PODFILE_SUFFIX - podfile.writeText(podfileContent) - val podInstallCommand = listOf("pod", "install") - - runCommand( - podInstallCommand, - project.logger, - exceptionHandler = { e: IOException -> - CocoapodsErrorHandlingUtil.handle(e, podInstallCommand) - }, - errorHandler = { retCode, output, _ -> - CocoapodsErrorHandlingUtil.handlePodInstallSyntheticError( - podInstallCommand.joinToString(" "), - retCode, - output, - family, - podNameAccessor.get() - ) - }, - processConfiguration = { - directory(syntheticDir) - }) - - val podsXcprojFile = podsXcodeProjDirAccessor.get() - check(podsXcprojFile.exists() && podsXcprojFile.isDirectory) { - "Synthetic project '${podsXcprojFile.path}' was not created." - } - } - - private fun getPodfileContent(specRepos: Collection, xcodeTarget: String) = - buildString { - - specRepos.forEach { - appendLine("source '$it'") - } - - appendLine("target '$xcodeTarget' do") - //if (useLibraries.get().not()) { - appendLine("\tuse_frameworks!") - //} - pods.get().mapNotNull { - buildString { - append("pod '${it.name}'") - - val pathType = when (it.source) { - is Path -> "path" - is Url -> "path" - is Git -> "git" - else -> null - } - - val path = it.source?.getLocalPathAccessor(project, it.name) - - if (path != null && pathType != null) { - append(", :$pathType => '$path'") - } - - } - }.forEach { appendLine("\t$it") } - appendLine("end\n") - } -} - -fun KClass.invokeDynamic(methodStart: String, instance: Any?, vararg params: Any?): R { - val method = this.java.methods.firstOrNull { it.name.startsWith(methodStart) } ?: error("Can't find accessor for $methodStart") - return method.invoke(instance, *params) as R -} - - -val PodGenTask.podsXcodeProjDirAccessor: Provider get() = this::class.invokeDynamic("getPodsXcodeProjDir", this) -val PodGenTask.specReposAccessor: Provider get() = this::class.invokeDynamic("getSpecRepos", this) -val PodGenTask.podNameAccessor: Provider get() = this::class.invokeDynamic("getPodName", this) -fun SpecRepos.getAllAccessor(): Collection = this::class.invokeDynamic("getAll", this) -fun PodLocation.getLocalPathAccessor(project: Project, podName: String): String? = this::class.invokeDynamic("getLocalPath", this, project, podName) - -private fun runCommand( - command: List, - logger: Logger, - errorHandler: ((retCode: Int, output: String, process: Process) -> String?)? = null, - exceptionHandler: ((ex: IOException) -> Unit)? = null, - processConfiguration: ProcessBuilder.() -> Unit = { } -): String { - var process: Process? = null - try { - process = ProcessBuilder(command) - .apply { - this.processConfiguration() - }.start() - } catch (e: IOException) { - if (exceptionHandler != null) exceptionHandler(e) else throw e - } - - if (process == null) { - throw IllegalStateException("Failed to run command ${command.joinToString(" ")}") - } - - var inputText = "" - var errorText = "" - - val inputThread = thread { - inputText = process.inputStream.use { - it.reader().readText() - } - } - - val errorThread = thread { - errorText = process.errorStream.use { - it.reader().readText() - } - } - - inputThread.join() - errorThread.join() - - val retCode = process.waitFor() - logger.info( - """ - |Information about "${command.joinToString(" ")}" call: - | - |${inputText} - """.trimMargin() - ) - - check(retCode == 0) { - errorHandler?.invoke(retCode, inputText.ifBlank { errorText }, process) - ?: """ - |Executing of '${command.joinToString(" ")}' failed with code $retCode and message: - | - |$inputText - | - |$errorText - | - """.trimMargin() - } - - return inputText -} - -private object CocoapodsErrorHandlingUtil { - fun handle(e: IOException, command: List) { - if (e.message?.contains("No such file or directory") == true) { - val message = """ - |'${command.take(2).joinToString(" ")}' command failed with an exception: - | ${e.message} - | - | Full command: ${command.joinToString(" ")} - | - | Possible reason: CocoaPods is not installed - | Please check that CocoaPods v1.10 or above is installed. - | - | To check CocoaPods version type 'pod --version' in the terminal - | - | To install CocoaPods execute 'sudo gem install cocoapods' - | - """.trimMargin() - throw IllegalStateException(message) - } else { - throw e - } - } - - fun handlePodInstallSyntheticError(command: String, retCode: Int, error: String, family: Family, podName: String): String? { - var message = """ - |'pod install' command on the synthetic project failed with return code: $retCode - | - | Full command: $command - | - | Error: ${error.lines().filter { it.contains("[!]") }.joinToString("\n")} - | - """.trimMargin() - - if ( - error.contains("deployment target") || - error.contains("no platform was specified") || - error.contains(Regex("The platform of the target .+ is not compatible with `$podName")) - ) { - message += """ - | - | Possible reason: ${family.name.toLowerCase()} deployment target is not configured - | Configure deployment_target for ALL targets as follows: - | cocoapods { - | ... - | ${family.name.toLowerCase()}.deploymentTarget = "..." - | ... - | } - | - """.trimMargin() - return message - } else if ( - error.contains("Unable to add a source with url") || - error.contains("Couldn't determine repo name for URL") || - error.contains("Unable to find a specification") - ) { - message += """ - | - | Possible reason: spec repos are not configured correctly. - | Ensure that spec repos are correctly configured for all private pod dependencies: - | cocoapods { - | specRepos { - | url("") - | } - | } - | - """.trimMargin() - return message - } - return null - } - -} diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt deleted file mode 100644 index 9f61f8e..0000000 --- a/buildSrc/src/main/kotlin/Versions.kt +++ /dev/null @@ -1,7 +0,0 @@ -object Versions { - const val JVM_GRPC_VERSION = "1.47.0" - const val JVM_GRPC_KOTLIN_VERSION = "1.2.1" - const val JVM_PROTOBUF_VERSION = "3.20.1" - - const val COROUTINES_VERSION = "1.6.4" -} \ No newline at end of file diff --git a/buildSrc/test-server/build.gradle.kts b/buildSrc/test-server/build.gradle.kts new file mode 100644 index 0000000..805e448 --- /dev/null +++ b/buildSrc/test-server/build.gradle.kts @@ -0,0 +1,94 @@ +import com.google.protobuf.gradle.* + +plugins { + kotlin("jvm") version libs.versions.kotlin.get() + + id("java") + id("com.google.protobuf") version libs.versions.protobufGradlePlugin.get() + application +} + +group = "io.github.timortel" +version = libs.versions.grpcKotlinMultiplatform.get() + +repositories { + gradlePluginPortal() + google() +} + +dependencies { + implementation(libs.kotlin.gradle.plugin) { + exclude("org.antlr") + } + implementation(libs.android.gradle.plugin) { + exclude("org.antlr") + } + + + implementation(libs.google.protobuf.kotlin) + implementation(libs.google.protobuf.java.util) + implementation(libs.grpc.protobuf) + implementation(libs.grpc.stub) + implementation(libs.grpc.kotlin.stub) + + implementation(libs.grpc.netty.shaded) + + implementation(libs.google.guava) + implementation(libs.kotlinx.coroutines.core) +} + +sourceSets { + main { + proto { + srcDirs("../../grpc-mp-test/src/commonMain/proto") + } + kotlin.srcDir(buildDir.resolve("generated/source/proto/main/grpc")) + kotlin.srcDir(buildDir.resolve("generated/source/proto/main/grpckt")) + kotlin.srcDir(buildDir.resolve("generated/source/proto/main/java")) + kotlin.srcDir(buildDir.resolve("generated/source/proto/main/kotlin")) + } +} + +protobuf { + protoc { + artifact = "com.google.protobuf:protoc:3.21.1" + } + + plugins { + id("grpc") { + artifact = "io.grpc:protoc-gen-grpc-java:1.48.1" + } + id("grpckt") { + artifact = "io.grpc:protoc-gen-grpc-kotlin:1.2.1:jdk7@jar" + } + } + generateProtoTasks { + ofSourceSet("main").forEach { + it.plugins { + id("grpc") { + } + id("grpckt") { + } + } + + it.builtins { + id("kotlin") { + option("lite") + } + } + } + } +} + +tasks.withType { + kotlinOptions.jvmTarget = "17" +} + +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 +} + +application { + mainClass.set("io.github.timortel.kmpgrpc.testserver.MainKt") +} \ No newline at end of file diff --git a/buildSrc/test-server/settings.gradle.kts b/buildSrc/test-server/settings.gradle.kts new file mode 100644 index 0000000..fa8bc74 --- /dev/null +++ b/buildSrc/test-server/settings.gradle.kts @@ -0,0 +1,7 @@ +dependencyResolutionManagement { + versionCatalogs { + create("libs") { + from(files("../gradle/libs.versions.toml")) + } + } +} \ No newline at end of file diff --git a/buildSrc/test-server/src/main/kotlin/io/github/timortel/kmpgrpc/testserver/Main.kt b/buildSrc/test-server/src/main/kotlin/io/github/timortel/kmpgrpc/testserver/Main.kt new file mode 100644 index 0000000..addc484 --- /dev/null +++ b/buildSrc/test-server/src/main/kotlin/io/github/timortel/kmpgrpc/testserver/Main.kt @@ -0,0 +1,5 @@ +package io.github.timortel.kmpgrpc.testserver + +fun main() { + TestServer.start().awaitTermination() +} \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/TestServer.kt b/buildSrc/test-server/src/main/kotlin/io/github/timortel/kmpgrpc/testserver/TestServer.kt similarity index 88% rename from buildSrc/src/main/kotlin/TestServer.kt rename to buildSrc/test-server/src/main/kotlin/io/github/timortel/kmpgrpc/testserver/TestServer.kt index c25ecf2..2d4aed0 100644 --- a/buildSrc/src/main/kotlin/TestServer.kt +++ b/buildSrc/test-server/src/main/kotlin/io/github/timortel/kmpgrpc/testserver/TestServer.kt @@ -1,3 +1,5 @@ +package io.github.timortel.kmpgrpc.testserver + import io.github.timortel.kotlin_multiplatform_grpc_plugin.test.basic_messages.* import io.grpc.Server import io.grpc.netty.shaded.io.grpc.netty.NettyServerBuilder @@ -9,10 +11,11 @@ object TestServer { private var server: Server? = null - fun start() { - if (server != null) return + fun start(): Server { + val currentServer = server + if (currentServer != null) return currentServer - server = NettyServerBuilder + val createdServer = NettyServerBuilder .forPort(17888) .addService(object : TestServiceGrpcKt.TestServiceCoroutineImplBase() { override suspend fun emptyRpc(request: EmptyMessage): EmptyMessage { @@ -58,6 +61,10 @@ object TestServer { ) .build() .start() + + server = createdServer + + return createdServer } fun stop() { diff --git a/docker-tests/envoy.yml b/docker-tests/envoy.yml new file mode 100644 index 0000000..ca7dab5 --- /dev/null +++ b/docker-tests/envoy.yml @@ -0,0 +1,61 @@ +admin: + access_log_path: /tmp/admin_access.log + address: + socket_address: { address: 0.0.0.0, port_value: 9901 } + +static_resources: + listeners: + - name: listener_0 + address: + socket_address: { address: 0.0.0.0, port_value: 8082 } + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + codec_type: auto + stat_prefix: ingress_http + route_config: + name: local_route + virtual_hosts: + - name: local_service + domains: [ "*" ] + routes: + - match: { prefix: "/" } + route: + cluster: echo_service + timeout: 0s + max_stream_duration: + grpc_timeout_header_max: 0s + cors: + allow_origin_string_match: + - prefix: "*" + allow_methods: GET, PUT, DELETE, POST, OPTIONS + allow_headers: keep-alive,user-agent,cache-control,content-type,content-transfer-encoding,custom-header-1,x-accept-content-transfer-encoding,x-accept-response-streaming,x-user-agent,x-grpc-web,grpc-timeout + max_age: "1728000" + expose_headers: custom-header-1,grpc-status,grpc-message + http_filters: + - name: envoy.filters.http.grpc_web + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.grpc_web.v3.GrpcWeb + - name: envoy.filters.http.cors + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.cors.v3.Cors + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + clusters: + - name: echo_service + connect_timeout: 0.25s + type: logical_dns + http2_protocol_options: { } + lb_policy: round_robin + load_assignment: + cluster_name: cluster_0 + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: 127.0.0.1 + port_value: 17888 diff --git a/docker-tests/js-tests.yml b/docker-tests/js-tests.yml new file mode 100644 index 0000000..e7d40ec --- /dev/null +++ b/docker-tests/js-tests.yml @@ -0,0 +1,33 @@ +services: + envoy: + image: envoyproxy/envoy-dev:latest + volumes: + - ./envoy.yml:/etc/envoy/envoy.yaml + network_mode: "host" + ports: + - "8082:8082" + + js-test: + image: kmp-grpc-jstest + build: + context: .. + network_mode: "host" + depends_on: + envoy: + condition: service_started + test-server: + condition: service_started + command: + - grpc-mp-test:jsTest + volumes: + - ./test-outputs/:/app/test-outputs/ + + test-server: + image: kmp-grpc-testserver + build: + context: .. + network_mode: "host" + ports: + - "17888:17888" + command: + - buildSrc:test-server:run diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 0000000..f02ad6e --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,45 @@ +[versions] +grpcKotlinMultiplatform = "0.4.0" + +androidGradlePlugin = "8.2.0" +androidCompileSdk = "34" +androidMinSdk = "21" + +kotlin = "1.9.22" + +grpcJvm = "1.61.1" +grpcKotlin = "1.4.1" +protobufJvm = "3.25.3" +protobufGradlePlugin = "0.9.4" + +kotlinxCoroutines = "1.8.0" +antlr = "4.13.1" + +gradlePluginPublish = "1.2.1" + +[libraries] +antlr = { group = "org.antlr", name = "antlr4", version.ref = "antlr" } + +kotlinx-coroutines-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "kotlinxCoroutines" } +kotlinx-coroutines-test = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-test", version.ref = "kotlinxCoroutines" } +google-protobuf-kotlin = { group = "com.google.protobuf", name = "protobuf-kotlin", version.ref = "protobufJvm" } +google-protobuf-java-util = { group = "com.google.protobuf", name = "protobuf-java-util", version.ref = "protobufJvm" } + +google-guava = { group = "com.google.guava", name = "guava", version = "33.0.0-jre" } + +grpc-protobuf = { group = "io.grpc", name = "grpc-protobuf", version.ref = "grpcJvm" } +grpc-protobuf-lite = { group = "io.grpc", name = "grpc-protobuf-lite", version.ref = "grpcJvm" } +grpc-stub = { group = "io.grpc", name = "grpc-stub", version.ref = "grpcJvm" } +grpc-kotlin-stub = { group = "io.grpc", name = "grpc-kotlin-stub", version.ref = "grpcKotlin" } +grpc-netty = { group = "io.grpc", name = "grpc-netty", version = "1.61.1" } +grpc-netty-shaded = { group = "io.grpc", name = "grpc-netty-shaded", version = "1.61.1" } + +squareup-kotlinpoet = { group = "com.squareup", name = "kotlinpoet", version = "1.16.0" } + +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" } + +[plugins] +android-library = { id = "com.android.library", version.ref = "androidGradlePlugin" } +kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } +kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ae04661..84a0b92 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/grpc-mp-test/build.gradle.kts b/grpc-mp-test/build.gradle.kts index 36c2c62..df0dd97 100644 --- a/grpc-mp-test/build.gradle.kts +++ b/grpc-mp-test/build.gradle.kts @@ -1,4 +1,9 @@ +@file:Suppress("UNUSED_VARIABLE") + +import io.github.timortel.kmpgrpc.testserver.TestServer import io.github.timortel.kotlin_multiplatform_grpc_plugin.GrpcMultiplatformExtension.OutputTarget +import org.gradle.api.tasks.testing.logging.TestExceptionFormat +import org.gradle.api.tasks.testing.logging.TestLogEvent import org.jetbrains.kotlin.gradle.targets.native.tasks.KotlinNativeSimulatorTest plugins { @@ -6,7 +11,7 @@ plugins { kotlin("multiplatform") kotlin("native.cocoapods") - id("io.github.timortel.kotlin-multiplatform-grpc-plugin") version "0.2.0" + id("io.github.timortel.kotlin-multiplatform-grpc-plugin") version libs.versions.grpcKotlinMultiplatform.get() } version = "dev" @@ -16,7 +21,7 @@ repositories { } kotlin { - android("android") + androidTarget("android") jvm { testRuns["test"].executionTask.configure { @@ -30,10 +35,11 @@ kotlin { browser() } - ios() iosArm64() iosSimulatorArm64() + applyDefaultHierarchyTemplate() + cocoapods { summary = "GRPC Kotlin Multiplatform test library" homepage = "https://github.com/TimOrtel/GRPC-Kotlin-Multiplatform" @@ -43,82 +49,70 @@ kotlin { pod("Protobuf") } - sourceSets { - val commonMain by getting { - dependencies { - api(project(":grpc-multiplatform-lib")) - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4") - } - } + compilerOptions { + freeCompilerArgs.add("-Xexpect-actual-classes") + } - val commonTest by getting { - dependencies { - implementation(kotlin("test")) - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.4") + sourceSets { + all { + languageSettings { + optIn("kotlinx.cinterop.ExperimentalForeignApi") } } - val iosMain by getting - - val jvmMain by getting { + commonMain { dependencies { + api(project(":grpc-multiplatform-lib")) + implementation(libs.kotlinx.coroutines.core) } } - val androidMain by getting { + commonTest { dependencies { + implementation(kotlin("test")) + implementation(libs.kotlinx.coroutines.test) } - - kotlin.srcDir(projectDir.resolve("build/generated/source/kmp-grpc/jvmMain/kotlin").canonicalPath) } val serializationTest by creating { - dependsOn(commonMain) - dependsOn(commonTest) + dependsOn(commonMain.get()) + dependsOn(commonTest.get()) dependencies { implementation(kotlin("test")) } kotlin.srcDir(projectDir.resolve("build/generated/source/kmp-grpc/commonMain/kotlin").canonicalPath) } - val jsTest by getting { + jsTest { dependsOn(serializationTest) } - val iosTest by getting { + iosTest { dependsOn(serializationTest) } - val jvmTest by getting { - dependsOn(jvmMain) + jvmTest { dependsOn(serializationTest) dependencies { - runtimeOnly("io.grpc:grpc-netty:1.50.2") + runtimeOnly(libs.grpc.netty) } } - - val iosSimulatorArm64Main by getting { - dependsOn(iosMain) - } - - val iosSimulatorArm64Test by getting { - dependsOn(iosTest) - } } } android { - compileSdk = (31) + namespace = "io.github.timortel.kotlin_multiplatform_grpc_plugin.test" + + compileSdk = libs.versions.androidCompileSdk.get().toInt() defaultConfig { - minSdk = (21) - targetSdk = (31) + minSdk = libs.versions.androidMinSdk.get().toInt() } compileOptions { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } sourceSets { @@ -132,24 +126,25 @@ android { grpcKotlinMultiplatform { targetSourcesMap.put( OutputTarget.COMMON, - listOf(kotlin.sourceSets.getByName("commonMain")) + listOf(kotlin.sourceSets.commonMain.get()) ) - targetSourcesMap.put( - OutputTarget.JS, - listOf(kotlin.sourceSets.getByName("jsMain"), kotlin.sourceSets.getByName("jsTest")) - ) - targetSourcesMap.put( - OutputTarget.JVM, - listOf(kotlin.sourceSets.getByName("androidMain"), kotlin.sourceSets.getByName("jvmMain")) - ) - targetSourcesMap.put( - OutputTarget.IOS, - listOf( - kotlin.sourceSets.getByName("iosMain"), - kotlin.sourceSets.getByName("iosTest") + with(kotlin) { + targetSourcesMap.put( + OutputTarget.JS, + listOf(kotlin.sourceSets.jsMain.get(), kotlin.sourceSets.jsTest.get()) + ) + + targetSourcesMap.put( + OutputTarget.JVM, + listOf(kotlin.sourceSets.androidMain.get(), kotlin.sourceSets.jvmMain.get(), kotlin.sourceSets.jvmTest.get()) ) - ) + + targetSourcesMap.put( + OutputTarget.IOS, + listOf(kotlin.sourceSets.iosMain.get(), kotlin.sourceSets.iosTest.get()) + ) + } val protoFolder = projectDir.resolve("src/commonMain/proto") protoSourceFolders.set( @@ -157,8 +152,6 @@ grpcKotlinMultiplatform { ) } -tasks.replace("podGenIOS", PatchedPodGenTask::class) - tasks.findByName("jvmTest")?.let { it.doFirst { TestServer.start() @@ -170,8 +163,6 @@ tasks.findByName("jvmTest")?.let { } tasks.named("iosSimulatorArm64Test", KotlinNativeSimulatorTest::class).configure { - deviceId = "iPhone 14" - doFirst { TestServer.start() } @@ -179,4 +170,19 @@ tasks.named("iosSimulatorArm64Test", KotlinNativeSimulatorTest::class).configure doLast { TestServer.stop() } +} + +tasks.withType(Test::class) { + testLogging.setEvents(listOf(TestLogEvent.FAILED)) + + testLogging.exceptionFormat = TestExceptionFormat.FULL + testLogging.showExceptions = true + testLogging.showCauses = true + testLogging.showStackTraces = true + testLogging.showStandardStreams = true + + reports.junitXml.required.set(true) + reports.html.required.set(true) + reports.junitXml.outputLocation.set(rootProject.rootDir.resolve("test-outputs/${project.name}/$name/")) + reports.html.outputLocation.set(rootProject.rootDir.resolve("test-outputs/${project.name}/$name/")) } \ No newline at end of file diff --git a/grpc-mp-test/src/commonTest/kotlin/io/github/timortel/kotlin_multiplatform_grpc_plugin/test/RpcTest.kt b/grpc-mp-test/src/commonTest/kotlin/io/github/timortel/kotlin_multiplatform_grpc_plugin/test/RpcTest.kt index 54de2c1..dbecc3e 100644 --- a/grpc-mp-test/src/commonTest/kotlin/io/github/timortel/kotlin_multiplatform_grpc_plugin/test/RpcTest.kt +++ b/grpc-mp-test/src/commonTest/kotlin/io/github/timortel/kotlin_multiplatform_grpc_plugin/test/RpcTest.kt @@ -2,6 +2,7 @@ package io.github.timortel.kotlin_multiplatform_grpc_plugin.test import io.github.timortel.kotlin_multiplatform_grpc_lib.KMChannel import io.github.timortel.kotlin_multiplatform_grpc_plugin.test.basic_messages.* +import kotlinx.coroutines.async import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.toCollection @@ -9,90 +10,89 @@ import kotlinx.coroutines.flow.toList import kotlinx.coroutines.test.runTest import kotlin.test.Test import kotlin.test.assertEquals +import kotlin.test.assertNotEquals +import kotlin.test.fail -class RpcTest { +abstract class RpcTest { - private val channel = KMChannel.Builder - .forAddress("localhost", 17888) + abstract val address: String + abstract val port: Int + + private val channel: KMChannel get() = KMChannel.Builder + .forAddress(address, port) .usePlaintext() .build() - private val stub = KMTestServiceStub(channel) + private val stub: KMTestServiceStub get() = KMTestServiceStub(channel) @Test - fun testEmpty() { - runTest { - val message = kmEmptyMessage { } + fun testEmpty() = runTest { + try { + val message = kmEmptyMessage { } val response = stub .emptyRpc(message) assertEquals(message, response) + } catch (e: Exception) { + e.printStackTrace() + throw e } + } @Test - fun testSimple() { - runTest { - val message = kmSimpleMessage { field1 = "Test" } - val response = stub - .simpleRpc(message) + fun testSimple() = runTest { + val message = kmSimpleMessage { field1 = "Test" } - assertEquals(message, response) + val response = async { + stub.simpleRpc(message) } + + assertEquals(message, response.await()) } @Test - fun testScalar() { - runTest { - val message = createScalarMessage() - val response = stub - .scalarRpc(message) + fun testScalar() = runTest { + val message = createScalarMessage() + val response = stub + .scalarRpc(message) - assertEquals(message, response) - } + assertEquals(message, response) } @Test - fun testEverything() { - runTest { - val message = createMessageWithAllTypes() - val response = stub - .everythingRpc(message) + fun testEverything() = runTest { + val message = createMessageWithAllTypes() + val response = stub + .everythingRpc(message) - assertEquals(message, response) - } + assertEquals(message, response) } @Test - fun testStreamEmpty() { - runTest { - val message = kmEmptyMessage { } - val flow: Flow = stub - .emptyStream(message) + fun testStreamEmpty() = runTest { + val message = kmEmptyMessage { } + val flow: Flow = stub + .emptyStream(message) - assertEquals(listOf(message, message, message), flow.toList()) - } + assertEquals(listOf(message, message, message), flow.toList()) } @Test - fun testStreamSimple() { - runTest { - val message = kmSimpleMessage { field1 = "Streaming test" } - val flow: Flow = stub - .simpleStreamingRpc(message) + fun testStreamSimple() = runTest { + val message = kmSimpleMessage { field1 = "Streaming test" } + val flow: Flow = stub + .simpleStreamingRpc(message) - assertEquals(listOf(message, message, message), flow.toList()) - } + assertEquals(listOf(message, message, message), flow.toList()) } @Test - fun testStreamEverything() { - runTest { - val message = createMessageWithAllTypes() - val flow: Flow = stub - .everythingStreamingRpc(message) + fun testStreamEverything() = runTest { + val message = createMessageWithAllTypes() + val flow: Flow = stub + .everythingStreamingRpc(message) - assertEquals(listOf(message, message, message), flow.toList()) - } + assertEquals(listOf(message, message, message), flow.toList()) } } \ No newline at end of file diff --git a/grpc-mp-test/src/commonTest/kotlin/io/github/timortel/kotlin_multiplatform_grpc_plugin/test/defaults.kt b/grpc-mp-test/src/commonTest/kotlin/io/github/timortel/kotlin_multiplatform_grpc_plugin/test/defaults.kt index 74ff07a..1173d2b 100644 --- a/grpc-mp-test/src/commonTest/kotlin/io/github/timortel/kotlin_multiplatform_grpc_plugin/test/defaults.kt +++ b/grpc-mp-test/src/commonTest/kotlin/io/github/timortel/kotlin_multiplatform_grpc_plugin/test/defaults.kt @@ -1,9 +1,6 @@ package io.github.timortel.kotlin_multiplatform_grpc_plugin.test -import io.github.timortel.kotlin_multiplatform_grpc_plugin.test.basic_messages.KMSimpleEnum -import io.github.timortel.kotlin_multiplatform_grpc_plugin.test.basic_messages.kmMessageWithEverything -import io.github.timortel.kotlin_multiplatform_grpc_plugin.test.basic_messages.kmScalarTypes -import io.github.timortel.kotlin_multiplatform_grpc_plugin.test.basic_messages.kmSimpleMessage +import io.github.timortel.kotlin_multiplatform_grpc_plugin.test.basic_messages.* fun createScalarMessage() = kmScalarTypes { field1 = "Test" @@ -14,6 +11,15 @@ fun createScalarMessage() = kmScalarTypes { field6 = 7.0 } +fun createComplexRepeated() = kmComplexRepeatedMessage { + field1List += listOf("Foo", "Bar", "Baz") + field2List += listOf(true, false, true) + field3List += listOf(14, 142, 1, -35) + field4List += listOf(12L, 23424L, 10312313L, -123131L) + field5List += listOf(-1f, 2f, 2.5f, -0.5f) + field6List += listOf(-0.5, 15.0) +} + fun createMessageWithAllTypes() = kmMessageWithEverything { field1 = "Test" field2 = true diff --git a/grpc-mp-test/src/iosTest/kotlin/io/github/timortel/kotlin_multiplatform_grpc_plugin/test/IOSSerializationTest.kt b/grpc-mp-test/src/iosTest/kotlin/io/github/timortel/kotlin_multiplatform_grpc_plugin/test/IOSSerializationTest.kt index 1df7fd6..8bdaf78 100644 --- a/grpc-mp-test/src/iosTest/kotlin/io/github/timortel/kotlin_multiplatform_grpc_plugin/test/IOSSerializationTest.kt +++ b/grpc-mp-test/src/iosTest/kotlin/io/github/timortel/kotlin_multiplatform_grpc_plugin/test/IOSSerializationTest.kt @@ -26,4 +26,6 @@ class IOSSerializationTest : SerializationTest() { serializeImpl(message, KMMessageWithEverything.Companion) override fun serialize(message: KMOneOfMessage): KMOneOfMessage = serializeImpl(message, KMOneOfMessage.Companion) + + override fun serialize(message: KMComplexRepeatedMessage): KMComplexRepeatedMessage = serializeImpl(message, KMComplexRepeatedMessage.Companion) } \ No newline at end of file diff --git a/grpc-mp-test/src/iosTest/kotlin/io/github/timortel/kotlin_multiplatform_grpc_plugin/test/IosRpcTest.kt b/grpc-mp-test/src/iosTest/kotlin/io/github/timortel/kotlin_multiplatform_grpc_plugin/test/IosRpcTest.kt new file mode 100644 index 0000000..697badc --- /dev/null +++ b/grpc-mp-test/src/iosTest/kotlin/io/github/timortel/kotlin_multiplatform_grpc_plugin/test/IosRpcTest.kt @@ -0,0 +1,6 @@ +package io.github.timortel.kotlin_multiplatform_grpc_plugin.test + +class IosRpcTest : RpcTest() { + override val address: String = "localhost" + override val port: Int = 17888 +} \ No newline at end of file diff --git a/grpc-mp-test/src/jsTest/kotlin/io/github/timortel/kotlin_multiplatform_grpc_plugin/test/JSSerializationTest.kt b/grpc-mp-test/src/jsTest/kotlin/io/github/timortel/kotlin_multiplatform_grpc_plugin/test/JSSerializationTest.kt index d6cb930..747fcdc 100644 --- a/grpc-mp-test/src/jsTest/kotlin/io/github/timortel/kotlin_multiplatform_grpc_plugin/test/JSSerializationTest.kt +++ b/grpc-mp-test/src/jsTest/kotlin/io/github/timortel/kotlin_multiplatform_grpc_plugin/test/JSSerializationTest.kt @@ -1,33 +1,31 @@ package io.github.timortel.kotlin_multiplatform_grpc_plugin.test -import io.github.timortel.kotlin_multiplatform_grpc_lib.message.JSImpl import io.github.timortel.kotlin_multiplatform_grpc_lib.message.KMMessage import io.github.timortel.kotlin_multiplatform_grpc_lib.message.MessageDeserializer import io.github.timortel.kotlin_multiplatform_grpc_plugin.test.basic_messages.* -import kotlin.test.Test -import kotlin.test.assertEquals class JSSerializationTest : SerializationTest() { - private fun serializeImpl(msg: T, deserializer: MessageDeserializer, creator: (T) -> J): J { - return creator(deserializer.deserializeBinary(msg.serializeBinary())) + private fun serializeImpl(msg: T, deserializer: MessageDeserializer): T { + val buffer = msg.serialize() + return deserializer.deserialize(buffer) } override fun serialize(message: KMLongMessage): KMLongMessage = - serializeImpl(message.jsImpl, JS_LongMessage.Companion, ::KMLongMessage) + serializeImpl(message, KMLongMessage.Companion) override fun serialize(message: KMRepeatedLongMessage): KMRepeatedLongMessage = - serializeImpl(message.jsImpl, JS_RepeatedLongMessage.Companion, ::KMRepeatedLongMessage) + serializeImpl(message, KMRepeatedLongMessage.Companion) - override fun serialize(message: KMScalarTypes): KMScalarTypes = - serializeImpl(message.jsImpl, JS_ScalarTypes.Companion, ::KMScalarTypes) + override fun serialize(message: KMScalarTypes): KMScalarTypes = serializeImpl(message, KMScalarTypes.Companion) override fun serialize(message: KMMessageWithSubMessage): KMMessageWithSubMessage = - serializeImpl(message.jsImpl, JS_MessageWithSubMessage.Companion, ::KMMessageWithSubMessage) + serializeImpl(message, KMMessageWithSubMessage.Companion) override fun serialize(message: KMMessageWithEverything): KMMessageWithEverything = - serializeImpl(message.jsImpl, JS_MessageWithEverything.Companion, ::KMMessageWithEverything) + serializeImpl(message, KMMessageWithEverything.Companion) - override fun serialize(message: KMOneOfMessage): KMOneOfMessage = - serializeImpl(message.jsImpl, JS_OneOfMessage.Companion, ::KMOneOfMessage) + override fun serialize(message: KMOneOfMessage): KMOneOfMessage = serializeImpl(message, KMOneOfMessage.Companion) + + override fun serialize(message: KMComplexRepeatedMessage): KMComplexRepeatedMessage = serializeImpl(message, KMComplexRepeatedMessage.Companion) } \ No newline at end of file diff --git a/grpc-mp-test/src/jsTest/kotlin/io/github/timortel/kotlin_multiplatform_grpc_plugin/test/JsRpcTest.kt b/grpc-mp-test/src/jsTest/kotlin/io/github/timortel/kotlin_multiplatform_grpc_plugin/test/JsRpcTest.kt new file mode 100644 index 0000000..ca8dc0c --- /dev/null +++ b/grpc-mp-test/src/jsTest/kotlin/io/github/timortel/kotlin_multiplatform_grpc_plugin/test/JsRpcTest.kt @@ -0,0 +1,6 @@ +package io.github.timortel.kotlin_multiplatform_grpc_plugin.test + +class JsRpcTest : RpcTest() { + override val address: String = "localhost" + override val port: Int = 8082 +} \ No newline at end of file diff --git a/grpc-mp-test/src/jvmTest/kotlin/io/github/timortel/kotlin_multiplatform_grpc_plugin/test/JVMSerializationTest.kt b/grpc-mp-test/src/jvmTest/kotlin/io/github/timortel/kotlin_multiplatform_grpc_plugin/test/JVMSerializationTest.kt index cba0543..66dcb08 100644 --- a/grpc-mp-test/src/jvmTest/kotlin/io/github/timortel/kotlin_multiplatform_grpc_plugin/test/JVMSerializationTest.kt +++ b/grpc-mp-test/src/jvmTest/kotlin/io/github/timortel/kotlin_multiplatform_grpc_plugin/test/JVMSerializationTest.kt @@ -25,4 +25,6 @@ class JVMSerializationTest : SerializationTest() { serializeImpl(message, KMMessageWithEverything.Companion) override fun serialize(message: KMOneOfMessage): KMOneOfMessage = serializeImpl(message, KMOneOfMessage.Companion) + + override fun serialize(message: KMComplexRepeatedMessage): KMComplexRepeatedMessage = serializeImpl(message, KMComplexRepeatedMessage.Companion) } \ No newline at end of file diff --git a/grpc-mp-test/src/jvmTest/kotlin/io/github/timortel/kotlin_multiplatform_grpc_plugin/test/JvmRpcTest.kt b/grpc-mp-test/src/jvmTest/kotlin/io/github/timortel/kotlin_multiplatform_grpc_plugin/test/JvmRpcTest.kt new file mode 100644 index 0000000..19d9988 --- /dev/null +++ b/grpc-mp-test/src/jvmTest/kotlin/io/github/timortel/kotlin_multiplatform_grpc_plugin/test/JvmRpcTest.kt @@ -0,0 +1,6 @@ +package io.github.timortel.kotlin_multiplatform_grpc_plugin.test + +class JvmRpcTest : RpcTest() { + override val address: String = "localhost" + override val port: Int = 17888 +} \ No newline at end of file diff --git a/grpc-mp-test/src/serializationTest/kotlin/io/github/timortel/kotlin_multiplatform_grpc_plugin/test/SerializationTest.kt b/grpc-mp-test/src/serializationTest/kotlin/io/github/timortel/kotlin_multiplatform_grpc_plugin/test/SerializationTest.kt index cafd179..254f583 100644 --- a/grpc-mp-test/src/serializationTest/kotlin/io/github/timortel/kotlin_multiplatform_grpc_plugin/test/SerializationTest.kt +++ b/grpc-mp-test/src/serializationTest/kotlin/io/github/timortel/kotlin_multiplatform_grpc_plugin/test/SerializationTest.kt @@ -23,7 +23,7 @@ abstract class SerializationTest { @Test fun testSerializeRepeatedLong() { - val message = kmRepeatedLongMessage { + val message: KMRepeatedLongMessage = kmRepeatedLongMessage { field1List += listOf(12L, 13L, 25L) } @@ -44,9 +44,19 @@ abstract class SerializationTest { abstract fun serialize(message: KMMessageWithSubMessage): KMMessageWithSubMessage + @Test + fun testComplexRepeatedSerialization() { + val msg = createComplexRepeated() + val reconstructed = serialize(msg) + + assertEquals(msg, reconstructed) + } + + abstract fun serialize(message: KMComplexRepeatedMessage): KMComplexRepeatedMessage + @Test fun testSerializeMessageWithMessage() { - val msg = kmMessageWithSubMessage { + val msg: KMMessageWithSubMessage = kmMessageWithSubMessage { field1 = kmSimpleMessage { field1 = "Foo" } } @@ -79,7 +89,7 @@ abstract class SerializationTest { @Test fun testSerialization() { - val msg = createMessageWithAllTypes() + val msg: KMMessageWithEverything = createMessageWithAllTypes() val reconstructed = serialize(msg) diff --git a/grpc-mp-test/test-android-protos/build.gradle.kts b/grpc-mp-test/test-android-protos/build.gradle.kts deleted file mode 100644 index f99de4e..0000000 --- a/grpc-mp-test/test-android-protos/build.gradle.kts +++ /dev/null @@ -1,90 +0,0 @@ -import com.google.protobuf.gradle.* - -plugins { - id("com.android.library") - kotlin("android") - - id("com.google.protobuf") -} - -repositories { - mavenCentral() -} - -dependencies { - api("io.grpc:grpc-stub:${Versions.JVM_GRPC_VERSION}") - api("io.grpc:grpc-protobuf-lite:${Versions.JVM_GRPC_VERSION}") - api("io.grpc:grpc-kotlin-stub:${Versions.JVM_GRPC_KOTLIN_VERSION}") - api("com.google.protobuf:protobuf-kotlin-lite:${Versions.JVM_PROTOBUF_VERSION}") - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:${Versions.COROUTINES_VERSION}") -} - -protobuf { - protoc { - artifact = "com.google.protobuf:protoc:${Versions.JVM_PROTOBUF_VERSION}" - } - - plugins { - id("java") { - artifact = "io.grpc:protoc-gen-grpc-java:${Versions.JVM_GRPC_VERSION}" - } - - id("grpc") { - artifact = "io.grpc:protoc-gen-grpc-java:${Versions.JVM_GRPC_VERSION}" - } - id("grpckt") { - artifact = "io.grpc:protoc-gen-grpc-kotlin:${Versions.JVM_GRPC_KOTLIN_VERSION}:jdk7@jar" - } - } - generateProtoTasks { - all().forEach { - it.plugins { - id("java") { - option("lite") - } - id("grpc") { - option("lite") - } - id("grpckt") { - option("lite") - } - } - - it.builtins { - id("kotlin") { - option("lite") - } - } - } - } -} - -android { - compileSdk = 31 - - defaultConfig { - minSdk = 21 - targetSdk = 31 - } - - compileOptions { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 - } - - sourceSets { - named("main") { - manifest.srcFile("src/main/AndroidManifest.xml") - res.srcDirs("src/main/res") - - val protoSrcs = listOf( - "../src/commonMain/proto" - ) - withGroovyBuilder { - "proto" { - "srcDir"(protoSrcs) - } - } - } - } -} \ No newline at end of file diff --git a/grpc-mp-test/test-android-protos/src/main/AndroidManifest.xml b/grpc-mp-test/test-android-protos/src/main/AndroidManifest.xml deleted file mode 100644 index fe1db82..0000000 --- a/grpc-mp-test/test-android-protos/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/grpc-mp-test/test-jvm-protos/build.gradle.kts b/grpc-mp-test/test-jvm-protos/build.gradle.kts deleted file mode 100644 index a7843dd..0000000 --- a/grpc-mp-test/test-jvm-protos/build.gradle.kts +++ /dev/null @@ -1,84 +0,0 @@ -import com.google.protobuf.gradle.* - -plugins { - kotlin("jvm") - - id("java") - id("com.google.protobuf") -} - -repositories { - mavenCentral() -} - -dependencies { - api(kotlin("stdlib")) - - api("com.google.protobuf:protobuf-kotlin:${Versions.JVM_PROTOBUF_VERSION}") - api("com.google.protobuf:protobuf-java-util:${Versions.JVM_PROTOBUF_VERSION}") - api("io.grpc:grpc-protobuf:${Versions.JVM_GRPC_VERSION}") - api("io.grpc:grpc-stub:${Versions.JVM_GRPC_VERSION}") - api("io.grpc:grpc-kotlin-stub:${Versions.JVM_GRPC_KOTLIN_VERSION}") - - compileOnly("org.jetbrains.kotlinx:kotlinx-coroutines-core:${Versions.COROUTINES_VERSION}") -} - -sourceSets { - main { - proto { - srcDirs("../src/commonMain/proto") - } - kotlin.srcDir(buildDir.resolve("generated/source/proto/main/grpc")) - kotlin.srcDir(buildDir.resolve("generated/source/proto/main/grpckt")) - kotlin.srcDir(buildDir.resolve("generated/source/proto/main/java")) - kotlin.srcDir(buildDir.resolve("generated/source/proto/main/kotlin")) - } -} - - -protobuf { - protoc { - artifact = "com.google.protobuf:protoc:${Versions.JVM_PROTOBUF_VERSION}" - } - - plugins { - id("grpc") { - artifact = "io.grpc:protoc-gen-grpc-java:${Versions.JVM_GRPC_VERSION}" - } - id("grpckt") { - artifact = "io.grpc:protoc-gen-grpc-kotlin:${Versions.JVM_GRPC_KOTLIN_VERSION}:jdk7@jar" - } - } - generateProtoTasks { - ofSourceSet("main").forEach { - it.plugins { - id("grpc") { - option("lite") - } - id("grpckt") { - option("lite") - } - } - - it.builtins { - id("kotlin") { - option("lite") - } - } - } - } -} - -tasks.withType().all { - kotlinOptions { - freeCompilerArgs = listOf("-Xopt-in=kotlin.RequiresOptIn") - } -} - -tasks.withType { - kotlinOptions.jvmTarget = "1.8" -} -java { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 -} \ No newline at end of file diff --git a/grpc-multiplatform-lib/build.gradle.kts b/grpc-multiplatform-lib/build.gradle.kts index d8d25b9..27303b5 100644 --- a/grpc-multiplatform-lib/build.gradle.kts +++ b/grpc-multiplatform-lib/build.gradle.kts @@ -1,3 +1,5 @@ +@file:Suppress("UNUSED_VARIABLE") + plugins { id("com.android.library") kotlin("multiplatform") @@ -6,7 +8,7 @@ plugins { } group = "io.github.timortel" -version = "0.3.0" +version = libs.versions.grpcKotlinMultiplatform.get() repositories { mavenCentral() @@ -14,18 +16,23 @@ repositories { } kotlin { - android("android") { + androidTarget("android") { publishLibraryVariants("release", "debug") } js(IR) { browser() - nodejs() } jvm("jvm") iosX64() iosArm64() iosSimulatorArm64() + applyDefaultHierarchyTemplate() + + compilerOptions { + freeCompilerArgs.add("-Xexpect-actual-classes") + } + cocoapods { version = "1.0" summary = "GRPC Kotlin Multiplatform Implementation" @@ -36,80 +43,71 @@ kotlin { baseName = "GRPCKotlinMultiplatform" } - ios.deploymentTarget = "14.1" + ios.deploymentTarget = "16.4" pod("gRPC-ProtoRPC", moduleName = "GRPCClient") pod("Protobuf", version = "~> 3.21", moduleName = "Protobuf") - //pod("gRPC-Core") } sourceSets { - val commonMain by getting { + all { + languageSettings { + optIn("kotlinx.cinterop.ExperimentalForeignApi") + } + } + + commonMain { dependencies { implementation(kotlin("stdlib-common")) - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.1") + implementation(libs.kotlinx.coroutines.core) } } - val commonTest by getting { + + commonTest { dependencies { implementation(kotlin("test-common")) implementation(kotlin("test-annotations-common")) } } - val GRPC = "1.46.0" - val GRPC_KOTLIN = "1.2.1" - val PROTOBUF = "3.20.1" - val iosJvmCommon by creating { - dependsOn(commonMain) + dependsOn(commonMain.get()) } val androidJvmCommon by creating { dependsOn(iosJvmCommon) } - - val jvmMain by getting { + jvmMain { dependsOn(androidJvmCommon) dependencies { - implementation("com.google.protobuf:protobuf-kotlin:${PROTOBUF}") - implementation("com.google.protobuf:protobuf-java-util:${PROTOBUF}") - implementation("io.grpc:grpc-protobuf:${GRPC}") - implementation("io.grpc:grpc-stub:${GRPC}") - implementation("io.grpc:grpc-kotlin-stub:${GRPC_KOTLIN}") + api(libs.grpc.stub) + api(libs.grpc.protobuf.lite) + api(libs.grpc.kotlin.stub) } } - val androidMain by getting { + androidMain { dependsOn(androidJvmCommon) dependencies { - implementation("io.grpc:grpc-stub:${GRPC}") - implementation("io.grpc:grpc-protobuf-lite:${GRPC}") - implementation("io.grpc:grpc-kotlin-stub:${GRPC_KOTLIN}") - implementation("com.google.protobuf:protobuf-kotlin-lite:${PROTOBUF}") + api(libs.grpc.stub) + api(libs.grpc.protobuf.lite) + api(libs.grpc.kotlin.stub) } } - val jsMain by getting { + jsMain { dependencies { - api(npm("google-protobuf", "^3.19.1")) - api(npm("grpc-web", "^1.3.0")) - api(npm("protobufjs", "^6.11.2")) + api(npm("google-protobuf", "^3.21.2")) + api(npm("grpc-web", "^1.5.0")) + api(npm("protobufjs", "^7.2.6")) } } - val iosX64Main by getting - val iosArm64Main by getting - - val iosSimulatorArm64Main by getting - val iosMain by creating { - dependsOn(commonMain) + iosMain { + dependsOn(commonMain.get()) dependsOn(iosJvmCommon) - iosX64Main.dependsOn(this) - iosArm64Main.dependsOn(this) - iosSimulatorArm64Main.dependsOn(this) } } } @@ -121,16 +119,17 @@ publishing { } android { - compileSdk = 31 + namespace = "io.github.timortel.kotlin_multiplatform_grpc_lib" + + compileSdk = libs.versions.androidCompileSdk.get().toInt() defaultConfig { - minSdk = 21 - targetSdk = 31 + minSdk = libs.versions.androidMinSdk.get().toInt() } compileOptions { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } sourceSets { @@ -146,5 +145,3 @@ kotlin.targets.withType(org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarge binaryOptions["memoryModel"] = "experimental" } } - -tasks.replace("podGenIOS", PatchedPodGenTask::class) \ No newline at end of file diff --git a/grpc-multiplatform-lib/src/androidJvmCommon/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/KMChannel.kt b/grpc-multiplatform-lib/src/androidJvmCommon/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/KMChannel.kt index 2af149e..2f6433d 100644 --- a/grpc-multiplatform-lib/src/androidJvmCommon/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/KMChannel.kt +++ b/grpc-multiplatform-lib/src/androidJvmCommon/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/KMChannel.kt @@ -3,6 +3,9 @@ package io.github.timortel.kotlin_multiplatform_grpc_lib import io.grpc.ManagedChannel import io.grpc.ManagedChannelBuilder +/** + * The Jvm [KMChannel] wraps the grpc [ManagedChannel] and delegates its operations to the wrapped native channel. + */ actual class KMChannel private constructor(val managedChannel: ManagedChannel) { actual class Builder(private val impl: ManagedChannelBuilder<*>) { diff --git a/grpc-multiplatform-lib/src/androidJvmCommon/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/message/DataType.kt b/grpc-multiplatform-lib/src/androidJvmCommon/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/message/DataType.kt index 9024413..94e2b76 100644 --- a/grpc-multiplatform-lib/src/androidJvmCommon/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/message/DataType.kt +++ b/grpc-multiplatform-lib/src/androidJvmCommon/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/message/DataType.kt @@ -2,4 +2,7 @@ package io.github.timortel.kotlin_multiplatform_grpc_lib.message import com.google.protobuf.WireFormat.FieldType +/** + * On the JVM, our [DataType] is identical to the [FieldType]. + */ actual typealias DataType = FieldType \ No newline at end of file diff --git a/grpc-multiplatform-lib/src/androidJvmCommon/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/message/KMMessage.kt b/grpc-multiplatform-lib/src/androidJvmCommon/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/message/KMMessage.kt index f82beb6..fbedf34 100644 --- a/grpc-multiplatform-lib/src/androidJvmCommon/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/message/KMMessage.kt +++ b/grpc-multiplatform-lib/src/androidJvmCommon/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/message/KMMessage.kt @@ -3,10 +3,19 @@ package io.github.timortel.kotlin_multiplatform_grpc_lib.message import io.github.timortel.kotlin_multiplatform_grpc_lib.io.CodedOutputStream import java.nio.ByteBuffer +/** + * On the jvm, we wrap [com.google.protobuf.CodedOutputStream] to serialize our messages. + */ actual interface KMMessage { + /** + * The size of this message when serialized in bytes. + */ val requiredSize: Int + /** + * Serializes this message and returns it as a [ByteArray]. + */ fun serialize(): ByteArray { val data = ByteArray(requiredSize) val stream = com.google.protobuf.CodedOutputStream.newInstance(ByteBuffer.wrap(data)) @@ -15,6 +24,9 @@ actual interface KMMessage { return data } + /** + * Serializes this message and writes it directly to the [CodedOutputStream]. + */ fun serialize(stream: CodedOutputStream) } diff --git a/grpc-multiplatform-lib/src/androidJvmCommon/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/metadata_util.kt b/grpc-multiplatform-lib/src/androidJvmCommon/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/metadata_util.kt index 81f4f6f..7f7bd45 100644 --- a/grpc-multiplatform-lib/src/androidJvmCommon/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/metadata_util.kt +++ b/grpc-multiplatform-lib/src/androidJvmCommon/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/metadata_util.kt @@ -1,5 +1,8 @@ package io.github.timortel.kotlin_multiplatform_grpc_lib +/** + * Convert the given multiplatform metadata to Jvm native [io.grpc.Metadata]. + */ val KMMetadata.jvmMetadata: io.grpc.Metadata get() = io.grpc.Metadata().apply { metadataMap.forEach { (key, value) -> diff --git a/grpc-multiplatform-lib/src/androidJvmCommon/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/stub/AndroidJvmKMStub.kt b/grpc-multiplatform-lib/src/androidJvmCommon/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/stub/AndroidJvmKMStub.kt index fdde71c..bebe004 100644 --- a/grpc-multiplatform-lib/src/androidJvmCommon/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/stub/AndroidJvmKMStub.kt +++ b/grpc-multiplatform-lib/src/androidJvmCommon/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/stub/AndroidJvmKMStub.kt @@ -3,9 +3,11 @@ package io.github.timortel.kotlin_multiplatform_grpc_lib.stub import io.github.timortel.kotlin_multiplatform_grpc_lib.KMChannel import io.github.timortel.kotlin_multiplatform_grpc_lib.util.TimeUnit import io.grpc.CallOptions -import io.grpc.stub.AbstractStub -//Additional layer of abstraction for the second generic argument. +// Additional layer of abstraction for the second generic argument. +/** + * The Android and Jvm stub type. + */ interface AndroidJvmKMStub> { val channel: KMChannel diff --git a/grpc-multiplatform-lib/src/androidJvmCommon/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/util/TimeUnit.kt b/grpc-multiplatform-lib/src/androidJvmCommon/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/util/TimeUnit.kt index c3e20e6..d6ca878 100644 --- a/grpc-multiplatform-lib/src/androidJvmCommon/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/util/TimeUnit.kt +++ b/grpc-multiplatform-lib/src/androidJvmCommon/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/util/TimeUnit.kt @@ -1,5 +1,8 @@ package io.github.timortel.kotlin_multiplatform_grpc_lib.util +/** + * On the jvm wrap [TimeUnit] with the java [java.util.concurrent.TimeUnit]. + */ actual enum class TimeUnit(val javaTimeUnit: java.util.concurrent.TimeUnit) { DAYS(java.util.concurrent.TimeUnit.DAYS), HOURS(java.util.concurrent.TimeUnit.HOURS), diff --git a/grpc-multiplatform-lib/src/commonMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/KMChannel.kt b/grpc-multiplatform-lib/src/commonMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/KMChannel.kt index c332c2f..ea882aa 100644 --- a/grpc-multiplatform-lib/src/commonMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/KMChannel.kt +++ b/grpc-multiplatform-lib/src/commonMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/KMChannel.kt @@ -1,16 +1,26 @@ package io.github.timortel.kotlin_multiplatform_grpc_lib /** - * Wrapps around the GRPC-Channel + * Wrapps around the GRPC-Channel. Create a channel using [Builder.forAddress]. + * For more information about grpc channels please refer to [the official grpc channel documentation](https://grpc.io/docs/what-is-grpc/core-concepts/#channels). */ expect class KMChannel { class Builder { companion object { + /** + * Construct a new [Builder]. Specify [name] and [port] that direct to your server. + */ fun forAddress(name: String, port: Int): Builder } + /** + * If called, the constructed channel will use http- + */ fun usePlaintext(): Builder + /** + * Construct the channel + */ fun build(): KMChannel } } \ No newline at end of file diff --git a/grpc-multiplatform-lib/src/commonMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/KMCode.kt b/grpc-multiplatform-lib/src/commonMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/KMCode.kt index 1a5f0fa..9a8a08f 100644 --- a/grpc-multiplatform-lib/src/commonMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/KMCode.kt +++ b/grpc-multiplatform-lib/src/commonMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/KMCode.kt @@ -24,6 +24,9 @@ enum class KMCode(val value: Int) { UNAUTHENTICATED(16); companion object { + /** + * Map the value back to the [KMCode]. + */ fun getCodeForValue(value: Int): KMCode = when (value) { 0 -> OK 1 -> CANCELLED diff --git a/grpc-multiplatform-lib/src/commonMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/KMMetadata.kt b/grpc-multiplatform-lib/src/commonMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/KMMetadata.kt index ee16e75..d53dea6 100644 --- a/grpc-multiplatform-lib/src/commonMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/KMMetadata.kt +++ b/grpc-multiplatform-lib/src/commonMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/KMMetadata.kt @@ -1,15 +1,27 @@ package io.github.timortel.kotlin_multiplatform_grpc_lib /** - * Metadata wrapper. + * Metadata allows you to pass key-value pairs to your requests, for example authentication metadata. + * Please refer to [the official metadata documentation](https://grpc.io/docs/what-is-grpc/core-concepts/#metadata). + * + * @property metadataMap the initial key-value pair configuration */ data class KMMetadata(val metadataMap: MutableMap = mutableMapOf()) { + /** + * Add a new entry to the key-value pairs. Replaces the current entry if the key already exists. + */ operator fun set(key: String, value: String) { metadataMap[key] = value } + /** + * Return the value for the given key, or null if the key does not exist in the map. + */ operator fun get(key: String) = metadataMap[key] + /** + * Join two metadata maps. + */ operator fun plus(other: KMMetadata) = KMMetadata((metadataMap + other.metadataMap).toMutableMap()) } \ No newline at end of file diff --git a/grpc-multiplatform-lib/src/commonMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/KMStatus.kt b/grpc-multiplatform-lib/src/commonMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/KMStatus.kt index 4dc1a31..2883717 100644 --- a/grpc-multiplatform-lib/src/commonMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/KMStatus.kt +++ b/grpc-multiplatform-lib/src/commonMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/KMStatus.kt @@ -1,5 +1,6 @@ package io.github.timortel.kotlin_multiplatform_grpc_lib -data class KMStatus(val code: KMCode, val statusMessage: String) { - -} \ No newline at end of file +/** + * Represents a [grpc status](https://grpc.github.io/grpc/core/md_doc_statuscodes.html?ref=apisyouwonthate.com). + */ +data class KMStatus(val code: KMCode, val statusMessage: String) diff --git a/grpc-multiplatform-lib/src/commonMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/KMStatusException.kt b/grpc-multiplatform-lib/src/commonMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/KMStatusException.kt index 08a61d5..b157409 100644 --- a/grpc-multiplatform-lib/src/commonMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/KMStatusException.kt +++ b/grpc-multiplatform-lib/src/commonMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/KMStatusException.kt @@ -1,4 +1,6 @@ package io.github.timortel.kotlin_multiplatform_grpc_lib -class KMStatusException(val status: KMStatus, cause: Throwable?) : RuntimeException(cause) { -} \ No newline at end of file +/** + * An exception throws when an erroneous status occurs in a call. + */ +data class KMStatusException(val status: KMStatus, override val cause: Throwable?) : RuntimeException(cause) diff --git a/grpc-multiplatform-lib/src/iosJvmCommon/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/CodedInputStream.kt b/grpc-multiplatform-lib/src/commonMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/CodedInputStream.kt similarity index 77% rename from grpc-multiplatform-lib/src/iosJvmCommon/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/CodedInputStream.kt rename to grpc-multiplatform-lib/src/commonMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/CodedInputStream.kt index e21d39d..a926b71 100644 --- a/grpc-multiplatform-lib/src/iosJvmCommon/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/CodedInputStream.kt +++ b/grpc-multiplatform-lib/src/commonMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/CodedInputStream.kt @@ -3,6 +3,10 @@ package io.github.timortel.kotlin_multiplatform_grpc_lib.io import io.github.timortel.kotlin_multiplatform_grpc_lib.message.DataType import io.github.timortel.kotlin_multiplatform_grpc_lib.message.KMMessage +/** + * Base class that decodes messages sent over the network connection. Counterpart to [CodedOutputStream]. + * See [the java CodedInputStream implementation](https://github.com/protocolbuffers/protobuf/blob/main/java/core/src/main/java/com/google/protobuf/CodedInputStream.java) for further details. + */ expect class CodedInputStream { var recursionDepth: Int @@ -38,8 +42,14 @@ expect class CodedInputStream { fun readString(): String + /** + * Read a [KMMessage] from the stream. + */ fun readKMMessage(messageFactory: (CodedInputStream) -> M): M + /** + * Read a map entry from the stream following the grpc specification. + */ fun readMapEntry( map: MutableMap, keyDataType: DataType, diff --git a/grpc-multiplatform-lib/src/iosJvmCommon/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/CodedOutputStream.kt b/grpc-multiplatform-lib/src/commonMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/CodedOutputStream.kt similarity index 92% rename from grpc-multiplatform-lib/src/iosJvmCommon/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/CodedOutputStream.kt rename to grpc-multiplatform-lib/src/commonMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/CodedOutputStream.kt index c95622d..2affc5d 100644 --- a/grpc-multiplatform-lib/src/iosJvmCommon/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/CodedOutputStream.kt +++ b/grpc-multiplatform-lib/src/commonMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/CodedOutputStream.kt @@ -2,6 +2,10 @@ package io.github.timortel.kotlin_multiplatform_grpc_lib.io import io.github.timortel.kotlin_multiplatform_grpc_lib.message.KMMessage +/** + * Base class that encodes messages to send them over the network connection. Counterpart to [CodedInputStream]. + * See [the java CodedOutputStream implementation](https://github.com/protocolbuffers/protobuf/blob/main/java/core/src/main/java/com/google/protobuf/CodedOutputStream.java) for further details. + */ expect class CodedOutputStream { fun writeBool(fieldNumber: Int, value: Boolean) diff --git a/grpc-multiplatform-lib/src/iosJvmCommon/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/ParseException.kt b/grpc-multiplatform-lib/src/commonMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/ParseException.kt similarity index 100% rename from grpc-multiplatform-lib/src/iosJvmCommon/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/ParseException.kt rename to grpc-multiplatform-lib/src/commonMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/ParseException.kt diff --git a/grpc-multiplatform-lib/src/iosJvmCommon/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/const.kt b/grpc-multiplatform-lib/src/commonMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/const.kt similarity index 50% rename from grpc-multiplatform-lib/src/iosJvmCommon/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/const.kt rename to grpc-multiplatform-lib/src/commonMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/const.kt index fe89717..cb5b6ea 100644 --- a/grpc-multiplatform-lib/src/iosJvmCommon/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/const.kt +++ b/grpc-multiplatform-lib/src/commonMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/const.kt @@ -1,5 +1,5 @@ package io.github.timortel.kotlin_multiplatform_grpc_lib.io -//https://github.com/protocolbuffers/protobuf/blob/520c601c99012101c816b6ccc89e8d6fc28fdbb8/objectivec/GPBDictionary.m#L66 +// https://github.com/protocolbuffers/protobuf/blob/520c601c99012101c816b6ccc89e8d6fc28fdbb8/objectivec/GPBDictionary.m#L66 const val kMapKeyFieldNumber = 1 const val kMapValueFieldNumber = 2 \ No newline at end of file diff --git a/grpc-multiplatform-lib/src/iosJvmCommon/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/message_reader.kt b/grpc-multiplatform-lib/src/commonMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/message_reader.kt similarity index 96% rename from grpc-multiplatform-lib/src/iosJvmCommon/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/message_reader.kt rename to grpc-multiplatform-lib/src/commonMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/message_reader.kt index 09fda2b..7745c60 100644 --- a/grpc-multiplatform-lib/src/iosJvmCommon/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/message_reader.kt +++ b/grpc-multiplatform-lib/src/commonMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/message_reader.kt @@ -3,6 +3,9 @@ package io.github.timortel.kotlin_multiplatform_grpc_lib.io import io.github.timortel.kotlin_multiplatform_grpc_lib.message.DataType import io.github.timortel.kotlin_multiplatform_grpc_lib.message.KMMessage +/** + * Read a [KMMessage] from the [wrapper] using the provided [messageFactory]. + */ fun readKMMessage( wrapper: CodedInputStream, messageFactory: (CodedInputStream) -> M diff --git a/grpc-multiplatform-lib/src/iosJvmCommon/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/wireformat.kt b/grpc-multiplatform-lib/src/commonMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/wireformat.kt similarity index 100% rename from grpc-multiplatform-lib/src/iosJvmCommon/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/wireformat.kt rename to grpc-multiplatform-lib/src/commonMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/wireformat.kt diff --git a/grpc-multiplatform-lib/src/commonMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/message/DataType.kt b/grpc-multiplatform-lib/src/commonMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/message/DataType.kt new file mode 100644 index 0000000..db74f5c --- /dev/null +++ b/grpc-multiplatform-lib/src/commonMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/message/DataType.kt @@ -0,0 +1,25 @@ +package io.github.timortel.kotlin_multiplatform_grpc_lib.message + +/** + * The supported data types that can be used as message fields. + */ +expect enum class DataType { + DOUBLE, + FLOAT, + INT64, + UINT64, + INT32, + FIXED64, + FIXED32, + BOOL, + STRING, + GROUP, + MESSAGE, + BYTES, + UINT32, + ENUM, + SFIXED32, + SFIXED64, + SINT32, + SINT64 +} \ No newline at end of file diff --git a/grpc-multiplatform-lib/src/commonMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/message/KMMessage.kt b/grpc-multiplatform-lib/src/commonMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/message/KMMessage.kt index 8465b62..ca51f0f 100644 --- a/grpc-multiplatform-lib/src/commonMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/message/KMMessage.kt +++ b/grpc-multiplatform-lib/src/commonMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/message/KMMessage.kt @@ -1,3 +1,6 @@ package io.github.timortel.kotlin_multiplatform_grpc_lib.message -expect interface KMMessage \ No newline at end of file +/** + * Base interface for all grpc messages. All messages are automatically generated by the gradle plugin based on your proto definitions. + */ +expect interface KMMessage diff --git a/grpc-multiplatform-lib/src/iosJvmCommon/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/message/MessageDeserializer.kt b/grpc-multiplatform-lib/src/commonMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/message/MessageDeserializer.kt similarity index 65% rename from grpc-multiplatform-lib/src/iosJvmCommon/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/message/MessageDeserializer.kt rename to grpc-multiplatform-lib/src/commonMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/message/MessageDeserializer.kt index c264a64..97f383c 100644 --- a/grpc-multiplatform-lib/src/iosJvmCommon/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/message/MessageDeserializer.kt +++ b/grpc-multiplatform-lib/src/commonMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/message/MessageDeserializer.kt @@ -1,5 +1,8 @@ package io.github.timortel.kotlin_multiplatform_grpc_lib.message +/** + * Construct a the message of type [T] based on the data of type [T]. + */ interface MessageDeserializer { fun deserialize(`data`: K): T } \ No newline at end of file diff --git a/grpc-multiplatform-lib/src/commonMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/stub/KMStub.kt b/grpc-multiplatform-lib/src/commonMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/stub/KMStub.kt index e3f9280..1ea2f4e 100644 --- a/grpc-multiplatform-lib/src/commonMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/stub/KMStub.kt +++ b/grpc-multiplatform-lib/src/commonMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/stub/KMStub.kt @@ -2,6 +2,13 @@ package io.github.timortel.kotlin_multiplatform_grpc_lib.stub import io.github.timortel.kotlin_multiplatform_grpc_lib.util.TimeUnit +/** + * A stub allows you to make your rpc requests. Stubs are generated based on your proto definition by the gradle plugin. + */ abstract class KMStub> { + + /** + * @return a new stub that will abort requests after the specified amount of time. + */ abstract fun withDeadlineAfter(duration: Long, unit: TimeUnit): S } \ No newline at end of file diff --git a/grpc-multiplatform-lib/src/commonMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/util/TimeUnit.kt b/grpc-multiplatform-lib/src/commonMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/util/TimeUnit.kt index e3984bd..b0c0582 100644 --- a/grpc-multiplatform-lib/src/commonMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/util/TimeUnit.kt +++ b/grpc-multiplatform-lib/src/commonMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/util/TimeUnit.kt @@ -1,5 +1,8 @@ package io.github.timortel.kotlin_multiplatform_grpc_lib.util +/** + * Multiplatform time unit specification. + */ expect enum class TimeUnit { DAYS, HOURS, diff --git a/grpc-multiplatform-lib/src/iosJvmCommon/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/map_writer.kt b/grpc-multiplatform-lib/src/iosJvmCommon/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/map_writer.kt new file mode 100644 index 0000000..1495b02 --- /dev/null +++ b/grpc-multiplatform-lib/src/iosJvmCommon/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/map_writer.kt @@ -0,0 +1,24 @@ +package io.github.timortel.kotlin_multiplatform_grpc_lib.io + +fun writeMap( + stream: CodedOutputStream, + fieldNumber: Int, + map: Map, + getKeySize: (fieldNumber: Int, key: K) -> Int, + getValueSize: (fieldNumber: Int, value: V) -> Int, + writeKey: CodedOutputStream.(fieldNumber: Int, K) -> Unit, + writeValue: CodedOutputStream.(fieldNumber: Int, V) -> Unit +) { + val tag = wireFormatMakeTag(fieldNumber, WireFormat.LENGTH_DELIMITED) + map.forEach { (key, value) -> + //Write tag + stream.writeInt32NoTag(tag) + //Write the size of the message + val msgSize = + getKeySize(kMapKeyFieldNumber, key) + getValueSize(kMapValueFieldNumber, value) + stream.writeInt32NoTag(msgSize) + //Write fields + writeKey(stream, kMapKeyFieldNumber, key) + writeValue(stream, kMapValueFieldNumber, value) + } +} \ No newline at end of file diff --git a/grpc-multiplatform-lib/src/iosJvmCommon/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/message_writer.kt b/grpc-multiplatform-lib/src/iosJvmCommon/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/message_writer.kt index eb2cb0a..cdc79ae 100644 --- a/grpc-multiplatform-lib/src/iosJvmCommon/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/message_writer.kt +++ b/grpc-multiplatform-lib/src/iosJvmCommon/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/message_writer.kt @@ -25,28 +25,6 @@ fun writeMessageList( values.forEach { writeKMMessage(stream, fieldNumber, it, requiredSize(it), writeMessage) } } -fun writeMap( - stream: CodedOutputStream, - fieldNumber: Int, - map: Map, - getKeySize: (fieldNumber: Int, key: K) -> Int, - getValueSize: (fieldNumber: Int, value: V) -> Int, - writeKey: CodedOutputStream.(fieldNumber: Int, K) -> Unit, - writeValue: CodedOutputStream.(fieldNumber: Int, V) -> Unit -) { - val tag = wireFormatMakeTag(fieldNumber, WireFormat.LENGTH_DELIMITED) - map.forEach { (key, value) -> - //Write tag - stream.writeInt32NoTag(tag) - //Write the size of the message - val msgSize = getKeySize(kMapKeyFieldNumber, key) + getValueSize(kMapValueFieldNumber, value) - stream.writeInt32NoTag(msgSize) - //Write fields - writeKey(stream, kMapKeyFieldNumber, key) - writeValue(stream, kMapValueFieldNumber, value) - } -} - fun writeArray( stream: CodedOutputStream, fieldNumber: Int, diff --git a/grpc-multiplatform-lib/src/iosMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/KMChannel.kt b/grpc-multiplatform-lib/src/iosMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/KMChannel.kt index 262f5d2..c20618e 100644 --- a/grpc-multiplatform-lib/src/iosMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/KMChannel.kt +++ b/grpc-multiplatform-lib/src/iosMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/KMChannel.kt @@ -1,7 +1,11 @@ package io.github.timortel.kotlin_multiplatform_grpc_lib import cocoapods.GRPCClient.* +import kotlinx.cinterop.ExperimentalForeignApi +/** + * On ios the channel equivalent are the [GRPCCallOptions]. + */ actual class KMChannel(private val name: String, private val port: Int, private val usePlaintext: Boolean) { fun buildRequestOptions(path: String) = GRPCRequestOptions("$name:$port", path, safety = GRPCCallSafetyDefault) diff --git a/grpc-multiplatform-lib/src/iosMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/CodedInputStream.kt b/grpc-multiplatform-lib/src/iosMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/CodedInputStream.kt index 50874fc..01996a7 100644 --- a/grpc-multiplatform-lib/src/iosMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/CodedInputStream.kt +++ b/grpc-multiplatform-lib/src/iosMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/CodedInputStream.kt @@ -3,6 +3,7 @@ package io.github.timortel.kotlin_multiplatform_grpc_lib.io import cocoapods.Protobuf.GPBCodedInputStream import io.github.timortel.kotlin_multiplatform_grpc_lib.message.DataType import io.github.timortel.kotlin_multiplatform_grpc_lib.message.KMMessage +import kotlinx.cinterop.ExperimentalForeignApi import kotlinx.cinterop.addressOf import kotlinx.cinterop.usePinned import kotlinx.cinterop.memScoped @@ -13,10 +14,13 @@ import platform.Foundation.NSData import platform.Foundation.create import platform.posix.memcpy +/** + * Implements the functionality by wrapping [GPBCodedInputStream]. + */ +@OptIn(ExperimentalForeignApi::class) actual class CodedInputStream(private val impl: GPBCodedInputStream, actual var recursionDepth: Int = 0) { - actual val bytesUntilLimit: Int - get() = TODO() + actual val bytesUntilLimit: Int get() = throw NotImplementedError() actual val isAtEnd: Boolean get() = impl.isAtEnd() @@ -98,6 +102,5 @@ actual class CodedInputStream(private val impl: GPBCodedInputStream, actual var actual fun popLimit(oldLimit: Int) = impl.popLimit(oldLimit.toULong()) - actual fun setSizeLimit(newLimit: Int): Int = TODO() - + actual fun setSizeLimit(newLimit: Int): Int = throw NotImplementedError() } \ No newline at end of file diff --git a/grpc-multiplatform-lib/src/iosMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/CodedOutputStream.kt b/grpc-multiplatform-lib/src/iosMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/CodedOutputStream.kt index d9df9d6..520e1dd 100644 --- a/grpc-multiplatform-lib/src/iosMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/CodedOutputStream.kt +++ b/grpc-multiplatform-lib/src/iosMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/CodedOutputStream.kt @@ -4,11 +4,15 @@ import cocoapods.Protobuf.* import io.github.timortel.kotlin_multiplatform_grpc_lib.message.KMMessage import io.github.timortel.kotlin_multiplatform_grpc_lib.message.requiredSizeMessage import io.github.timortel.kotlin_multiplatform_grpc_lib.message.serializeMessage +import kotlinx.cinterop.ExperimentalForeignApi import kotlinx.cinterop.allocArrayOf import kotlinx.cinterop.memScoped import platform.Foundation.NSData import platform.Foundation.create +/** + * Implements the functionality by wrapping [GPBCodedOutputStream]. + */ actual class CodedOutputStream(private val impl: GPBCodedOutputStream) { companion object { @@ -82,7 +86,6 @@ actual class CodedOutputStream(private val impl: GPBCodedOutputStream) { tag: UInt ) { writeArray(this, fieldNumber, values, tag, ::GPBComputeFixed64SizeNoTag, ::writeFixed64NoTag, ::writeFixed64) - } actual fun writeFixed64NoTag(value: ULong) = impl.writeFixed64NoTag(value) diff --git a/grpc-multiplatform-lib/src/iosMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/GPBCodedInputStreamWrapper.kt b/grpc-multiplatform-lib/src/iosMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/GPBCodedInputStreamWrapper.kt index fee3552..aa59b7c 100644 --- a/grpc-multiplatform-lib/src/iosMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/GPBCodedInputStreamWrapper.kt +++ b/grpc-multiplatform-lib/src/iosMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/GPBCodedInputStreamWrapper.kt @@ -2,4 +2,7 @@ package io.github.timortel.kotlin_multiplatform_grpc_lib.io import cocoapods.Protobuf.GPBCodedInputStream +/** + * Additional wrapper of [GPBCodedInputStream] to keep track of the [recursionDepth]. + */ data class GPBCodedInputStreamWrapper(val stream: GPBCodedInputStream, var recursionDepth: Int = 0) \ No newline at end of file diff --git a/grpc-multiplatform-lib/src/iosMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/message/DataType.kt b/grpc-multiplatform-lib/src/iosMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/message/DataType.kt index 34e1216..d5cc59b 100644 --- a/grpc-multiplatform-lib/src/iosMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/message/DataType.kt +++ b/grpc-multiplatform-lib/src/iosMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/message/DataType.kt @@ -2,6 +2,9 @@ package io.github.timortel.kotlin_multiplatform_grpc_lib.message import cocoapods.Protobuf.* +/** + * On ios, the data type is represented by its [nativeValue]. + */ actual enum class DataType(val nativeValue: UByte) { DOUBLE(GPBDataTypeBool), FLOAT(GPBDataTypeFloat), diff --git a/grpc-multiplatform-lib/src/iosMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/message/KMMessage.kt b/grpc-multiplatform-lib/src/iosMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/message/KMMessage.kt index 27f8670..77d3aef 100644 --- a/grpc-multiplatform-lib/src/iosMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/message/KMMessage.kt +++ b/grpc-multiplatform-lib/src/iosMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/message/KMMessage.kt @@ -12,8 +12,14 @@ import platform.posix.size_t */ actual interface KMMessage { + /** + * The size this message takes up in a byte array. + */ val requiredSize: Int + /** + * Serializes this message and returns it as [NSData]. + */ fun serialize(): NSData { val data = NSMutableData().apply { setLength(requiredSize.toULong()) } val stream = GPBCodedOutputStream(data) @@ -22,6 +28,9 @@ actual interface KMMessage { return data } + /** + * Serializes this message and writes it directly to [CodedOutputStream]. + */ fun serialize(stream: CodedOutputStream) } diff --git a/grpc-multiplatform-lib/src/iosMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/rpc/rpc_implementation.kt b/grpc-multiplatform-lib/src/iosMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/rpc/rpc_implementation.kt index b527e96..408076b 100644 --- a/grpc-multiplatform-lib/src/iosMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/rpc/rpc_implementation.kt +++ b/grpc-multiplatform-lib/src/iosMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/rpc/rpc_implementation.kt @@ -21,6 +21,9 @@ import kotlin.coroutines.resume import kotlin.coroutines.resumeWithException import kotlin.coroutines.suspendCoroutine +/** + * Perform a unary rpc call as a suspending function. Uses [GRPCCall2] for the actual call. + */ @Throws(KMStatusException::class, CancellationException::class) suspend fun unaryCallImplementation( channel: KMChannel, @@ -62,6 +65,10 @@ suspend fun unaryCallImplementation( } } +/** + * Performs a server side stream call and returns a [Flow] that emits whenever we receive a new message from the server. + * Uses [GRPCCall2] for the actual call. + */ fun serverSideStreamingCallImplementation( channel: KMChannel, callOptions: GRPCCallOptions, diff --git a/grpc-multiplatform-lib/src/iosMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/stub/IOSKMStub.kt b/grpc-multiplatform-lib/src/iosMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/stub/IOSKMStub.kt index 7795b8b..9138081 100644 --- a/grpc-multiplatform-lib/src/iosMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/stub/IOSKMStub.kt +++ b/grpc-multiplatform-lib/src/iosMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/stub/IOSKMStub.kt @@ -6,13 +6,29 @@ import io.github.timortel.kotlin_multiplatform_grpc_lib.KMChannel import io.github.timortel.kotlin_multiplatform_grpc_lib.KMMetadata import io.github.timortel.kotlin_multiplatform_grpc_lib.util.TimeUnit +/** + * Ios [KMStub] wrapper. + */ interface IOSKMStub> { + /** + * The [KMChannel] of this stub. + */ val channel: KMChannel + + /** + * The current [GRPCCallOptions] of this stub. + */ val callOptions: GRPCCallOptions + /** + * Construct a new channel using both the associated [KMChannel] and [GRPCCallOptions]. + */ fun build(channel: KMChannel, callOptions: GRPCCallOptions): S + /** + * @return a new stub that sends the given [metadata] on each request. + */ fun withMetadata(metadata: KMMetadata): S { val mutableOptions = callOptions.mutableCopy() as GRPCMutableCallOptions mutableOptions.setInitialMetadata(metadata.metadataMap.toMap()) @@ -20,6 +36,9 @@ interface IOSKMStub> { return build(channel, mutableOptions) } + /** + * @return a new stub that aborts every call after the specified deadline. + */ fun withDeadlineAfter(duration: Long, unit: TimeUnit): S { val mutableOptions = callOptions.mutableCopy() as GRPCMutableCallOptions diff --git a/grpc-multiplatform-lib/src/iosMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/util/TimeUnit.kt b/grpc-multiplatform-lib/src/iosMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/util/TimeUnit.kt index 7749f3b..e9582c9 100644 --- a/grpc-multiplatform-lib/src/iosMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/util/TimeUnit.kt +++ b/grpc-multiplatform-lib/src/iosMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/util/TimeUnit.kt @@ -1,5 +1,8 @@ package io.github.timortel.kotlin_multiplatform_grpc_lib.util +/** + * Implements the time unit using a factor that converts the time unit into milliseconds. + */ actual enum class TimeUnit(val toMilliFactor: Long) { DAYS(24 * 60 * 60 * 1000), HOURS(60 * 60 * 1000), diff --git a/grpc-multiplatform-lib/src/jsMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/JSPB.kt b/grpc-multiplatform-lib/src/jsMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/JSPB.kt index 5966b17..e6739c5 100644 --- a/grpc-multiplatform-lib/src/jsMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/JSPB.kt +++ b/grpc-multiplatform-lib/src/jsMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/JSPB.kt @@ -1,6 +1,9 @@ package io.github.timortel.kotlin_multiplatform_grpc_lib +import org.khronos.webgl.Uint8Array + @JsModule("google-protobuf") +@JsNonModule external object JSPB { object Message { @@ -38,16 +41,24 @@ external object JSPB { fun inherits(clazz: dynamic, superClazz: dynamic) class BinaryWriter { - fun getResultBuffer(): dynamic + + @JsName("encoder_") + val encoder: BinaryEncoder + + fun getResultBuffer(): Uint8Array fun writeDouble(field: Int, value: Double) fun writeFloat(field: Int, value: Float) fun writeInt64(field: Int, value: Long) + fun writeSint64(field: Int, value: Long) fun writeUInt64(field: Int, value: dynamic) fun writeInt32(field: Int, value: Int) + fun writeSint32(field: Int, value: Int) fun writeUInt32(field: Int, value: dynamic) fun writeFixed64(field: Int, value: dynamic) fun writeFixed32(field: Int, value: dynamic) + fun writeSfixed64(field: Int, value: dynamic) + fun writeSfixed32(field: Int, value: dynamic) fun writeBool(field: Int, value: Boolean) fun writeGroup(field: Int, value: dynamic) fun writeBytes(field: Int, value: dynamic) @@ -55,17 +66,70 @@ external object JSPB { fun writeMessage(field: Int, value: dynamic, writerCallback: dynamic) fun writeEnum(field: Int, value: Int) + fun writeRepeatedBytes(field: Int, value: dynamic) + fun writeRepeatedString(field: Int, value: dynamic) fun writePackedBool(field: Int, value: dynamic) + fun writeRepeatedBool(field: Int, value: dynamic) fun writePackedFloat(field: Int, value: dynamic) + fun writeRepeatedFloat(field: Int, value: dynamic) fun writePackedDouble(field: Int, value: dynamic) + fun writeRepeatedDouble(field: Int, value: dynamic) fun writePackedInt32(field: Int, value: dynamic) + fun writeRepeatedInt32(field: Int, value: dynamic) fun writePackedInt64(field: Int, value: dynamic) + fun writeRepeatedInt64(field: Int, value: dynamic) fun writeRepeatedMessage(field: Int, value: dynamic, writerCallback: dynamic) fun writePackedEnum(field: Int, value: dynamic) + fun writeRepeatedEnum(field: Int, value: dynamic) + fun writePackedFixed32(field: Int, value: dynamic) + fun writeRepeatedFixed32(field: Int, value: dynamic) + fun writePackedFixed64(field: Int, value: dynamic) + fun writeRepeatedFixed64(field: Int, value: dynamic) + + fun writePackedSfixed32(field: Int, value: dynamic) + fun writeRepeatedSfixed32(field: Int, value: dynamic) + fun writePackedSfixed64(field: Int, value: dynamic) + fun writeRepeatedSfixed64(field: Int, value: dynamic) + + fun writePackedSint32(field: Int, value: dynamic) + fun writeRepeatedSint32(field: Int, value: dynamic) + fun writePackedSint64(field: Int, value: dynamic) + fun writeRepeatedSint64(field: Int, value: dynamic) + + fun writePackedUint32(field: Int, value: dynamic) + fun writeRepeatedUint32(field: Int, value: dynamic) + fun writePackedUint64(field: Int, value: dynamic) + fun writeRepeatedUint64(field: Int, value: dynamic) + + @JsName("appendUint8Array_") + fun appendUint8Array(value: dynamic) + + @JsName("beginDelimited_") + fun beginDelimited(field: Int): dynamic + + @JsName("endDelimited_") + fun endDelimited(bookmark: dynamic) + + fun beginSubMessage(fieldNumber: Int) + + fun endSubMessage() } class BinaryReader(bytes: dynamic) { + + @JsName("nextField_") + var nextField: Int + + @JsName("nextWireType_") + var nextWireType: Int + + @JsName("decoder_") + val decoder: BinaryDecoder + + @JsName("fieldCursor_") + var fieldCursor: dynamic + fun nextField(): Boolean fun isEndGroup(): Boolean @@ -75,11 +139,16 @@ external object JSPB { fun readDouble(): Double fun readFloat(): Float fun readInt64(): dynamic + fun readSInt32(): dynamic + fun readSInt64(): dynamic fun readUInt64(): dynamic fun readInt32(): Int fun readUInt32(): dynamic fun readFixed64(): dynamic fun readFixed32(): dynamic + fun readSfixed32(): dynamic + fun readSfixed64(): dynamic + fun readBool(): Boolean fun readBytes(): dynamic fun readString(): String @@ -101,4 +170,59 @@ external object JSPB { object Map { fun deserializeBinary(message: dynamic, reader: dynamic, keyReader: dynamic, valueReader: dynamic, valueReaderCallback: dynamic, defaultKey: dynamic, defaultValue: dynamic) } + + class BinaryDecoder { + fun setEnd(limit: Int) + + fun getEnd(): Int + + fun atEnd(): Boolean + + fun readInt32(): Number + fun readInt64(): Number + + fun readZigzagVarint32(): Number + fun readZigzagVarint64(): Number + + fun readUint32(): Number + fun readUint64(): Number + + fun readUnsignedVarint32(): Int + fun readUnsignedVarint64(): Int + + fun readSignedVarint32(): Int + fun readSignedVarint64(): Number + + fun readFloat(): Float + fun readDouble(): Double + + fun readBool(): Boolean + + fun getCursor(): Int + } + + class BinaryEncoder { + fun writeBool(value: dynamic) + fun writeFloat(value: dynamic) + fun writeDouble(value: dynamic) + fun writeSplitFixed32(value: dynamic) + fun writeSplitFixed64(value: dynamic) + fun writeUint32(value: dynamic) + fun writeUint64(value: dynamic) + fun writeInt8(value: dynamic) + fun writeInt32(value: dynamic) + fun writeInt64(value: dynamic) + + fun writeSignedVarint32(value: dynamic) + fun writeSignedVarint64(value: dynamic) + + fun writeUnsignedVarint32(value: dynamic) + fun writeUnsignedVarint64(value: dynamic) + + fun writeZigzagVarint32(value: dynamic) + fun writeZigzagVarint64(value: dynamic) + + fun writeEnum(value: Int) + fun writeString(value: String) + } } \ No newline at end of file diff --git a/grpc-multiplatform-lib/src/jsMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/KMChannel.kt b/grpc-multiplatform-lib/src/jsMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/KMChannel.kt index 6d12fe4..3590b72 100644 --- a/grpc-multiplatform-lib/src/jsMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/KMChannel.kt +++ b/grpc-multiplatform-lib/src/jsMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/KMChannel.kt @@ -4,8 +4,7 @@ package io.github.timortel.kotlin_multiplatform_grpc_lib actual class KMChannel private constructor( name: String, port: Int, - usePlainText: Boolean, - val metadata: KMMetadata + usePlainText: Boolean ) { val connectionString = (if (usePlainText) "http://" else "https://") + "$name:$port" @@ -26,6 +25,6 @@ actual class KMChannel private constructor( return this } - actual fun build(): KMChannel = KMChannel(name, port, usePlainText, KMMetadata()) + actual fun build(): KMChannel = KMChannel(name, port, usePlainText) } } \ No newline at end of file diff --git a/grpc-multiplatform-lib/src/jsMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/CodedInputStream.kt b/grpc-multiplatform-lib/src/jsMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/CodedInputStream.kt new file mode 100644 index 0000000..b5b9412 --- /dev/null +++ b/grpc-multiplatform-lib/src/jsMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/CodedInputStream.kt @@ -0,0 +1,140 @@ +package io.github.timortel.kotlin_multiplatform_grpc_lib.io + +import io.github.timortel.kotlin_multiplatform_grpc_lib.JSPB +import io.github.timortel.kotlin_multiplatform_grpc_lib.message.DataType +import io.github.timortel.kotlin_multiplatform_grpc_lib.message.KMMessage +import org.khronos.webgl.Int8Array +import org.khronos.webgl.Uint8Array + +actual class CodedInputStream( + private val impl: JSPB.BinaryReader, + actual var recursionDepth: Int = 0 +) { + + private var lastReadTag: Int = 0 + + actual val bytesUntilLimit: Int get() = throw NotImplementedError() + actual val isAtEnd: Boolean + get() = impl.decoder.atEnd() + + actual fun readTag(): Int { + if (isAtEnd) { + lastReadTag = 0 + return 0 + } + impl.fieldCursor = impl.decoder.getCursor() + val tag = impl.decoder.readUnsignedVarint32() + + impl.nextField = tag ushr 3 + impl.nextWireType = tag and 0x7 + + lastReadTag = tag + + return tag + } + + actual fun checkLastTagWas(value: Int) { + if (lastReadTag != value) { + throw ParseException() + } + } + + actual fun getLastTag(): Int = lastReadTag + + actual fun skipField(tag: Int): Boolean { + // On an error skipField throws + return try { + impl.skipField() + true + } catch (_: Throwable) { + false + } + } + + actual fun skipMessage() { + while (true) { + val tag = readTag() + if (tag == 0 || !skipField(tag)) return + } + } + + actual fun readDouble(): Double = impl.decoder.readDouble() + + actual fun readFloat(): Float = impl.decoder.readFloat() + + actual fun readUInt64(): ULong = impl.decoder.readUnsignedVarint64().toLong().toULong() + + actual fun readInt64(): Long = impl.decoder.readSignedVarint64().toLong() + + actual fun readInt32(): Int = impl.decoder.readSignedVarint32() + + actual fun readFixed32(): Int = impl.decoder.readUint32().toInt() + + actual fun readBool(): Boolean = impl.decoder.readBool() + + actual fun readString(): String = impl.readString() + + /** + * Read a [KMMessage] from the stream. + */ + actual fun readKMMessage(messageFactory: (CodedInputStream) -> M): M { + return readKMMessage(this, messageFactory) + } + + /** + * Read a map entry from the stream following the grpc specification. + */ + actual fun readMapEntry( + map: MutableMap, + keyDataType: DataType, + valueDataType: DataType, + defaultKey: K?, + defaultValue: V?, + readKey: CodedInputStream.() -> K, + readValue: CodedInputStream.() -> V + ) = readMapEntry( + this, + map, + keyDataType, + valueDataType, + defaultKey, + defaultValue, + readKey, + readValue + ) + + actual fun readBytes(): ByteArray = + Int8Array((impl.readBytes() as Uint8Array).buffer).unsafeCast() + + actual fun readByteArray(): ByteArray = throw NotImplementedError() + + actual fun readUInt32(): UInt = impl.decoder.readUnsignedVarint32().toUInt() + + actual fun readEnum(): Int = impl.decoder.readSignedVarint64().toInt() + + actual fun readSFixed32(): Int = impl.decoder.readInt32().toInt() + + actual fun readSFixed64(): Long = impl.decoder.readInt64().toLong() + + actual fun readSInt32(): Int = impl.decoder.readZigzagVarint32().toInt() + + actual fun readSInt64(): Long = impl.decoder.readZigzagVarint32().toLong() + + actual fun readRawVarint32(): Int = impl.decoder.readSignedVarint32() + + actual fun readRawVarint64(): Long = impl.decoder.readSignedVarint64().toLong() + + actual fun readRawByte(): Byte = throw NotImplementedError("Not available in js") + + actual fun pushLimit(newLimit: Int): Int { + val oldLimit = impl.decoder.getEnd() + impl.decoder.setEnd(impl.decoder.getCursor() + newLimit) + return oldLimit + } + + actual fun popLimit(oldLimit: Int) { + impl.decoder.setEnd(oldLimit) + } + + actual fun setSizeLimit(newLimit: Int): Int = throw NotImplementedError() +} \ No newline at end of file diff --git a/grpc-multiplatform-lib/src/jsMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/CodedOutputStream.kt b/grpc-multiplatform-lib/src/jsMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/CodedOutputStream.kt new file mode 100644 index 0000000..b745e3a --- /dev/null +++ b/grpc-multiplatform-lib/src/jsMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/CodedOutputStream.kt @@ -0,0 +1,313 @@ +package io.github.timortel.kotlin_multiplatform_grpc_lib.io + +import io.github.timortel.kotlin_multiplatform_grpc_lib.JSPB +import io.github.timortel.kotlin_multiplatform_grpc_lib.message.KMMessage +import io.github.timortel.kotlin_multiplatform_grpc_lib.message.serializeMessage + +actual class CodedOutputStream(internal val impl: JSPB.BinaryWriter) { + + actual fun writeBool(fieldNumber: Int, value: Boolean) = impl.writeBool(fieldNumber, value) + + actual fun writeBoolArray( + fieldNumber: Int, + value: List, + tag: UInt + ) = writeArray(this, fieldNumber, value, tag, writeNoTag = ::writeBoolNoTag, writeWithTag = ::writeBool) + + actual fun writeBoolNoTag(value: Boolean) { + impl.encoder.writeBool(value) + } + + actual fun writeBytes(fieldNumber: Int, value: ByteArray) { + impl.writeBytes(fieldNumber, value) + } + + actual fun writeBytesArray( + fieldNumber: Int, + value: List, + tag: UInt + ) = writeArray(tag, writeRepeated = { + impl.writeRepeatedBytes(fieldNumber, value) + }, writePacked = { + impl.writeBytes(fieldNumber, value) + }) + + actual fun writeBytesNoTag(value: ByteArray) { + impl.appendUint8Array(value) + } + + actual fun writeDouble(fieldNumber: Int, value: Double) { + impl.writeDouble(fieldNumber, value) + } + + actual fun writeDoubleArray( + fieldNumber: Int, + values: List, + tag: UInt + ) = writeArray(this, fieldNumber, values, tag, writeNoTag = ::writeDoubleNoTag, writeWithTag = ::writeDouble) + + actual fun writeDoubleNoTag(value: Double) { + impl.encoder.writeDouble(value) + } + + actual fun writeEnum(fieldNumber: Int, value: Int) { + impl.writeEnum(fieldNumber, value) + } + + actual fun writeEnumArray( + fieldNumber: Int, + values: List, + tag: UInt + ) = writeArray(this, fieldNumber, values, tag, writeNoTag = ::writeEnumNoTag, writeWithTag = ::writeEnum) + + actual fun writeEnumNoTag(value: Int) { + impl.encoder.writeEnum(value) + } + + actual fun writeFixed32(fieldNumber: Int, value: UInt) { + impl.writeFixed32(fieldNumber, value) + } + + actual fun writeFixed32Array( + fieldNumber: Int, + values: List, + tag: UInt + ) = writeArray(this, fieldNumber, values, tag, writeNoTag = ::writeFixed32NoTag, writeWithTag = ::writeFixed32) + + actual fun writeFixed32NoTag(value: UInt) { + impl.encoder.writeUint32(value) + } + + actual fun writeFixed64(fieldNumber: Int, value: ULong) { + impl.writeFixed64(fieldNumber, value) + } + + actual fun writeFixed64Array( + fieldNumber: Int, + values: List, + tag: UInt + ) = writeArray(this, fieldNumber, values, tag, writeNoTag = ::writeFixed64NoTag, writeWithTag = ::writeFixed64) + + actual fun writeFixed64NoTag(value: ULong) { + impl.encoder.writeUint64(value) + } + + actual fun writeFloat(fieldNumber: Int, value: Float) { + impl.writeFloat(fieldNumber, value) + } + + actual fun writeFloatArray( + fieldNumber: Int, + values: List, + tag: UInt + ) = writeArray(this, fieldNumber, values, tag, writeNoTag = ::writeFloatNoTag, writeWithTag = ::writeFloat) + + actual fun writeFloatNoTag(value: Float) { + impl.encoder.writeFloat(value) + } + + actual fun writeGroup( + fieldNumber: Int, + value: KMMessage + ): Unit = TODO() + + actual fun writeGroupArray( + fieldNumber: Int, + values: List + ): Unit = TODO() + + actual fun writeGroupNoTag( + fieldNumber: Int, + value: KMMessage + ): Unit = TODO() + + actual fun writeInt32(fieldNumber: Int, value: Int) { + impl.writeInt32(fieldNumber, value) + } + + actual fun writeInt32Array( + fieldNumber: Int, + values: List, + tag: UInt + ) = writeArray(this, fieldNumber, values, tag, writeNoTag = ::writeInt32NoTag, writeWithTag = ::writeInt32) + + actual fun writeInt32NoTag(value: Int) { + impl.encoder.writeSignedVarint32(value) + } + + actual fun writeInt64(fieldNumber: Int, value: Long) { + impl.writeInt64(fieldNumber, value) + } + + actual fun writeInt64Array( + fieldNumber: Int, + values: List, + tag: UInt + ) = writeArray(this, fieldNumber, values, tag, writeNoTag = ::writeInt64NoTag, writeWithTag = ::writeInt64) + + actual fun writeInt64NoTag(value: Long) { + impl.encoder.writeSignedVarint64(value) + } + + actual fun writeMessage( + fieldNumber: Int, + value: KMMessage + ) { + val bookmark = impl.beginDelimited(fieldNumber) + value.serialize(this) + impl.endDelimited(bookmark) + } + + actual fun writeMessageArray( + fieldNumber: Int, + values: List + ) { + values.forEach { message -> + writeMessage(fieldNumber, message) + } + } + + actual fun writeMessageNoTag(value: KMMessage) { + serializeMessage(value, this) + } + + actual fun writeMessageSetExtension( + fieldNumber: Int, + value: KMMessage + ): Unit = TODO() + + actual fun writeRawByte(value: UByte) { + impl.encoder.writeInt8(value) + } + + actual fun writeRawData(data: ByteArray) { + impl.appendUint8Array(data) + } + + actual fun writeRawLittleEndian32(value: Int) { + writeFixed32NoTag(value.toUInt()) + } + + actual fun writeRawLittleEndian64(value: Long) { + writeFixed64NoTag(value.toULong()) + } + + actual fun writeRawMessageSetExtension(fieldNumber: Int, value: ByteArray): Unit = TODO() + + actual fun writeRawVarint32(value: Int) { + impl.encoder.writeUint32(value) + } + + actual fun writeRawVarint64(value: Long) { + impl.encoder.writeUint64(value) + } + + actual fun writeRawVarintSizeTAs32(value: ULong): Unit = + throw NotImplementedError("Not supported on js") + + actual fun writeSFixed32(fieldNumber: Int, value: Int) { + impl.writeSfixed32(fieldNumber, value) + } + + actual fun writeSFixed32Array( + fieldNumber: Int, + values: List, + tag: UInt + ) = writeArray(this, fieldNumber, values, tag, writeNoTag = ::writeSFixed32NoTag, writeWithTag = ::writeSFixed32) + + actual fun writeSFixed32NoTag(value: Int) { + impl.encoder.writeSplitFixed32(value) + } + + actual fun writeSFixed64(fieldNumber: Int, value: Long) { + impl.writeSfixed64(fieldNumber, value) + } + + actual fun writeSFixed64Array( + fieldNumber: Int, + values: List, + tag: UInt + ) = writeArray(this, fieldNumber, values, tag, writeNoTag = ::writeSFixed64NoTag, writeWithTag = ::writeSFixed64) + + actual fun writeSFixed64NoTag(value: Long) { + impl.encoder.writeSplitFixed64(value) + } + + actual fun writeSInt32(fieldNumber: Int, value: Int) { + impl.writeSint32(fieldNumber, value) + } + + actual fun writeSInt32Array( + fieldNumber: Int, + values: List, + tag: UInt + ) = writeArray(this, fieldNumber, values, tag, writeNoTag = ::writeSInt32NoTag, writeWithTag = ::writeSInt32) + + actual fun writeSInt32NoTag(value: Int) { + impl.encoder.writeZigzagVarint32(value) + } + + actual fun writeSInt64(fieldNumber: Int, value: Long) { + impl.writeSint64(fieldNumber, value) + } + + actual fun writeSInt64Array( + fieldNumber: Int, + values: List, + tag: UInt + ) = writeArray(this, fieldNumber, values, tag, writeNoTag = ::writeSInt64NoTag, writeWithTag = ::writeSInt64) + + actual fun writeSInt64NoTag(value: Long) { + impl.encoder.writeZigzagVarint64(value) + } + + actual fun writeString(fieldNumber: Int, value: String) { + impl.writeString(fieldNumber, value) + } + + actual fun writeStringArray( + fieldNumber: Int, + values: List + ) { + values.forEach { value -> writeString(fieldNumber, value) } + } + + actual fun writeStringNoTag(value: String) { + impl.encoder.writeString(value) + } + + actual fun writeTag( + fieldNumber: UInt, + format: WireFormat + ) { + impl.encoder.writeUnsignedVarint32(wireFormatMakeTag(fieldNumber.toInt(), format)) + } + + actual fun writeUInt32(fieldNumber: Int, value: UInt) { + impl.writeUInt32(fieldNumber, value) + } + + actual fun writeUInt32Array( + fieldNumber: Int, + values: List, + tag: UInt + ) = writeArray(this, fieldNumber, values, tag, writeNoTag = ::writeUInt32NoTag, writeWithTag = ::writeUInt32) + + actual fun writeUInt32NoTag(value: UInt) { + impl.encoder.writeUnsignedVarint32(value) + } + + actual fun writeUInt64(fieldNumber: Int, value: ULong) { + impl.writeUInt64(fieldNumber, value) + } + + actual fun writeUInt64Array( + fieldNumber: Int, + values: List, + tag: UInt + ) = writeArray(this, fieldNumber, values, tag, writeNoTag = ::writeUInt64NoTag, writeWithTag = ::writeUInt64) + + actual fun writeUInt64NoTag(value: ULong) { + impl.encoder.writeUnsignedVarint64(value) + } +} \ No newline at end of file diff --git a/grpc-multiplatform-lib/src/jsMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/TypeUtil.kt b/grpc-multiplatform-lib/src/jsMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/TypeUtil.kt new file mode 100644 index 0000000..ae11f63 --- /dev/null +++ b/grpc-multiplatform-lib/src/jsMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/TypeUtil.kt @@ -0,0 +1,5 @@ +package io.github.timortel.kotlin_multiplatform_grpc_lib.io + +import org.khronos.webgl.Uint8Array + +fun Uint8Array.toByteArray(): ByteArray = unsafeCast() \ No newline at end of file diff --git a/grpc-multiplatform-lib/src/jsMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/map_writer.kt b/grpc-multiplatform-lib/src/jsMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/map_writer.kt new file mode 100644 index 0000000..d82d84b --- /dev/null +++ b/grpc-multiplatform-lib/src/jsMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/map_writer.kt @@ -0,0 +1,18 @@ +package io.github.timortel.kotlin_multiplatform_grpc_lib.io + +fun writeMap( + stream: CodedOutputStream, + fieldNumber: Int, + map: Map, + writeKey: CodedOutputStream.(fieldNumber: Int, K) -> Unit, + writeValue: CodedOutputStream.(fieldNumber: Int, V) -> Unit +) { + map.forEach { (key, value) -> + stream.impl.beginSubMessage(fieldNumber) + + writeKey(stream, kMapKeyFieldNumber, key) + writeValue(stream, kMapValueFieldNumber, value) + + stream.impl.endSubMessage() + } +} \ No newline at end of file diff --git a/grpc-multiplatform-lib/src/jsMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/message_writer.kt b/grpc-multiplatform-lib/src/jsMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/message_writer.kt new file mode 100644 index 0000000..f55c2ae --- /dev/null +++ b/grpc-multiplatform-lib/src/jsMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/io/message_writer.kt @@ -0,0 +1,35 @@ +package io.github.timortel.kotlin_multiplatform_grpc_lib.io + + +fun writeArray( + stream: CodedOutputStream, + fieldNumber: Int, + values: Collection, + tag: UInt, + writeNoTag: (T) -> Unit, + writeWithTag: (Int, T) -> Unit +) { + if (tag != 0u) { + if (values.isEmpty()) return + + val mark = stream.impl.beginDelimited(fieldNumber) + + values.forEach(writeNoTag) + + stream.impl.endDelimited(mark) + } else { + values.forEach { writeWithTag(fieldNumber, it) } + } +} + +fun writeArray( + tag: UInt, + writeRepeated: () -> Unit, + writePacked: () -> Unit +) { + if (tag != 0u) { + writePacked() + } else { + writeRepeated() + } +} \ No newline at end of file diff --git a/grpc-multiplatform-lib/src/iosJvmCommon/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/message/DataType.kt b/grpc-multiplatform-lib/src/jsMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/message/DataType.kt similarity index 90% rename from grpc-multiplatform-lib/src/iosJvmCommon/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/message/DataType.kt rename to grpc-multiplatform-lib/src/jsMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/message/DataType.kt index 8cb1add..6c21107 100644 --- a/grpc-multiplatform-lib/src/iosJvmCommon/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/message/DataType.kt +++ b/grpc-multiplatform-lib/src/jsMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/message/DataType.kt @@ -1,6 +1,6 @@ package io.github.timortel.kotlin_multiplatform_grpc_lib.message -expect enum class DataType { +actual enum class DataType { DOUBLE, FLOAT, INT64, diff --git a/grpc-multiplatform-lib/src/jsMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/message/JSImpl.kt b/grpc-multiplatform-lib/src/jsMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/message/JSImpl.kt deleted file mode 100644 index 2521cc5..0000000 --- a/grpc-multiplatform-lib/src/jsMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/message/JSImpl.kt +++ /dev/null @@ -1,5 +0,0 @@ -package io.github.timortel.kotlin_multiplatform_grpc_lib.message - -interface JSImpl { - fun serializeBinary(): dynamic -} \ No newline at end of file diff --git a/grpc-multiplatform-lib/src/jsMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/message/KMMessage.kt b/grpc-multiplatform-lib/src/jsMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/message/KMMessage.kt index 5a77889..4f39196 100644 --- a/grpc-multiplatform-lib/src/jsMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/message/KMMessage.kt +++ b/grpc-multiplatform-lib/src/jsMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/message/KMMessage.kt @@ -1,5 +1,25 @@ package io.github.timortel.kotlin_multiplatform_grpc_lib.message +import io.github.timortel.kotlin_multiplatform_grpc_lib.JSPB +import io.github.timortel.kotlin_multiplatform_grpc_lib.io.CodedOutputStream +import io.github.timortel.kotlin_multiplatform_grpc_lib.io.toByteArray + actual interface KMMessage { - val jsImpl: JSImpl -} \ No newline at end of file + + /** + * Serializes this message and returns it as a [ByteArray]. + */ + fun serialize(): ByteArray { + val stream = JSPB.BinaryWriter() + serialize(CodedOutputStream(stream)) + + return stream.getResultBuffer().toByteArray() + } + + /** + * Serializes this message and writes it directly to the [CodedOutputStream]. + */ + fun serialize(stream: CodedOutputStream) +} + +val serializeMessage: (KMMessage, CodedOutputStream) -> Unit = { message, stream -> message.serialize(stream) } diff --git a/grpc-multiplatform-lib/src/jsMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/message/MessageDeserializer.kt b/grpc-multiplatform-lib/src/jsMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/message/MessageDeserializer.kt deleted file mode 100644 index 59770f9..0000000 --- a/grpc-multiplatform-lib/src/jsMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/message/MessageDeserializer.kt +++ /dev/null @@ -1,5 +0,0 @@ -package io.github.timortel.kotlin_multiplatform_grpc_lib.message - -interface MessageDeserializer { - fun deserializeBinary(bytes: dynamic): T -} \ No newline at end of file diff --git a/grpc-multiplatform-lib/src/jsMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/rpc/rpc_implementation.kt b/grpc-multiplatform-lib/src/jsMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/rpc/rpc_implementation.kt index 0b2d6c3..4b02922 100644 --- a/grpc-multiplatform-lib/src/jsMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/rpc/rpc_implementation.kt +++ b/grpc-multiplatform-lib/src/jsMain/kotlin/io/github/timortel/kotlin_multiplatform_grpc_lib/rpc/rpc_implementation.kt @@ -43,12 +43,18 @@ fun serverSideStreamingCallImplementation(performCall: () -> dynam stream.on("end") { close() + Unit } stream.on("status") { status -> //If the status is not ok, we throw an error if (status.code as Int != 0) { - close(KMStatusException(KMStatus(KMCode.getCodeForValue(status.code as Int), status.details as String), null)) + close( + KMStatusException( + KMStatus(KMCode.getCodeForValue(status.code as Int), status.details as String), + null + ) + ) } } @@ -60,4 +66,6 @@ fun serverSideStreamingCallImplementation(performCall: () -> dynam stream.cancel() as Unit } } -} \ No newline at end of file + // Catch a very weird bug that occurs when calling close() + .catch { if (it !is ClassCastException) throw it } +} diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts index e21afbf..843e8d3 100644 --- a/plugin/build.gradle.kts +++ b/plugin/build.gradle.kts @@ -1,26 +1,23 @@ plugins { - kotlin("jvm") version "1.7.10" + kotlin("jvm") version libs.versions.kotlin.get() id("java-gradle-plugin") id("maven-publish") - id("com.gradle.plugin-publish") version "0.18.0" + id("com.gradle.plugin-publish") version libs.versions.gradlePluginPublish.get() antlr } group = "io.github.timortel" -version = "0.3.0" +version = libs.versions.grpcKotlinMultiplatform.get() java { withSourcesJar() withJavadocJar() } -pluginBundle { +gradlePlugin { website = "https://github.com/TimOrtel/GRPC-Kotlin-Multiplatform" vcsUrl = "https://github.com/TimOrtel/GRPC-Kotlin-Multiplatform.git" - tags = listOf("grpc", "protobuf", "kotlin-multiplatform", "kotlin", "multiplatform") -} -gradlePlugin { plugins { create("kotlin-multiplatform-grpc-plugin") { id = "io.github.timortel.kotlin-multiplatform-grpc-plugin" @@ -53,12 +50,12 @@ repositories { } dependencies { - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4") - antlr("org.antlr:antlr4:4.11.1") - implementation("org.antlr:antlr4:4.10.1") + implementation(libs.kotlinx.coroutines.core) + antlr(libs.antlr) + implementation(libs.antlr) - implementation("com.squareup:kotlinpoet:1.12.0") - compileOnly("org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.0") + implementation(libs.squareup.kotlinpoet) + compileOnly(libs.kotlin.gradle.plugin) } tasks.generateGrammarSource { @@ -71,4 +68,8 @@ tasks.withType().all { kotlinOptions { freeCompilerArgs = listOf("-Xopt-in=kotlin.RequiresOptIn", "-Xopt-in=kotlin.ExperimentalStdlibApi") } +} + +tasks.withType().all { + dependsOn("generateGrammarSource") } \ No newline at end of file diff --git a/plugin/settings.gradle.kts b/plugin/settings.gradle.kts new file mode 100644 index 0000000..fa8bc74 --- /dev/null +++ b/plugin/settings.gradle.kts @@ -0,0 +1,7 @@ +dependencyResolutionManagement { + versionCatalogs { + create("libs") { + from(files("../gradle/libs.versions.toml")) + } + } +} \ No newline at end of file diff --git a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/GrpcMultiplatformPlugin.kt b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/GrpcMultiplatformPlugin.kt index 47b9205..6e91c0e 100644 --- a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/GrpcMultiplatformPlugin.kt +++ b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/GrpcMultiplatformPlugin.kt @@ -3,22 +3,10 @@ package io.github.timortel.kotlin_multiplatform_grpc_plugin import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.GenerateMultiplatformSourcesTask import org.gradle.api.Plugin import org.gradle.api.Project -import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension -import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation import org.jetbrains.kotlin.gradle.plugin.KotlinMultiplatformPluginWrapper -import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType -import org.jetbrains.kotlin.gradle.plugin.KotlinTarget -import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget -import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinIosArm32Variant -import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinIosArm64Variant -import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinIosSimulatorArm64Variant -import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinIosX64Variant -import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrTarget -import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget import org.jetbrains.kotlin.gradle.tasks.Kotlin2JsCompile import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import org.jetbrains.kotlin.gradle.tasks.KotlinNativeCompile -import org.jetbrains.kotlin.gradle.utils.`is` class GrpcMultiplatformPlugin : Plugin { override fun apply(project: Project) { @@ -28,7 +16,6 @@ class GrpcMultiplatformPlugin : Plugin { project.tasks.register("generateMPProtos", GenerateMultiplatformSourcesTask::class.java) project.plugins.withType(KotlinMultiplatformPluginWrapper::class.java) { - project.afterEvaluate { val generateMpProtosTask = project.tasks.withType(GenerateMultiplatformSourcesTask::class.java) diff --git a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/Const.kt b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/Const.kt index 89585c7..17e3294 100644 --- a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/Const.kt +++ b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/Const.kt @@ -241,16 +241,14 @@ object Const { } object Companion { - object IOS { - object DataDeserializationFunction { - const val NAME = "deserialize" - const val DATA_PARAM = "data" - } + object DataDeserializationFunction { + const val NAME = "deserialize" + const val DATA_PARAM = "data" + } - object WrapperDeserializationFunction { - const val NAME = "deserialize" - const val STREAM_PARAM = "stream" - } + object WrapperDeserializationFunction { + const val NAME = "deserializeFromWrapper" + const val STREAM_PARAM = "stream" } } } diff --git a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/common/CommonFunctionGenerator.kt b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/common/CommonFunctionGenerator.kt deleted file mode 100644 index 0b94144..0000000 --- a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/common/CommonFunctionGenerator.kt +++ /dev/null @@ -1,45 +0,0 @@ -package io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.common - -import com.squareup.kotlinpoet.CodeBlock -import com.squareup.kotlinpoet.FunSpec -import com.squareup.kotlinpoet.TypeName -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.ProtoMessage -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.Const - -abstract class CommonFunctionGenerator { - - /** - * Recursively adds all common getters. Common getters transform the native values to common values. - */ - fun generateCommonGetter(messages: List) { - messages.forEach { message -> - //Common property, that converts this JS type to a common type - addFunction( - FunSpec - .builder(Const.Message.CommonFunction.NAME) - .addParameter( - Const.Message.CommonFunction.PARAMETER_NATIVE, - getNativePlatformType(message) - ) - .addCode(getGetter(message)) - .returns(message.commonType) - .build() - ) - - generateCommonGetter(message.children) - } - } - - private fun getGetter(message: ProtoMessage): CodeBlock = - CodeBlock.builder() - .add("return·%T(%N)", message.commonType, Const.Message.CommonFunction.PARAMETER_NATIVE) - .build() - - protected abstract fun addFunction(funSpec: FunSpec) - - /** - * @return the type of the message for the native type, so for JVM returns message.jvmType - */ - protected abstract fun getNativePlatformType(message: ProtoMessage): TypeName - -} \ No newline at end of file diff --git a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/common/JsCommonFunctionGenerator.kt b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/common/JsCommonFunctionGenerator.kt deleted file mode 100644 index a18923d..0000000 --- a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/common/JsCommonFunctionGenerator.kt +++ /dev/null @@ -1,14 +0,0 @@ -package io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.common - -import com.squareup.kotlinpoet.* -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.ProtoMessage - -class JsCommonFunctionGenerator(private val builder: FileSpec.Builder) : CommonFunctionGenerator() { - - override fun addFunction(funSpec: FunSpec) { - builder.addFunction(funSpec) - } - - override fun getNativePlatformType(message: ProtoMessage): TypeName = message.jsType - -} \ No newline at end of file diff --git a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/common/JvmCommonFunctionGenerator.kt b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/common/JvmCommonFunctionGenerator.kt deleted file mode 100644 index cbf4024..0000000 --- a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/common/JvmCommonFunctionGenerator.kt +++ /dev/null @@ -1,14 +0,0 @@ -package io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.common - -import com.squareup.kotlinpoet.* -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.ProtoMessage - -class JvmCommonFunctionGenerator(private val builder: FileSpec.Builder) : CommonFunctionGenerator() { - - override fun addFunction(funSpec: FunSpec) { - builder.addFunction(funSpec) - } - - override fun getNativePlatformType(message: ProtoMessage): TypeName = message.jvmType - -} \ No newline at end of file diff --git a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/dsl/IosJvmDslBuilder.kt b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/dsl/ActualDslBuilder.kt similarity index 89% rename from plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/dsl/IosJvmDslBuilder.kt rename to plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/dsl/ActualDslBuilder.kt index 82ba211..a2533f9 100644 --- a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/dsl/IosJvmDslBuilder.kt +++ b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/dsl/ActualDslBuilder.kt @@ -4,11 +4,14 @@ import com.squareup.kotlinpoet.FunSpec import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.ProtoMessage import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.Const -object IosJvmDslBuilder : DslBuilder(true) { +/** + * For JVM, JS and iOS + */ +object ActualDslBuilder : DslBuilder(true) { override fun modifyBuildFunction(builder: FunSpec.Builder, message: ProtoMessage) { builder.apply { - addCode("return %T(", message.iosType) + addCode("return %T(", message.commonType) message.attributes.filter { !it.isOneOfAttribute }.forEach { attr -> val propertyName = Const.Message.Attribute.propertyName(message, attr) diff --git a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/dsl/CommonDslBuilder.kt b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/dsl/CommonDslBuilder.kt index 9a516af..77898c5 100644 --- a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/dsl/CommonDslBuilder.kt +++ b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/dsl/CommonDslBuilder.kt @@ -2,8 +2,6 @@ package io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatfo import com.squareup.kotlinpoet.FunSpec import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.ProtoMessage -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.ProtoMessageAttribute -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.map.mapper.MapMapper object CommonDslBuilder : DslBuilder(false) { diff --git a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/dsl/JsDslBuilder.kt b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/dsl/JsDslBuilder.kt deleted file mode 100644 index 18311b6..0000000 --- a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/dsl/JsDslBuilder.kt +++ /dev/null @@ -1,105 +0,0 @@ -package io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.dsl - -import com.squareup.kotlinpoet.CodeBlock -import com.squareup.kotlinpoet.FunSpec -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.Const -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.map.mapper.CommonToJsMapMapper -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.map.mapper.MapMapper -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.ProtoMessage -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.ProtoMessageAttribute - -object JsDslBuilder : SubDslBuilder(true) { - - override val mapMapper: MapMapper = CommonToJsMapMapper - - override fun initializeBuilder(builder: FunSpec.Builder, message: ProtoMessage): String { - builder.addStatement("val jsImpl = %T()", message.jsType) - return "jsImpl" - } - - override fun returnPlatformType(builder: FunSpec.Builder, message: ProtoMessage, builderVariableName: String) { - builder.addStatement("return %T(jsImpl)", message.commonType) - } - - override fun setEnumValue( - builder: FunSpec.Builder, - variableName: String, - message: ProtoMessage, - attribute: ProtoMessageAttribute, - builderVariable: String - ) { - val f = Const.Message.Attribute.Scalar.JS.setFunction(message, attribute).simpleName - val v = CodeBlock.of("%N?.value ?: 0", variableName) - - builder.addCode("%N.%N(", builderVariable, f) - builder.addCode(v) - builder.addCode(")\n") - } - - override fun setScalarValue( - builder: FunSpec.Builder, - variableName: String, - message: ProtoMessage, - attribute: ProtoMessageAttribute, - builderVariable: String - ) { - val f = Const.Message.Attribute.Scalar.JS.setFunction(message, attribute).simpleName - val v = if (attribute.types.doDiffer) { - CodeBlock.of( - "%N.%N", - variableName, - Const.Message.Constructor.JS.PARAM_IMPL - ) - } else { - CodeBlock.of("%N", variableName) - } - - builder.addCode("%N.%N(", builderVariable, f) - builder.addCode(v) - builder.addCode(")\n") - } - - override fun addAllValues( - builder: FunSpec.Builder, - message: ProtoMessage, - attribute: ProtoMessageAttribute, - builderVariable: String - ) { - builder.addCode( - "%N.%N(", - builderVariable, - Const.Message.Attribute.Repeated.JS.setListFunctionName(attribute) - ) - - if (attribute.types.doDiffer) { - builder.addCode( - "%N.map·{ it.%N }.toTypedArray()", - Const.DSL.Attribute.Repeated.propertyName(attribute), - Const.Message.Constructor.JS.PARAM_IMPL - ) - } else { - builder.addCode("%N.toTypedArray()", Const.DSL.Attribute.Repeated.propertyName(attribute)) - } - - builder.addCode(")\n") - } - - override fun addAllEnumValues( - builder: FunSpec.Builder, - message: ProtoMessage, - attribute: ProtoMessageAttribute, - builderVariable: String - ) { - builder.addCode( - "%N.%N(", - builderVariable, - Const.Message.Attribute.Repeated.JS.setListFunctionName(attribute) - ) - - builder.addCode( - "%N.map·{ it.%N }.toTypedArray())\n", - Const.DSL.Attribute.Repeated.propertyName(attribute), - "value" - ) - } -} \ No newline at end of file diff --git a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/dsl/SubDslBuilder.kt b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/dsl/SubDslBuilder.kt deleted file mode 100644 index 633cb43..0000000 --- a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/dsl/SubDslBuilder.kt +++ /dev/null @@ -1,171 +0,0 @@ -package io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.dsl - -import com.squareup.kotlinpoet.* -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.ProtoType -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.* -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.Const -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.map.mapper.MapMapper - -abstract class SubDslBuilder(isActual: Boolean) : DslBuilder(isActual) { - - protected abstract val mapMapper: MapMapper - - override fun modifyBuildFunction(builder: FunSpec.Builder, message: ProtoMessage) { - val buildParam = initializeBuilder(builder, message) - - builder.apply { - message.attributes.filter { !it.isOneOfAttribute }.forEach { attr -> - when (attr.attributeType) { - is Scalar -> { - val attrName = Const.DSL.Attribute.Scalar.propertyName(attr) - addStatement( - "val %N = %N", - attrName, - attrName - ) - beginControlFlow("if (%N != null)", attrName) - - if (attr.types.isEnum) { - setEnumValue(builder, attrName, message, attr, buildParam) - } else { - setScalarValue(builder, attrName, message, attr, buildParam) - } - - endControlFlow() - } - - is Repeated -> { - if (attr.types.isEnum) { - addAllEnumValues(builder, message, attr, buildParam) - } else { - addAllValues(builder, message, attr, buildParam) - } - } - - is MapType -> { - addCode( - mapMapper.mapMap( - buildParam, - CodeBlock.of(Const.DSL.Attribute.Map.propertyName(attr)), - message, - attr, - attr.attributeType - ) - ) - } - } - } - - message.oneOfs.forEach { oneOf -> - beginControlFlow( - "when·(val %N = %N)·{", - Const.DSL.OneOf.propertyName(message, oneOf), - Const.DSL.OneOf.propertyName(message, oneOf) - ) - - oneOf.attributes.forEach { attr -> - beginControlFlow( - "is·%T·->·{", - Const.Message.OneOf.childClassName(message, oneOf, attr) - ) - - //get wrapper property - addStatement( - "val %N = %N.%N", - Const.Message.Attribute.propertyName(message, attr), - Const.DSL.OneOf.propertyName(message, oneOf), - Const.Message.Attribute.propertyName(message, attr) - ) - - if (attr.types.protoType == ProtoType.ENUM) { - setEnumValue( - builder, - Const.Message.Attribute.propertyName(message, attr), - message, - attr, - buildParam - ) - } else { - setScalarValue( - builder, - Const.Message.Attribute.propertyName(message, attr), - message, - attr, - buildParam - ) - } - - endControlFlow() - } - - //When must be exhaustive - addStatement( - "%T, %T·->·{}", - Const.Message.OneOf.notSetClassName(message, oneOf), - Const.Message.OneOf.unknownClassName(message, oneOf) - ) - - endControlFlow() - } - } - - returnPlatformType(builder, message, buildParam) - } - - /** - * Initialize the platform dependent builder - * @return the name of the builder variable - */ - protected abstract fun initializeBuilder(builder: FunSpec.Builder, message: ProtoMessage): String - - /** - * Add the code that returns the final platform dependent KM type - */ - protected abstract fun returnPlatformType( - builder: FunSpec.Builder, - message: ProtoMessage, - builderVariableName: String - ) - - /** - * Add code to set the enum value stored in variableName. - */ - protected abstract fun setEnumValue( - builder: FunSpec.Builder, - variableName: String, - message: ProtoMessage, - attribute: ProtoMessageAttribute, - builderVariable: String - ) - - /** - * Add code to set the non-enum scalar value stored in variable name - */ - protected abstract fun setScalarValue( - builder: FunSpec.Builder, - variableName: String, - message: ProtoMessage, - attribute: ProtoMessageAttribute, - builderVariable: String - ) - - /** - * Add all non-enums to the list in the builder - */ - protected abstract fun addAllValues( - builder: FunSpec.Builder, - message: ProtoMessage, - attribute: ProtoMessageAttribute, - builderVariable: String - ) - - /** - * Add all enums to the list in the builder - */ - protected abstract fun addAllEnumValues( - builder: FunSpec.Builder, - message: ProtoMessage, - attribute: ProtoMessageAttribute, - builderVariable: String - ) -} \ No newline at end of file diff --git a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/map/IosJvmMapMessageMethodGenerator.kt b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/map/ActualMapMessageMethodGenerator.kt similarity index 89% rename from plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/map/IosJvmMapMessageMethodGenerator.kt rename to plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/map/ActualMapMessageMethodGenerator.kt index 3d1e208..76cb3f7 100644 --- a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/map/IosJvmMapMessageMethodGenerator.kt +++ b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/map/ActualMapMessageMethodGenerator.kt @@ -6,7 +6,10 @@ import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatfor import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.ProtoMessageAttribute import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.Const -object IosJvmMapMessageMethodGenerator : MapMessageMethodGenerator(true) { +/** + * For JVM, JS and iOS + */ +object ActualMapMessageMethodGenerator : MapMessageMethodGenerator() { override val modifiers: List = listOf(KModifier.ACTUAL) override fun modifyMapProperty( diff --git a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/map/CommonMapMessageMethodGenerator.kt b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/map/CommonMapMessageMethodGenerator.kt index 257c6b4..9aade9f 100644 --- a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/map/CommonMapMessageMethodGenerator.kt +++ b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/map/CommonMapMessageMethodGenerator.kt @@ -5,7 +5,7 @@ import com.squareup.kotlinpoet.PropertySpec import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.ProtoMessage import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.ProtoMessageAttribute -object CommonMapMessageMethodGenerator : MapMessageMethodGenerator(false) { +object CommonMapMessageMethodGenerator : MapMessageMethodGenerator() { override val modifiers: List = listOf(KModifier.EXPECT) diff --git a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/map/JsMapMessageMethodGenerator.kt b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/map/JsMapMessageMethodGenerator.kt deleted file mode 100644 index 63c0bab..0000000 --- a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/map/JsMapMessageMethodGenerator.kt +++ /dev/null @@ -1,44 +0,0 @@ -package io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.map - -import com.squareup.kotlinpoet.ClassName -import com.squareup.kotlinpoet.CodeBlock -import com.squareup.kotlinpoet.KModifier -import com.squareup.kotlinpoet.PropertySpec -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.MapType -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.ProtoMessage -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.ProtoMessageAttribute -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.Const -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.map.mapper.JsToCommonMapMapper - -object JsMapMessageMethodGenerator : MapMessageMethodGenerator(true) { - - override val modifiers: List = listOf(KModifier.ACTUAL) - - override fun modifyMapProperty( - builder: PropertySpec.Builder, - protoMessage: ProtoMessage, - messageAttribute: ProtoMessageAttribute - ) { - val mapType = messageAttribute.attributeType as MapType - - val mapVariable = CodeBlock.of( - "%T<%T, %T>(%N.%N(false))", - ClassName("io.github.timortel.kotlin_multiplatform_grpc_lib", "JSPBMap"), - mapType.keyTypes.jsType, - mapType.valueTypes.jsType, - Const.Message.Constructor.JS.PARAM_IMPL, - Const.Message.Attribute.Map.JS.getMapFunctionName(messageAttribute) - ) - - if (mapType.valueTypes.doDiffer || mapType.keyTypes.doDiffer) { - val initializer = CodeBlock.builder() - .add("lazy·{\n") - .add(JsToCommonMapMapper.mapMap("newMap", mapVariable, protoMessage, messageAttribute, mapType)) - .add("\n}\n") - - builder.delegate(initializer.build()) - } else { - builder.initializer(mapVariable) - } - } -} \ No newline at end of file diff --git a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/map/MapMessageMethodGenerator.kt b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/map/MapMessageMethodGenerator.kt index 719dc22..52a8448 100644 --- a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/map/MapMessageMethodGenerator.kt +++ b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/map/MapMessageMethodGenerator.kt @@ -10,7 +10,7 @@ import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatfor import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.ProtoMessageAttribute import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.Const -abstract class MapMessageMethodGenerator(private val isActual: Boolean) { +abstract class MapMessageMethodGenerator { private companion object { private val mapType = Map::class.asTypeName() diff --git a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/map/mapper/CommonToJsMapMapper.kt b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/map/mapper/CommonToJsMapMapper.kt deleted file mode 100644 index e0ac027..0000000 --- a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/map/mapper/CommonToJsMapMapper.kt +++ /dev/null @@ -1,45 +0,0 @@ -package io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.map.mapper - -import com.squareup.kotlinpoet.CodeBlock -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.Types -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.MapType -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.ProtoMessage -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.ProtoMessageAttribute -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.Const -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.objPropertyName - -object CommonToJsMapMapper : MapMapper() { - - override fun handleCreatedMap( - builderVariable: String, - mapToPutVariable: CodeBlock, - message: ProtoMessage, - attribute: ProtoMessageAttribute, - mapType: MapType - ): CodeBlock { - val internalMapVariableName = attribute.name - - return CodeBlock - .builder() - .addStatement( - "val %N = %N.%N(false)", - internalMapVariableName, - builderVariable, - Const.Message.Attribute.Map.JS.getMapFunctionName(attribute) - ) - .add(mapToPutVariable) - .add(".forEach·{ (k, v) -> %N.set(k, v) }\n", internalMapVariableName) - .build() - } - - override fun mapVariable( - variableName: String, - message: ProtoMessage, - attribute: ProtoMessageAttribute, - types: Types - ): CodeBlock { - return if (types.isEnum) { - CodeBlock.of("%N.value", variableName) - } else CodeBlock.of("%N.%N.%N", variableName, Const.Message.Constructor.JS.PARAM_IMPL, objPropertyName) - } -} \ No newline at end of file diff --git a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/map/mapper/CommonToJvmMapMapper.kt b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/map/mapper/CommonToJvmMapMapper.kt deleted file mode 100644 index db1e445..0000000 --- a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/map/mapper/CommonToJvmMapMapper.kt +++ /dev/null @@ -1,43 +0,0 @@ -package io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.map.mapper - -import com.squareup.kotlinpoet.CodeBlock -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.Types -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.MapType -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.ProtoMessage -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.ProtoMessageAttribute -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.Const - -object CommonToJvmMapMapper : MapMapper() { - - override fun handleCreatedMap( - builderVariable: String, - mapToPutVariable: CodeBlock, - message: ProtoMessage, - attribute: ProtoMessageAttribute, - mapType: MapType - ): CodeBlock { - return CodeBlock - .builder() - .add( - "%N.%N(", - builderVariable, - Const.Message.Attribute.Map.JVM.putAllFunctionName(attribute) - ) - .add(mapToPutVariable) - .add(")\n") - .build() - } - - override fun mapVariable( - variableName: String, - message: ProtoMessage, - attribute: ProtoMessageAttribute, - types: Types - ): CodeBlock { - return if (types.isEnum) { - CodeBlock.of("%T.forNumber(%N.value)", types.jvmType, variableName) - } else { - CodeBlock.of("%N.%N", variableName, Const.Message.Constructor.JVM.PARAM_IMPL) - } - } -} \ No newline at end of file diff --git a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/map/mapper/JsToCommonMapMapper.kt b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/map/mapper/JsToCommonMapMapper.kt deleted file mode 100644 index be952d3..0000000 --- a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/map/mapper/JsToCommonMapMapper.kt +++ /dev/null @@ -1,41 +0,0 @@ -package io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.map.mapper - -import com.squareup.kotlinpoet.CodeBlock -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.Types -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.MapType -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.ProtoMessage -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.ProtoMessageAttribute -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.Const - -object JsToCommonMapMapper : MapMapper() { - - override fun handleCreatedMap( - builderVariable: String, - mapToPutVariable: CodeBlock, - message: ProtoMessage, - attribute: ProtoMessageAttribute, - mapType: MapType - ): CodeBlock { - return CodeBlock.builder() - .add(mapToPutVariable) - .build() - } - - override fun mapVariable( - variableName: String, - message: ProtoMessage, - attribute: ProtoMessageAttribute, - types: Types - ): CodeBlock { - return if (types.isEnum) { - CodeBlock.of( - "%T.%N(%N)", - types.commonType, - Const.Enum.getEnumForNumFunctionName, - variableName - ) - } else { - CodeBlock.of("%T(%T(%N))", types.commonType, types.jsType, variableName) - } - } -} \ No newline at end of file diff --git a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/map/mapper/JvmToCommonMapMapper.kt b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/map/mapper/JvmToCommonMapMapper.kt deleted file mode 100644 index 70534b8..0000000 --- a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/map/mapper/JvmToCommonMapMapper.kt +++ /dev/null @@ -1,41 +0,0 @@ -package io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.map.mapper - -import com.squareup.kotlinpoet.CodeBlock -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.Types -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.MapType -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.ProtoMessage -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.ProtoMessageAttribute -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.Const - -object JvmToCommonMapMapper : MapMapper() { - - override fun handleCreatedMap( - builderVariable: String, - mapToPutVariable: CodeBlock, - message: ProtoMessage, - attribute: ProtoMessageAttribute, - mapType: MapType - ): CodeBlock { - return CodeBlock.builder() - .add(mapToPutVariable) - .build() - } - - override fun mapVariable( - variableName: String, - message: ProtoMessage, - attribute: ProtoMessageAttribute, - types: Types - ): CodeBlock { - return if (types.isEnum) { - CodeBlock.of( - "%T.%N(%N.number)", - types.commonType, - Const.Enum.getEnumForNumFunctionName, - variableName - ) - } else { - CodeBlock.of("%M(%N)", Const.Message.CommonFunction.JVM.commonFunction(types.jvmType), variableName) - } - } -} \ No newline at end of file diff --git a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/map/mapper/MapMapper.kt b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/map/mapper/MapMapper.kt deleted file mode 100644 index cebafcf..0000000 --- a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/map/mapper/MapMapper.kt +++ /dev/null @@ -1,90 +0,0 @@ -package io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.map.mapper - -import com.squareup.kotlinpoet.CodeBlock -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.Types -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.MapType -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.ProtoMessage -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.ProtoMessageAttribute - -/** - * Can map a map from common to native and backwards - */ -abstract class MapMapper { - - fun mapMap( - builderVariable: String, - mapVariable: CodeBlock, - message: ProtoMessage, - attribute: ProtoMessageAttribute, - mapType: MapType - ): CodeBlock { - val builder = CodeBlock.builder() - - //Here we have to handle the case where we can directly put the map in or where we have to modify the map - if (!mapType.keyTypes.doDiffer && !mapType.valueTypes.doDiffer) { - //Case where nothing differs, and we can directly put in the map - builder.add( - handleCreatedMap( - builderVariable, - mapVariable, - message, - attribute, - mapType - ) - ) - } else { - builder.beginControlFlow("run") - //Something differs, we have to copy and map the map - builder.add("val newMap = ") - builder.add(mapVariable) - builder.add(".entries.associate·{·(k, v) ->\n") - - if (mapType.keyTypes.doDiffer) { - builder.add(mapVariable("k", message, attribute, mapType.keyTypes)) - } else { - builder.add("k") - } - - builder.add(" to ") - - if (mapType.valueTypes.doDiffer) { - builder.add(mapVariable("v", message, attribute, mapType.valueTypes)) - } else { - builder.add("v") - } - - builder.add("\n") - - builder.add("}\n") - - builder.add(handleCreatedMap(builderVariable, CodeBlock.of("newMap"), message, attribute, mapType)) - builder.endControlFlow() - } - - return builder.build() - } - - /** - * Generate the code that puts the map into the builder map - * - * @param mapToPutVariable the variable of type map that is put int - */ - protected abstract fun handleCreatedMap( - builderVariable: String, - mapToPutVariable: CodeBlock, - message: ProtoMessage, - attribute: ProtoMessageAttribute, - mapType: MapType - ): CodeBlock - - /** - * @return a code block that transforms the variable into the correct type - */ - protected abstract fun mapVariable( - variableName: String, - message: ProtoMessage, - attribute: ProtoMessageAttribute, - types: Types - ): CodeBlock - -} \ No newline at end of file diff --git a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/oneof/ActualOneOfMethodAndClassGenerator.kt b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/oneof/ActualOneOfMethodAndClassGenerator.kt new file mode 100644 index 0000000..80cc1fb --- /dev/null +++ b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/oneof/ActualOneOfMethodAndClassGenerator.kt @@ -0,0 +1,66 @@ +package io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.oneof + +import com.squareup.kotlinpoet.FunSpec +import com.squareup.kotlinpoet.KModifier +import com.squareup.kotlinpoet.PropertySpec +import com.squareup.kotlinpoet.TypeSpec +import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.CodedOutputStream +import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.ProtoMessage +import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.ProtoOneOf +import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.Const +import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.proto_file.ActualProtoFileWriter + +abstract class ActualOneOfMethodAndClassGenerator : OneOfMethodAndClassGenerator(true) { + + override val attrs: List = listOf(KModifier.ACTUAL) + + override fun modifyOneOfProperty(builder: PropertySpec.Builder, message: ProtoMessage, oneOf: ProtoOneOf) { + builder.initializer(Const.Message.OneOf.propertyName(message, oneOf)) + } + + override fun modifyParentClass(builder: TypeSpec.Builder, message: ProtoMessage, oneOf: ProtoOneOf) { + addSerializeFunction( + builder, + listOf(KModifier.ABSTRACT) + ) { + + } + } + + override fun modifyChildClass( + builder: TypeSpec.Builder, + message: ProtoMessage, + oneOf: ProtoOneOf, + childClassType: ChildClassType + ) { + addSerializeFunction(builder, listOf(KModifier.OVERRIDE)) { + when (childClassType) { + is ChildClassType.Normal -> addCode( + ActualProtoFileWriter.getWriteScalarFieldCode( + message, + childClassType.attr, + Const.Message.OneOf.IosJvm.SERIALIZE_FUNCTION_STREAM_PARAM_NAME, + performIsMessageSetCheck = false + ) + ) + ChildClassType.Unknown, ChildClassType.NotSet -> { + } + } + } + } + + private fun addSerializeFunction( + builder: TypeSpec.Builder, + modifiers: List, + modify: FunSpec.Builder.() -> Unit + ) { + builder.addFunction( + FunSpec + .builder(Const.Message.OneOf.IosJvm.SERIALIZE_FUNCTION_NAME) + .addModifiers(modifiers) + .addParameter(Const.Message.OneOf.IosJvm.SERIALIZE_FUNCTION_STREAM_PARAM_NAME, CodedOutputStream) + .apply(modify) + .build() + ) + } +} \ No newline at end of file diff --git a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/oneof/CommonOneOfMethodAndClassGenerator.kt b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/oneof/CommonOneOfMethodAndClassGenerator.kt index d809661..6fbc14a 100644 --- a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/oneof/CommonOneOfMethodAndClassGenerator.kt +++ b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/oneof/CommonOneOfMethodAndClassGenerator.kt @@ -1,6 +1,5 @@ package io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.oneof -import com.squareup.kotlinpoet.ClassName import com.squareup.kotlinpoet.KModifier import com.squareup.kotlinpoet.PropertySpec import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.ProtoMessage diff --git a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/oneof/IosJvmOneOfMethodAndClassGenerator.kt b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/oneof/IosJvmOneOfMethodAndClassGenerator.kt index bbf77cf..fe27f22 100644 --- a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/oneof/IosJvmOneOfMethodAndClassGenerator.kt +++ b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/oneof/IosJvmOneOfMethodAndClassGenerator.kt @@ -5,20 +5,20 @@ import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatfor import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.ProtoMessage import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.ProtoOneOf import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.Const +import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.proto_file.ActualProtoFileWriter import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.proto_file.IosJvmProtoFileWriteBase +import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.proto_file.ProtoFileWriter -object IosJvmOneOfMethodAndClassGenerator : OneOfMethodAndClassGenerator(true) { - override val attrs: List = listOf(KModifier.ACTUAL) +object IosJvmOneOfMethodAndClassGenerator : ActualOneOfMethodAndClassGenerator() { - override fun modifyOneOfProperty(builder: PropertySpec.Builder, message: ProtoMessage, oneOf: ProtoOneOf) { - builder.initializer(Const.Message.OneOf.propertyName(message, oneOf)) - } + override fun modifyParentClass( + builder: TypeSpec.Builder, + message: ProtoMessage, + oneOf: ProtoOneOf + ) { + super.modifyParentClass(builder, message, oneOf) - override fun modifyParentClass(builder: TypeSpec.Builder, message: ProtoMessage, oneOf: ProtoOneOf) { builder.addProperty(Const.Message.OneOf.IosJvm.REQUIRED_SIZE_PROPERTY_NAME, INT, KModifier.ABSTRACT) - addSerializeFunction(builder, listOf(KModifier.ABSTRACT)) { - - } } override fun modifyChildClass( @@ -27,6 +27,8 @@ object IosJvmOneOfMethodAndClassGenerator : OneOfMethodAndClassGenerator(true) { oneOf: ProtoOneOf, childClassType: ChildClassType ) { + super.modifyChildClass(builder, message, oneOf, childClassType) + builder.addProperty( PropertySpec .builder( @@ -49,36 +51,5 @@ object IosJvmOneOfMethodAndClassGenerator : OneOfMethodAndClassGenerator(true) { ) .build() ) - - addSerializeFunction(builder, listOf(KModifier.OVERRIDE)) { - when (childClassType) { - is ChildClassType.Normal -> addCode( - IosJvmProtoFileWriteBase.getWriteScalarFieldCode( - message, - childClassType.attr, - Const.Message.OneOf.IosJvm.SERIALIZE_FUNCTION_STREAM_PARAM_NAME, - performIsMessageSetCheck = false - ) - ) - ChildClassType.Unknown, ChildClassType.NotSet -> { - } - } - - } - } - - private fun addSerializeFunction( - builder: TypeSpec.Builder, - modifiers: List, - modify: FunSpec.Builder.() -> Unit - ) { - builder.addFunction( - FunSpec - .builder(Const.Message.OneOf.IosJvm.SERIALIZE_FUNCTION_NAME) - .addModifiers(modifiers) - .addParameter(Const.Message.OneOf.IosJvm.SERIALIZE_FUNCTION_STREAM_PARAM_NAME, CodedOutputStream) - .apply(modify) - .build() - ) } } \ No newline at end of file diff --git a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/oneof/JsOneOfMethodAndClassGenerator.kt b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/oneof/JsOneOfMethodAndClassGenerator.kt index c35e935..7cad762 100644 --- a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/oneof/JsOneOfMethodAndClassGenerator.kt +++ b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/oneof/JsOneOfMethodAndClassGenerator.kt @@ -1,55 +1,3 @@ package io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.oneof -import com.squareup.kotlinpoet.ClassName -import com.squareup.kotlinpoet.FunSpec -import com.squareup.kotlinpoet.KModifier -import com.squareup.kotlinpoet.PropertySpec -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.ProtoType -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.ProtoMessage -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.ProtoOneOf -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.Const - -object JsOneOfMethodAndClassGenerator : OneOfMethodAndClassGenerator(true) { - - override val attrs: List = listOf(KModifier.ACTUAL) - - override fun modifyOneOfProperty( - builder: PropertySpec.Builder, - message: ProtoMessage, - oneOf: ProtoOneOf - ) { - builder.getter( - FunSpec - .getterBuilder() - .apply { - addCode("return when(jsImpl.%N()) {\n", Const.Message.OneOf.JS.getCaseFunctionName(oneOf)) - oneOf.attributes.forEach { attr -> - addCode( - "%L -> %T(", attr.protoId, - Const.Message.OneOf.childClassName(message, oneOf, attr) - ) - - if (attr.types.protoType == ProtoType.MESSAGE) { - addCode("%M(", Const.Message.CommonFunction.JS.commonFunction(attr)) - } - - addCode( - "%N.%N()", - Const.Message.Constructor.JS.PARAM_IMPL, - Const.Message.Attribute.Scalar.JS.getFunction(message, attr) - ) - - if (attr.types.protoType == ProtoType.MESSAGE) { - addCode(")") - } - - addCode(")\n") - } - addCode("0 -> %T\n", Const.Message.OneOf.notSetClassName(message, oneOf)) - addCode("else -> %T\n", Const.Message.OneOf.unknownClassName(message, oneOf)) - addCode("}") - } - .build() - ) - } -} \ No newline at end of file +object JsOneOfMethodAndClassGenerator : ActualOneOfMethodAndClassGenerator() \ No newline at end of file diff --git a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/oneof/OneOfMethodAndClassGenerator.kt b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/oneof/OneOfMethodAndClassGenerator.kt index e68603a..e45c46f 100644 --- a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/oneof/OneOfMethodAndClassGenerator.kt +++ b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/oneof/OneOfMethodAndClassGenerator.kt @@ -101,7 +101,7 @@ abstract class OneOfMethodAndClassGenerator(private val isActual: Boolean) { ) } - //Unknown + // Unknown addObject(Const.Message.OneOf.unknownClassName(protoMessage, protoOneOf), ChildClassType.Unknown) addObject(Const.Message.OneOf.notSetClassName(protoMessage, protoOneOf), ChildClassType.NotSet) } @@ -116,6 +116,7 @@ abstract class OneOfMethodAndClassGenerator(private val isActual: Boolean) { ) protected open fun modifyParentClass(builder: TypeSpec.Builder, message: ProtoMessage, oneOf: ProtoOneOf) {} + protected open fun modifyChildClass( builder: TypeSpec.Builder, message: ProtoMessage, diff --git a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/proto3_ios_file_writer.kt b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/proto3_ios_file_writer.kt index 9b0cf09..18b76a5 100644 --- a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/proto3_ios_file_writer.kt +++ b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/proto3_ios_file_writer.kt @@ -1,12 +1,12 @@ package io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.ProtoFile -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.dsl.IosJvmDslBuilder -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.proto_file.IOSProtoFileWriter +import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.dsl.ActualDslBuilder +import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.proto_file.IosProtoFileWriter import java.io.File fun writeIOSFiles(protoFile: ProtoFile, iosOutputDir: File) { - IOSProtoFileWriter(protoFile).writeFile(iosOutputDir) + IosProtoFileWriter(protoFile).writeFile(iosOutputDir) //JVM helper // FileSpec @@ -17,5 +17,5 @@ fun writeIOSFiles(protoFile: ProtoFile, iosOutputDir: File) { // .build() // .writeTo(jvmOutputDir) - writeDSLBuilder(protoFile, IosJvmDslBuilder, iosOutputDir) + writeDSLBuilder(protoFile, ActualDslBuilder, iosOutputDir) } \ No newline at end of file diff --git a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/proto3_ios_service_writer.kt b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/proto3_ios_service_writer.kt index 7008d89..90c6c23 100644 --- a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/proto3_ios_service_writer.kt +++ b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/proto3_ios_service_writer.kt @@ -2,9 +2,9 @@ package io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatfo import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.ProtoFile import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.ProtoService -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.service.IOSServiceWriter +import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.service.IosServiceWriter import java.io.File fun writeIOSServiceFile(protoFile: ProtoFile, service: ProtoService, iosOutputFolder: File) { - IOSServiceWriter.writeServiceStub(protoFile, service, iosOutputFolder) + IosServiceWriter.writeServiceStub(protoFile, service, iosOutputFolder) } \ No newline at end of file diff --git a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/proto3_js_file_writer.kt b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/proto3_js_file_writer.kt index 8440dc3..408e19f 100644 --- a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/proto3_js_file_writer.kt +++ b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/proto3_js_file_writer.kt @@ -1,802 +1,12 @@ package io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators -import com.squareup.kotlinpoet.* -import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.JSImpl -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.MessageDeserializer -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.ProtoType -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.common.JsCommonFunctionGenerator -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.dsl.JsDslBuilder +import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.ProtoFile +import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.dsl.ActualDslBuilder import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.proto_file.JsProtoFileWriter -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.* -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.kmMessage import java.io.File -private val jspb = ClassName("io.github.timortel.kotlin_multiplatform_grpc_lib", "JSPB") -private val jspbMessage = jspb.nestedClass("Message") -private val jspbWriter = jspb.nestedClass("BinaryWriter") -private val jspbReader = jspb.nestedClass("BinaryReader") -private val jspbMap = jspb.nestedClass("Map") - -val functionWrapper = MemberName("io.github.timortel.kotlin_multiplatform_grpc_lib.util", "wrap") - -const val objPropertyName = "obj" - -fun generateBridgeClass(parentClass: ClassName?, message: ProtoMessage): TypeSpec { - val prototypePropertyName = "prototype" - val newFunctionName = "new" - - val jsImplClassName = parentClass?.nestedClass("JS_" + message.name) ?: ClassName(message.pkg, "JS_" + message.name) - - return TypeSpec - .classBuilder(jsImplClassName) - .addSuperinterface(JSImpl) - .primaryConstructor( - FunSpec - .constructorBuilder() - .addParameter( - ParameterSpec - .builder(objPropertyName, Dynamic) - .defaultValue("new()") - .build() - ) - .build() - ) - .addProperty( - PropertySpec - .builder(objPropertyName, Dynamic) - .initializer(objPropertyName) - .build() - ) - .addType( - TypeSpec - .companionObjectBuilder() - .addSuperinterface(MessageDeserializer.parameterizedBy(jsImplClassName)) - .addProperty(prototypePropertyName, Dynamic) - .addInitializerBlock( - CodeBlock - .builder() - .addStatement("val jspb = %T", jspb) - .addStatement("val messageClass = js(\"function()·{·jspb.Message.initialize(this,·[],·0,·-1,·null,·null);·}\")") - .addStatement("%T.inherits(messageClass, %T)", jspb, jspbMessage) - .addStatement("%N = messageClass", prototypePropertyName) - .build() - ) - .addFunction( - FunSpec - .builder(newFunctionName) - .returns(Dynamic) - .addStatement("val prototype = %N", prototypePropertyName) - .addStatement("return js(\"new prototype\")") - .build() - ) - .addFunction( - FunSpec - .builder("serializeBinaryToWriter") - .addParameter("msg", Dynamic) - .addParameter("writer", jspbWriter) - .addCode(writeSerializeBinaryToWriter(message)) - .build() - ) - .addFunction( - FunSpec - .builder("deserializeBinaryFromReader") - .addParameter("msg", Dynamic) - .addParameter("reader", jspbReader) - .returns(Dynamic) - .addCode(writeDeserializeBinaryFromReader(message)) - .build() - ) - .addFunction( - FunSpec - .builder("deserializeBinary") - .addModifiers(KModifier.OVERRIDE) - .addParameter("bytes", Dynamic) - .returns(message.jsType) - .addStatement("val reader = %T(bytes)", jspbReader) - .addStatement("val msg = %T()", message.jsType) - .addStatement("deserializeBinaryFromReader(msg.%N, reader)", objPropertyName) - .addStatement("return msg") - .build() - ) - - .build() - ) - .addFunction( - FunSpec - .builder("serializeBinary") - .addModifiers(KModifier.OVERRIDE) - .returns(Dynamic) - .addStatement("val writer = %T()", jspbWriter) - .addStatement("serializeBinaryToWriter(this.%N, writer)", objPropertyName) - .addStatement("return writer.getResultBuffer()") - .build() - ) - .apply { - //Add getters and setters - - message.attributes.forEach { attr -> - when (attr.attributeType) { - is Scalar -> { - val getterCodeBlock = when (attr.types.protoType) { - ProtoType.DOUBLE, ProtoType.FLOAT -> { - val isDouble = attr.types.protoType == ProtoType.DOUBLE - - CodeBlock.of( - "return %T.%N(%N, %L, %L) as %T", - jspbMessage, - "getFloatingPointFieldWithDefault", - objPropertyName, - attr.protoId, - if (isDouble) 0.0 else 0f, - if (isDouble) Double::class else Float::class - ) - } - - ProtoType.INT_32, ProtoType.INT_64 -> { - val isLong = attr.types.protoType == ProtoType.INT_64 - - val code = if (isLong) { - "return %T.%N(%N, %L, 0L)" - } else "return %T.%N(%N, %L, 0) as Int" - - CodeBlock.of( - code, - jspbMessage, - "getFieldWithDefault", - objPropertyName, - attr.protoId - ) - } - - ProtoType.BOOL -> - CodeBlock.of( - "return %T.%N(%N, %L, %L)", - jspbMessage, - "getBooleanFieldWithDefault", - objPropertyName, - attr.protoId, - false - ) - - ProtoType.STRING -> - CodeBlock.of( - "return %T.%N(%N, %L, \"\") as String", - jspbMessage, - "getFieldWithDefault", - objPropertyName, - attr.protoId - ) - - ProtoType.MESSAGE -> - CodeBlock.of( - "return %T(%T.%N(%N, %T.%N, %L))", - attr.types.jsType, - jspbMessage, - "getWrapperField", - objPropertyName, - attr.types.jsType, - prototypePropertyName, - attr.protoId - ) - - ProtoType.ENUM -> - CodeBlock.of( - "return %T.%N(%N, %L, %L)", - jspbMessage, - "getFieldWithDefault", - objPropertyName, - attr.protoId, - 0 - ) - - ProtoType.MAP -> throw IllegalStateException() - } - - val setterCodeBlock = when (attr.types.protoType) { - ProtoType.DOUBLE, ProtoType.FLOAT, ProtoType.INT_32, ProtoType.INT_64, ProtoType.BOOL, ProtoType.STRING, ProtoType.ENUM -> { - val setterName = when (attr.types.protoType) { - ProtoType.DOUBLE, ProtoType.FLOAT -> "setProto3FloatField" - ProtoType.INT_32, ProtoType.INT_64 -> "setProto3IntField" - ProtoType.BOOL -> "setProto3BooleanField" - ProtoType.STRING -> "setProto3StringField" - ProtoType.ENUM -> "setProto3EnumField" - else -> throw IllegalStateException() - } - - CodeBlock.of( - "%T.%N(%N, %L, %N)", - jspbMessage, - setterName, - objPropertyName, - attr.protoId, - "value" - ) - } - - ProtoType.MAP -> throw IllegalStateException() - ProtoType.MESSAGE -> - CodeBlock.of( - "%T.%N(%N, %L, %N.%N)", - jspbMessage, - "setWrapperField", - objPropertyName, - attr.protoId, - "value", - objPropertyName - ) - } - - //getter - addFunction( - FunSpec - .builder(Const.Message.Attribute.Scalar.JS.getFunction(message, attr).simpleName) - .returns(attr.types.jsType) - .addCode(getterCodeBlock) - .build() - ) - - //setter - addFunction( - FunSpec - .builder(Const.Message.Attribute.Scalar.JS.setFunction(message, attr).simpleName) - .addParameter("value", attr.types.jsType.copy(nullable = false)) - .addCode(setterCodeBlock) - .build() - ) - - //hasField - if (attr.types.isNullable) { - addFunction( - FunSpec - .builder(Const.Message.Attribute.Scalar.JS.getHasFunction(message, attr).simpleName) - .returns(Boolean::class) - .addCode( - "return %T.getField(%N, %L) != null", - jspbMessage, - objPropertyName, - attr.protoId - ) - .build() - ) - } - } - - is Repeated -> { - val setterCode = when (attr.types.protoType) { - ProtoType.DOUBLE, ProtoType.FLOAT, ProtoType.INT_32, ProtoType.INT_64, ProtoType.BOOL, ProtoType.STRING, ProtoType.ENUM -> - CodeBlock.of( - "%T.setField(%N, %L, %N)", - jspbMessage, - objPropertyName, - attr.protoId, - "values" - ) - - ProtoType.MESSAGE -> CodeBlock.of( - "%T.setRepeatedWrapperField(%N, %L, %N.map·{ it.%N }.toTypedArray())", - jspbMessage, - objPropertyName, - attr.protoId, - "values", - objPropertyName - ) - - ProtoType.MAP -> throw IllegalStateException() - } - - val getterCode = when (attr.types.protoType) { - ProtoType.DOUBLE, ProtoType.FLOAT -> { - val isDouble = attr.types.protoType == ProtoType.DOUBLE - - CodeBlock.of( - "return %T.%N(%N, %L) as Array<%T>", - jspbMessage, - "getRepeatedFloatingPointField", - objPropertyName, - attr.protoId, - if (isDouble) DOUBLE else FLOAT - ) - } - - ProtoType.INT_32, ProtoType.INT_64 -> { - val isLong = attr.types.protoType == ProtoType.INT_64 - - CodeBlock.of( - "return %T.%N(%N, %L) as Array<%T>", - jspbMessage, - "getRepeatedField", - objPropertyName, - attr.protoId, - if (isLong) NUMBER else INT - ) - } - - ProtoType.BOOL -> - CodeBlock.of( - "return %T.%N(%N, %L) as Array", - jspbMessage, - "getRepeatedBooleanField", - objPropertyName, - attr.protoId - ) - - ProtoType.STRING -> - CodeBlock.of( - "return %T.%N(%N, %L) as Array", - jspbMessage, - "getRepeatedField", - objPropertyName, - attr.protoId - ) - - ProtoType.MESSAGE -> - CodeBlock.of( - "return %T.%N(%N, %T.%N, %L).map·{ %T(it) }.toTypedArray() as Array<%T>", - jspbMessage, - "getRepeatedWrapperField", - objPropertyName, - attr.types.jsType, - prototypePropertyName, - attr.protoId, - attr.types.jsType, - attr.types.jsType - ) - - ProtoType.ENUM -> - CodeBlock.of( - "return %T.%N(%N, %L) as Array", - jspbMessage, - "getRepeatedField", - objPropertyName, - attr.protoId - ) - - ProtoType.MAP -> throw IllegalStateException() - } - - val arrayType = ClassName("kotlin", "Array") - .parameterizedBy( - //INT64 is broken, therefore we cast it to number instead. - if (attr.types.protoType == ProtoType.INT_64) NUMBER else attr.types.jsType - ) - //List getter - addFunction( - FunSpec - .builder(Const.Message.Attribute.Repeated.JS.getListFunctionName(attr)) - .returns(arrayType) - .addCode(getterCode) - .build() - ) - //List setter - addFunction( - FunSpec - .builder(Const.Message.Attribute.Repeated.JS.setListFunctionName(attr)) - .addParameter("values", arrayType) - .addCode(setterCode) - .build() - ) - } - - is MapType -> { - //Get map - addFunction( - FunSpec - .builder(Const.Message.Attribute.Map.JS.getMapFunctionName(attr)) - .addParameter("noLazyCreate", Boolean::class) - .returns(Dynamic) - .addCode( - CodeBlock.builder().apply { - add( - "return %T.getMapField(%N, %L, noLazyCreate, ", - jspbMessage, - objPropertyName, - attr.protoId - ) - - if (attr.attributeType.valueTypes.protoType == ProtoType.MESSAGE) { - add( - "%T.%N", attr.attributeType.valueTypes.jsType, - prototypePropertyName - ) - } else { - add("null") - } - - add(")") - }.build() - ) - .build() - ) - } - } - } - - //Add get case function for each oneof - message.oneOfs.forEach { oneOf -> - addFunction( - FunSpec - .builder(Const.Message.OneOf.JS.getCaseFunctionName(oneOf)) - .returns(Int::class) - .apply { - addCode( - "return %T.computeOneofCase(%N, arrayOf(", - jspbMessage, - objPropertyName, - ) - - addCode( - oneOf.attributes.joinToString { it.protoId.toString() }, - ) - - addCode("))") - } - .build() - ) - } - - message.children.forEach { childMessage -> - addType(generateBridgeClass(jsImplClassName, childMessage)) - } - } - .build() -} - -private fun writeSerializeBinaryToWriter(message: ProtoMessage): CodeBlock { - return CodeBlock.builder().apply { - addStatement("val message = %T(msg)", message.jsType) - addStatement("var temp: dynamic = undefined") - message.attributes.forEach { attr -> - when (attr.attributeType) { - is Scalar -> { - val getter = Const.Message.Attribute.Scalar.JS.getFunction(message, attr) - addStatement("temp = message.%N()", getter) - add("if (message.%N()", getter) - - val (writerFunction, alreadyHandled) = when (attr.types.protoType) { - ProtoType.DOUBLE -> { - beginControlFlow(" != 0.0)") - "writeDouble" to false - } - - ProtoType.FLOAT -> { - beginControlFlow(" != 0f)") - "writeFloat" to false - } - - ProtoType.INT_32 -> { - beginControlFlow(" != 0)") - "writeInt32" to false - } - - ProtoType.INT_64 -> { - beginControlFlow(" != 0L)") - "writeInt64" to false - } - - ProtoType.BOOL -> { - beginControlFlow(")") - "writeBool" to false - } - - ProtoType.STRING -> { - beginControlFlow(".length > 0)") - "writeString" to false - } - - ProtoType.MAP -> throw IllegalStateException() - ProtoType.MESSAGE -> { - beginControlFlow(" != null)") - addStatement( - "writer.writeMessage(%L, temp.%N, %T.Companion::serializeBinaryToWriter)", - attr.protoId, - objPropertyName, - attr.types.jsType, - ) - endControlFlow() - "writeMessage" to true - } - - ProtoType.ENUM -> { - beginControlFlow(" != 0)") - "writeEnum" to false - } - } - - if (!alreadyHandled) { - addStatement("writer.%N(%L, temp)", writerFunction, attr.protoId) - endControlFlow() - } - } - - is Repeated -> { - addStatement("temp = message.%N()", Const.Message.Attribute.Repeated.JS.getListFunctionName(attr)) - beginControlFlow("if (temp.length > 0)") - addStatement("writer.%N(%L, temp", getRepeatedWriterFunction(attr.types.protoType), attr.protoId) - - if (attr.types.protoType == ProtoType.MESSAGE) { - add(", %T.Companion::serializeBinaryToWriter", attr.types.jsType) - } - - add(")") - - endControlFlow() - } - - is MapType -> { - addStatement("temp = message.%N(false)", Const.Message.Attribute.Map.JS.getMapFunctionName(attr)) - beginControlFlow("if (temp != null && temp.getLength() > 0)") - add( - "temp.serializeBinary(%L, writer, %T::%N.%M(writer), %T::%N.%M(writer), ", - attr.protoId, - jspbWriter, - getWriterFunction(attr.attributeType.keyTypes.protoType), - functionWrapper, - jspbWriter, - getWriterFunction(attr.attributeType.valueTypes.protoType), - functionWrapper - ) - if (attr.attributeType.valueTypes.protoType == ProtoType.MESSAGE) { - add("%T.Companion::serializeBinaryToWriter", attr.attributeType.valueTypes.jsType) - } else { - add("null") - } - add(")") - endControlFlow() - } - } - } - }.build() -} - -private fun getWriterFunction(protoType: ProtoType) = when (protoType) { - ProtoType.DOUBLE -> "writeDouble" - ProtoType.FLOAT -> "writeFloat" - ProtoType.INT_32 -> "writeInt32" - ProtoType.INT_64 -> "writeInt64" - ProtoType.BOOL -> "writeBool" - ProtoType.STRING -> "writeString" - ProtoType.MAP -> throw IllegalArgumentException() - ProtoType.MESSAGE -> "writeMessage" - ProtoType.ENUM -> "writeEnum" -} - -private fun getRepeatedWriterFunction(protoType: ProtoType) = when (protoType) { - ProtoType.DOUBLE -> "writePackedDouble" - ProtoType.FLOAT -> "writePackedFloat" - ProtoType.INT_32 -> "writePackedInt32" - ProtoType.INT_64 -> "writePackedInt64" - ProtoType.BOOL -> "writePackedBool" - ProtoType.STRING -> "writeRepeatedString" - ProtoType.MAP -> throw IllegalArgumentException() - ProtoType.MESSAGE -> "writeRepeatedMessage" - ProtoType.ENUM -> "writePackedEnum" -} - -/** - * Generates the code for deserializeBinaryFromReader. Mimics the code generation of protoc for js - */ -private fun writeDeserializeBinaryFromReader(message: ProtoMessage): CodeBlock { - val getRepeatedListVarName = { repeatedAttr: ProtoMessageAttribute -> "${repeatedAttr.name.decapitalize()}List" } - - return CodeBlock.builder().apply { - addStatement("val message = %T(msg)", message.jsType) - - message.attributes.filter { it.attributeType is Repeated }.forEach { repeatedAttr -> - addStatement( - "val %N = mutableListOf<%T>()", - getRepeatedListVarName(repeatedAttr), - repeatedAttr.types.jsType - ) - } - - beginControlFlow("while (reader.nextField())") - - beginControlFlow("if (reader.isEndGroup())") - addStatement("break;") - endControlFlow() - - addStatement("val field = reader.getFieldNumber()") - beginControlFlow("when (field)") - - message.attributes.forEach { attr -> - beginControlFlow("%L ->", attr.protoId) - when (attr.attributeType) { - is Scalar -> { - when (attr.types.protoType) { - ProtoType.DOUBLE, ProtoType.FLOAT, ProtoType.INT_32, ProtoType.BOOL, ProtoType.ENUM, ProtoType.STRING -> { - addStatement("val value = reader.%N()", getScalarReadFunctionName(attr.types.protoType)) - addStatement( - "message.%N(value)", - Const.Message.Attribute.Scalar.JS.setFunction(message, attr) - ) - } - - ProtoType.INT_64 -> { - addStatement("//Handle weird behaviour of Long") - addStatement("val value = (reader.readInt64() as %T).toLong()", NUMBER) - addStatement( - "message.%N(value)", - Const.Message.Attribute.Scalar.JS.setFunction(message, attr) - ) - } - - ProtoType.MESSAGE -> { - addStatement("val value = %T()\n", attr.types.jsType) - addStatement( - "reader.readMessage(value.%N, %T.Companion::deserializeBinaryFromReader)", - objPropertyName, - attr.types.jsType - ) - addStatement( - "message.%N(value)", - Const.Message.Attribute.Scalar.JS.setFunction(message, attr) - ) - } - - ProtoType.MAP -> throw IllegalStateException() - } - } - - is Repeated -> { - when (attr.types.protoType) { - ProtoType.DOUBLE, ProtoType.FLOAT, ProtoType.INT_32, ProtoType.INT_64, ProtoType.BOOL, ProtoType.ENUM -> { - addStatement( - "val value = if (reader.isDelimited()) (reader.%N() as Array<%T>).toList() else listOf(reader.%N())\n", - getRepeatedReadFunctionName(attr.types.protoType), - attr.types.jsType, - getScalarReadFunctionName(attr.types.protoType) - ) - - addStatement("%N += value", getRepeatedListVarName(attr)) - } - - ProtoType.STRING -> { - addStatement("%N += reader.readString()", getRepeatedListVarName(attr)) - } - - ProtoType.MESSAGE -> { - addStatement("val value = %T()", attr.types.jsType) - addStatement( - "reader.readMessage(value.%N, %T.Companion::deserializeBinaryFromReader)", - objPropertyName, - attr.types.jsType - ) - addStatement("%N += value", getRepeatedListVarName(attr)) - } - - ProtoType.MAP -> throw IllegalStateException() - } - } - - is MapType -> { - addStatement( - "val value = message.%N(false)", - Const.Message.Attribute.Map.JS.getMapFunctionName(attr) - ) - addStatement("reader.readMessage(value,·{·message:·dynamic,·reader:·dynamic ->") - addStatement( - "%T.deserializeBinary(message, reader, %T::%N.%M(reader), %T::%N.%M(reader), ", - jspbMap, - jspbReader, - getScalarReadFunctionName(attr.attributeType.keyTypes.protoType), - functionWrapper, - jspbReader, - getScalarReadFunctionName(attr.attributeType.valueTypes.protoType), - functionWrapper - ) - - //Add the reader callback, for non message types = null - if (attr.attributeType.valueTypes.protoType == ProtoType.MESSAGE) { - add("%T::deserializeBinaryFromReader, ", attr.attributeType.valueTypes.jsType) - } else { - add("null, ") - } - - //The key type cannot be another map or message. - if (attr.attributeType.keyTypes.protoType == ProtoType.STRING) { - add("\"\", ") - } else { - add("%L, ", getDefaultValueLiteral(attr.attributeType.keyTypes.protoType)) - } - - if (attr.attributeType.valueTypes.protoType == ProtoType.MESSAGE) { - add("%T().%N", attr.attributeType.valueTypes.jsType, objPropertyName) - } else if (attr.attributeType.valueTypes.protoType == ProtoType.STRING) { - add("\"\"") - } else { - add("%L", getDefaultValueLiteral(attr.attributeType.valueTypes.protoType)) - } - - add(")\n") - add("})\n") - } - } - endControlFlow() - } - - addStatement("else -> reader.skipField()") - - endControlFlow() - - endControlFlow() - - //In the end set the repeated fields - message.attributes.filter { it.attributeType is Repeated }.forEach { repeatedAttr -> - addStatement( - "message.%N(%N.toTypedArray())", - Const.Message.Attribute.Repeated.JS.setListFunctionName(repeatedAttr), - getRepeatedListVarName(repeatedAttr) - ) - } - - addStatement("return msg") - }.build() -} - -/** - * @return the default value for unset values of the given prototype. E.g. for string the empty string, for integers 0 etc. - */ -private fun getDefaultValueLiteral(protoType: ProtoType) = when (protoType) { - ProtoType.DOUBLE -> 0.0 - ProtoType.FLOAT -> 0f - ProtoType.INT_32 -> 0 - ProtoType.INT_64 -> 0L - ProtoType.BOOL -> false - ProtoType.STRING -> "" - ProtoType.MAP -> throw IllegalArgumentException("Maps never need a default type") - ProtoType.MESSAGE -> throw IllegalArgumentException("Message default cannot be represented as a literal") - ProtoType.ENUM -> 0 -} - -private fun getScalarReadFunctionName(protoType: ProtoType) = when (protoType) { - ProtoType.DOUBLE -> "readDouble" - ProtoType.FLOAT -> "readFloat" - ProtoType.INT_32 -> "readInt32" - ProtoType.INT_64 -> "readInt64" - ProtoType.BOOL -> "readBool" - ProtoType.ENUM -> "readEnum" - ProtoType.STRING -> "readString" - ProtoType.MAP -> throw IllegalArgumentException("Map does not have a read function.") - ProtoType.MESSAGE -> "readMessage" -} - -private fun getRepeatedReadFunctionName(protoType: ProtoType) = when (protoType) { - ProtoType.DOUBLE -> "readPackedDouble" - ProtoType.FLOAT -> "readPackedFloat" - ProtoType.INT_32 -> "readPackedInt32" - ProtoType.INT_64 -> "readPackedInt64" - ProtoType.BOOL -> "readPackedBool" - ProtoType.ENUM -> "readPackedEnum" - ProtoType.MAP, ProtoType.MESSAGE, ProtoType.STRING -> throw IllegalArgumentException("Map, string and message do not have repeated read functions.") -} - fun writeJsFiles(protoFile: ProtoFile, jsOutputDir: File) { JsProtoFileWriter(protoFile).writeFile(jsOutputDir) - //JS Bridge - FileSpec - .builder(protoFile.pkg, protoFile.fileNameWithoutExtension + "_js_bridge") - .apply { - //Add an open class for each message - - protoFile.messages.forEach { message -> - addType( - generateBridgeClass(null, message) - ) - } - } - .build() - .writeTo(jsOutputDir) - - //Js common helper - FileSpec - .builder(protoFile.pkg, protoFile.fileNameWithoutExtension + "_js_helper") - .apply { - JsCommonFunctionGenerator(this).generateCommonGetter(protoFile.messages) - } - .build() - .writeTo(jsOutputDir) - - writeDSLBuilder(protoFile, JsDslBuilder, jsOutputDir) + writeDSLBuilder(protoFile, ActualDslBuilder, jsOutputDir) } \ No newline at end of file diff --git a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/proto3_js_service_writer.kt b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/proto3_js_service_writer.kt index b3bbe57..393b0ae 100644 --- a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/proto3_js_service_writer.kt +++ b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/proto3_js_service_writer.kt @@ -7,18 +7,25 @@ import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatfor import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.service.JsServiceWriter import java.io.File -private val grpcWebClientBase = ClassName("io.github.timortel.kotlin_multiplatform_grpc_lib.rpc", "GrpcWebClientBase") -private val clientOptions = ClassName("io.github.timortel.kotlin_multiplatform_grpc_lib.rpc", "ClientOptions") -private val methodDescriptor = ClassName("io.github.timortel.kotlin_multiplatform_grpc_lib.rpc", "MethodDescriptor") +private val grpcWebClientBase = + ClassName("io.github.timortel.kotlin_multiplatform_grpc_lib.rpc", "GrpcWebClientBase") +private val clientOptions = + ClassName("io.github.timortel.kotlin_multiplatform_grpc_lib.rpc", "ClientOptions") +private val methodDescriptor = + ClassName("io.github.timortel.kotlin_multiplatform_grpc_lib.rpc", "MethodDescriptor") fun writeJsServiceFile(protoFile: ProtoFile, service: ProtoService, jsOutputFolder: File) { JsServiceWriter.writeServiceStub(protoFile, service, jsOutputFolder) - val getMethodDescriptorName = { rpc: ProtoRpc -> "${rpc.rpcName.decapitalize()}MethodDescriptor" } + val getMethodDescriptorName = + { rpc: ProtoRpc -> "${rpc.rpcName.decapitalize()}MethodDescriptor" } //js bridge FileSpec - .builder(protoFile.pkg, "${protoFile.fileNameWithoutExtension}_${service.serviceName}_service_js_bridge") + .builder( + protoFile.pkg, + "${protoFile.fileNameWithoutExtension}_${service.serviceName}_service_js_bridge" + ) .addType( TypeSpec .classBuilder(getJSServiceName(service)) @@ -58,17 +65,17 @@ fun writeJsServiceFile(protoFile: ProtoFile, service: ProtoService, jsOutputFold .builder(methodDescriptorName, methodDescriptor, KModifier.PRIVATE) .initializer(CodeBlock.builder().apply { addStatement( - "%T(%S, %S, ::%T, ::%T, {·request:·dynamic -> %T(request).serializeBinary() }, %T::deserializeBinary)", + "%T(%S, %S, ::%T, ::%T, {·request:·%T -> request.serialize() }, %T.Companion::deserialize)", methodDescriptor, "/${protoFile.pkg}.${service.serviceName}/${rpc.rpcName}", when (rpc.method) { ProtoRpc.Method.UNARY -> "unary" ProtoRpc.Method.SERVER_STREAMING -> "server_streaming" }, - rpc.request.jsType, - rpc.response.jsType, - rpc.request.jsType, - rpc.response.jsType + rpc.request.commonType, + rpc.response.commonType, + rpc.request.commonType, + rpc.response.commonType ) }.build()) .build() @@ -77,23 +84,22 @@ fun writeJsServiceFile(protoFile: ProtoFile, service: ProtoService, jsOutputFold addFunction( FunSpec .builder(rpc.rpcName) - .addParameter("request", rpc.request.jsType) + .addParameter("request", rpc.request.commonType) .addParameter("metadata", Dynamic) .apply { addCode( - "return client.%N(\"\$hostname/%L/%L\", request.%N, metadata ?: js(%S), %N", + "return client.%N(\"\$hostname/%L/%L\", request, metadata ?: js(%S), %N", when (rpc.method) { ProtoRpc.Method.UNARY -> "rpcCall" ProtoRpc.Method.SERVER_STREAMING -> "serverStreaming" }, "${protoFile.pkg}.${service.serviceName}", rpc.rpcName, - objPropertyName, "{}", methodDescriptorName ) - when(rpc.method) { + when (rpc.method) { ProtoRpc.Method.UNARY -> { addParameter( "callback", @@ -101,14 +107,15 @@ fun writeJsServiceFile(protoFile: ProtoFile, service: ProtoService, jsOutputFold parameters = arrayOf( Dynamic, - rpc.response.jsType + rpc.response.commonType ), - returnType = Unit::class.asTypeName() + returnType = UNIT ) ) addCode(", callback)") } + ProtoRpc.Method.SERVER_STREAMING -> { returns(Dynamic) addCode(")") diff --git a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/proto3_jvm_file_writer.kt b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/proto3_jvm_file_writer.kt index c27bd41..78537f6 100644 --- a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/proto3_jvm_file_writer.kt +++ b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/proto3_jvm_file_writer.kt @@ -2,7 +2,7 @@ package io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatfo import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.proto_file.JvmProtoFileWriter import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.ProtoFile -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.dsl.IosJvmDslBuilder +import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.dsl.ActualDslBuilder import java.io.File fun writeJvmFiles(protoFile: ProtoFile, jvmOutputDir: File) { @@ -17,5 +17,5 @@ fun writeJvmFiles(protoFile: ProtoFile, jvmOutputDir: File) { // .build() // .writeTo(jvmOutputDir) - writeDSLBuilder(protoFile, IosJvmDslBuilder, jvmOutputDir) + writeDSLBuilder(protoFile, ActualDslBuilder, jvmOutputDir) } \ No newline at end of file diff --git a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/proto_file/ActualProtoFileWriter.kt b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/proto_file/ActualProtoFileWriter.kt new file mode 100644 index 0000000..adefaf0 --- /dev/null +++ b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/proto_file/ActualProtoFileWriter.kt @@ -0,0 +1,810 @@ +package io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.proto_file + +import com.squareup.kotlinpoet.ClassName +import com.squareup.kotlinpoet.CodeBlock +import com.squareup.kotlinpoet.FunSpec +import com.squareup.kotlinpoet.KModifier +import com.squareup.kotlinpoet.LIST +import com.squareup.kotlinpoet.MAP +import com.squareup.kotlinpoet.MUTABLE_LIST +import com.squareup.kotlinpoet.MUTABLE_MAP +import com.squareup.kotlinpoet.ParameterSpec +import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy +import com.squareup.kotlinpoet.TypeName +import com.squareup.kotlinpoet.TypeSpec +import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.CodedInputStream +import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.CodedOutputStream +import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.DataType +import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.MessageDeserializer +import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.ProtoType +import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.Types +import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.WireFormatForType +import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.WireFormatMakeTag +import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.MapType +import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.ProtoFile +import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.ProtoMessage +import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.ProtoMessageAttribute +import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.ProtoOneOf +import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.Repeated +import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.Scalar +import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.Const +import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.kmMessage +import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.readKMMessage +import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.readMapEntry + +abstract class ActualProtoFileWriter(protoFile: ProtoFile) : ProtoFileWriter(protoFile, true) { + abstract val serializeFunctionCode: CodeBlock + abstract val serializedDataType: TypeName + + abstract val deserializeFunctionCode: CodeBlock + + override fun applyToClass( + builder: TypeSpec.Builder, + message: ProtoMessage, + messageClassName: ClassName + ) { + builder.apply { + primaryConstructor( + FunSpec + .constructorBuilder() + .apply { + //one of attributes do not get a parameter, as they get the one of parameter + message.attributes.filter { !it.isOneOfAttribute }.forEach { attr -> + when (attr.attributeType) { + is Scalar -> { + if (attr.types.isNullable) { + addParameter( + ParameterSpec.builder( + attr.name, + attr.commonType.copy(nullable = true) + ) + .defaultValue("null") + .build() + ) + } else { + addParameter( + ParameterSpec + .builder(attr.name, attr.commonType) + .defaultValue( + attr.commonDefaultValue( + false, + useEmptyMessage = false + ) + ) + .build() + ) + } + } + + is Repeated -> { + addParameter( + ParameterSpec + .builder( + Const.Message.Attribute.Repeated.listPropertyName( + attr + ), + LIST.parameterizedBy(attr.commonType) + ) + .defaultValue("emptyList()") + .build() + ) + } + + is MapType -> { + addParameter( + ParameterSpec + .builder( + Const.Message.Attribute.propertyName(message, attr), + MAP.parameterizedBy( + attr.attributeType.keyTypes.iosType, + attr.attributeType.valueTypes.iosType + ) + ) + .defaultValue("emptyMap()") + .build() + ) + } + } + } + + message.oneOfs.forEach { oneOf -> + addParameter( + ParameterSpec + .builder( + Const.Message.OneOf.propertyName(message, oneOf), + Const.Message.OneOf.parentSealedClassName(message, oneOf) + ) + .defaultValue(oneOf.defaultValue(message)) + .build() + ) + } + } + .build() + ) + + addSuperinterface(kmMessage) + + addFunction( + FunSpec + .builder(Const.Message.IOS.SerializeFunction.NAME) + .addModifiers(KModifier.OVERRIDE) + .addParameter( + Const.Message.IOS.SerializeFunction.STREAM_PARAM, + CodedOutputStream + ) + .apply { buildSerializeFunction(this, message) } + .build() + ) + + addFunction( + FunSpec + .builder("serialize") + .addModifiers(KModifier.OVERRIDE) + .returns(serializedDataType) + .addCode( + serializeFunctionCode + ) + + .build() + ) + + addType( + TypeSpec + .companionObjectBuilder() + .addSuperinterface( + MessageDeserializer.parameterizedBy( + messageClassName, + serializedDataType + ) + ) + .addFunction( + // The function that builds the message from NSData + FunSpec + .builder(Const.Message.Companion.DataDeserializationFunction.NAME) + .addModifiers(KModifier.OVERRIDE) + .addParameter( + Const.Message.Companion.DataDeserializationFunction.DATA_PARAM, + serializedDataType + ) + .addCode( + deserializeFunctionCode + ) + .returns(message.commonType) + .build() + ) + .addFunction( + //The function that builds the message from a stream. + FunSpec + .builder(Const.Message.Companion.WrapperDeserializationFunction.NAME) + .addParameter( + Const.Message.Companion.WrapperDeserializationFunction.STREAM_PARAM, + CodedInputStream + ) + .returns(message.iosType) + .apply { buildWrapperDeserializationFunction(this, message) } + .build() + ) + .build() + ) + } + } + + /** + * Generate the serialize function, adds a write call for each attribute + */ + private fun buildSerializeFunction(builder: FunSpec.Builder, message: ProtoMessage) { + builder.apply { + message.attributes.filter { !it.isOneOfAttribute }.forEach { attr -> + when (attr.attributeType) { + is Scalar -> { + addCode("if·(%N·!=·", Const.Message.Attribute.propertyName(message, attr)) + addCode(attr.commonDefaultValue(false, useEmptyMessage = false)) + beginControlFlow(")·{ ") + addCode( + getWriteScalarFieldCode( + message, + attr, + Const.Message.IOS.SerializeFunction.STREAM_PARAM, + performIsMessageSetCheck = true + ) + ) + endControlFlow() + } + + is Repeated -> { + val writeListPartialFunctionName = when (attr.types.protoType) { + ProtoType.DOUBLE -> "Double" + ProtoType.FLOAT -> "Float" + ProtoType.INT_32 -> "Int32" + ProtoType.INT_64 -> "Int64" + ProtoType.BOOL -> "Bool" + ProtoType.STRING -> "String" + ProtoType.MESSAGE -> "Message" + ProtoType.ENUM -> "Enum" + ProtoType.MAP -> throw IllegalStateException() + } + + val writeArrayFunction = "write${writeListPartialFunctionName}Array" + + when (attr.types.protoType) { + ProtoType.DOUBLE, + ProtoType.FLOAT, + ProtoType.INT_32, + ProtoType.INT_64, + ProtoType.BOOL -> { + //Write packed. + // From GPBDescriptor.m: GPBWireFormatForType(description->dataType, + // ((description->flags & GPBFieldPacked) != 0)) + addStatement( + "%N.%N(%L, %N, %M(%L, %M(%T.%N, true)).toUInt())", + Const.Message.IOS.SerializeFunction.STREAM_PARAM, + writeArrayFunction, + attr.protoId, + Const.Message.Attribute.Repeated.listPropertyName(attr), + WireFormatMakeTag, + attr.protoId, + WireFormatForType, + DataType, + getDataTypeForProtoType(attr.types.protoType) + ) + } + + ProtoType.ENUM -> { + addStatement( + "%N.%N(%L, %N.map·{ it.%N }, %M(%L, %M(%T.%N, true)).toUInt())", + Const.Message.IOS.SerializeFunction.STREAM_PARAM, + writeArrayFunction, + attr.protoId, + Const.Message.Attribute.Repeated.listPropertyName(attr), + Const.Enum.VALUE_PROPERTY_NAME, + WireFormatMakeTag, + attr.protoId, + WireFormatForType, + DataType, + getDataTypeForProtoType(attr.types.protoType) + ) + } + + ProtoType.STRING -> { + //Write unpacked. 0 means unpacked + addStatement( + "%N.%N(%L, %N)", + Const.Message.IOS.SerializeFunction.STREAM_PARAM, + writeArrayFunction, + attr.protoId, + Const.Message.Attribute.propertyName(message, attr) + ) + } + + ProtoType.MESSAGE -> { + addStatement( + "%N.%N(%L, %N)", + Const.Message.IOS.SerializeFunction.STREAM_PARAM, + writeArrayFunction, + attr.protoId, + Const.Message.Attribute.propertyName(message, attr) + ) + } + + ProtoType.MAP -> throw IllegalStateException() + } + } + + is MapType -> { + buildMapAttributeSerializeCode(builder, message, attr, attr.attributeType) + } + } + } + + message.oneOfs.forEach { oneOf -> + addStatement( + "%N.%N(%N)", + Const.Message.OneOf.propertyName(message, oneOf), + Const.Message.OneOf.IosJvm.SERIALIZE_FUNCTION_NAME, + Const.Message.IOS.SerializeFunction.STREAM_PARAM + ) + } + } + } + + /** + * Called by [buildSerializeFunction] when an attribute with the map type needs to be serialized + */ + protected abstract fun buildMapAttributeSerializeCode( + builder: FunSpec.Builder, + message: ProtoMessage, + attr: ProtoMessageAttribute, + mapType: MapType + ) + + /** + * Append code that serializes the given types in a function call for type CodedOutputStream.(fieldNumber: Int, K) -> Unit + */ + protected fun FunSpec.Builder.addMapKeyTypeSerializationCode(keyTypes: Types) { + when (keyTypes.protoType) { + ProtoType.INT_32, + ProtoType.INT_64, + ProtoType.BOOL, + ProtoType.STRING -> addWriteScalarFieldCode(keyTypes) + + ProtoType.DOUBLE, ProtoType.FLOAT, ProtoType.MESSAGE, ProtoType.MAP, ProtoType.ENUM -> throw IllegalStateException( + "Illegal Map Key Type: ${keyTypes.protoType}" + ) + } + } + + /** + * Append code that serializes the given types in a function call for type CodedOutputStream.(fieldNumber: Int, V) -> Unit + */ + protected fun FunSpec.Builder.addMapValueTypeSerializationCode(valueTypes: Types) { + when (valueTypes.protoType) { + ProtoType.DOUBLE, + ProtoType.FLOAT, + ProtoType.INT_32, + ProtoType.INT_64, + ProtoType.BOOL, + ProtoType.STRING -> addWriteScalarFieldCode(valueTypes) + + ProtoType.ENUM -> addWriteEnumFieldCode() + ProtoType.MESSAGE -> addCode( + "{·fieldNumber,·msg·-> %N.writeMessage(fieldNumber, msg)·}", + Const.Message.IOS.SerializeFunction.STREAM_PARAM + ) + + ProtoType.MAP -> throw IllegalStateException() + } + } + + fun FunSpec.Builder.addWriteScalarFieldCode(types: Types) { + addCode( + "%T::%N", + CodedOutputStream, + getWriteScalarFunctionName(types.protoType) + ) + } + + private fun FunSpec.Builder.addWriteEnumFieldCode() { + addCode( + "{·fieldNumber,·it·-> writeEnum(fieldNumber, it.%N)·}", + Const.Enum.VALUE_PROPERTY_NAME + ) + } + + + private fun buildWrapperDeserializationFunction( + builder: FunSpec.Builder, + message: ProtoMessage + ) { + val wrapperParamName = + Const.Message.Companion.WrapperDeserializationFunction.STREAM_PARAM + + val getOneOfVarName = { oneOf: ProtoOneOf -> oneOf.name } + + val getAttrVarName = { attr: ProtoMessageAttribute -> + when (attr.attributeType) { + is MapType -> "${attr.name}Map" + is Repeated -> "${attr.name}List" + is Scalar -> attr.name + } + } + + builder.apply { + message.attributes.filter { !it.isOneOfAttribute }.forEach { attr -> + val (type, mutable) = when (attr.attributeType) { + is Scalar -> attr.types.commonType.copy(nullable = attr.types.isNullable) to true + is Repeated -> MUTABLE_LIST.parameterizedBy(attr.types.iosType) to false + is MapType -> MUTABLE_MAP.parameterizedBy( + attr.attributeType.keyTypes.iosType, + attr.attributeType.valueTypes.iosType + ) to false + } + + addCode( + if (mutable) "var %N: %T = " else "val %N: %T = ", + getAttrVarName(attr), + type + ) + addCode(attr.commonDefaultValue(true, useEmptyMessage = false)) + addCode("\n") + } + + message.oneOfs.forEach { oneOf -> + addCode( + "var %N: %T = ", + getOneOfVarName(oneOf), + Const.Message.OneOf.parentSealedClassName(message, oneOf) + ) + + addCode(oneOf.defaultValue(message)) + addCode("\n") + } + + beginControlFlow("while (true)") + addStatement( + "val tag = %N.readTag()", + wrapperParamName + ) + addStatement("if (tag == 0) break") + + beginControlFlow("when (tag)") + + val getScalarAndRepeatedTagCode = { attr: ProtoMessageAttribute -> + val isPacked = isAttrPackable(attr) && attr.attributeType is Repeated + + val dataType = getDataTypeForProtoType(attr.types.protoType) + + CodeBlock.of( + "%M(%L, %M(%T.%N, %L)).toInt()", + WireFormatMakeTag, + attr.protoId, + WireFormatForType, + DataType, + dataType, + isPacked + ) + } + + //The function name of that reads the type from the input stream + val getScalarReadFunctionName = { protoType: ProtoType -> + when (protoType) { + ProtoType.DOUBLE -> "readDouble" + ProtoType.FLOAT -> "readFloat" + ProtoType.INT_32 -> "readInt32" + ProtoType.INT_64 -> "readInt64" + ProtoType.BOOL -> "readBool" + ProtoType.STRING -> "readString" + ProtoType.ENUM, ProtoType.MAP, ProtoType.MESSAGE -> throw IllegalStateException() + } + } + + val getScalarReadFieldCode = { attr: ProtoMessageAttribute -> + when (attr.types.protoType) { + ProtoType.DOUBLE, + ProtoType.FLOAT, + ProtoType.INT_32, + ProtoType.INT_64, + ProtoType.BOOL, + ProtoType.STRING -> { + CodeBlock.of( + "%N.%N()", + wrapperParamName, + getScalarReadFunctionName(attr.types.protoType) + ) + } + + ProtoType.MESSAGE -> CodeBlock.of( + "%M(%N, %T.Companion::%N)", + readKMMessage, + wrapperParamName, + attr.types.iosType, + Const.Message.Companion.WrapperDeserializationFunction.NAME + ) + + ProtoType.ENUM -> CodeBlock.of( + "%T.%N(%N.readEnum())", + attr.types.iosType, + Const.Enum.getEnumForNumFunctionName, + wrapperParamName + ) + + else -> throw IllegalStateException() + } + } + + message.attributes.filter { !it.isOneOfAttribute }.forEach { attr -> + when (attr.attributeType) { + is Scalar, is Repeated -> { + addCode(getScalarAndRepeatedTagCode(attr)) + } + + is MapType -> addCode( + "%M(%L, %M(%T.%N, false)).toInt()", + WireFormatMakeTag, + attr.protoId, + WireFormatForType, + DataType, + "MESSAGE" + ) + } + + addCode(" -> ") + + when (attr.attributeType) { + is Scalar -> { + addCode("%N·=·", getAttrVarName(attr)) + addCode(getScalarReadFieldCode(attr)) + addCode("\n") + } + + is Repeated -> { + beginControlFlow("{") + when (attr.types.protoType) { + ProtoType.DOUBLE, + ProtoType.FLOAT, + ProtoType.INT_32, + ProtoType.INT_64, + ProtoType.BOOL, + ProtoType.ENUM -> { + //All packable + addStatement( + "val length·=·%N.readInt32()", + wrapperParamName + ) + addStatement( + "val limit·=·%N.pushLimit(length)", + wrapperParamName + ) + beginControlFlow( + "while·(!%N.isAtEnd)·{", + wrapperParamName + ) + //Enums need to first get mapped + if (attr.types.protoType == ProtoType.ENUM) { + addStatement( + "%N += %T.%N(%N.readEnum())", + getAttrVarName(attr), + attr.types.commonType, + Const.Enum.getEnumForNumFunctionName, + wrapperParamName + ) + } else { + val functionName = + getScalarReadFunctionName(attr.types.protoType) + + addStatement( + "%N·+=·%N.%N()", + getAttrVarName(attr), + wrapperParamName, + functionName + ) + } + + endControlFlow() + + addStatement("%N.popLimit(limit)", wrapperParamName) + } + + ProtoType.MESSAGE -> addCode( + "%N·+=·%M(%N, %T.Companion::%N)\n", + getAttrVarName(attr), + readKMMessage, + wrapperParamName, + attr.types.iosType, + Const.Message.Companion.WrapperDeserializationFunction.NAME + ) + + ProtoType.STRING -> addCode( + "%N·+=·%N.readString()", + getAttrVarName(attr), + wrapperParamName + ) + + ProtoType.MAP -> throw IllegalStateException() + } + endControlFlow() + } + + is MapType -> { + addCode( + "%M(%N, %N, %T.%N, %T.%N, ", + readMapEntry, + Const.Message.Companion.WrapperDeserializationFunction.STREAM_PARAM, + getAttrVarName(attr), + DataType, + getDataTypeForProtoType(attr.attributeType.keyTypes.protoType), + DataType, + getDataTypeForProtoType(attr.attributeType.valueTypes.protoType), + ) + + val getDefaultEntry = { types: Types -> + when (types.protoType) { + ProtoType.DOUBLE -> CodeBlock.of("0.0") + ProtoType.FLOAT -> CodeBlock.of("0f") + ProtoType.INT_32 -> CodeBlock.of("0") + ProtoType.INT_64 -> CodeBlock.of("0L") + ProtoType.BOOL -> CodeBlock.of("false") + ProtoType.STRING -> CodeBlock.of("\"\"") + ProtoType.MAP -> throw IllegalStateException() + ProtoType.MESSAGE -> CodeBlock.of("%T()", types.iosType) + ProtoType.ENUM -> CodeBlock.of( + "%T.%N(0)", + types.iosType, + Const.Enum.getEnumForNumFunctionName + ) + } + } + + val addReadScalarCode = { types: Types -> + addCode( + "{·%N()·}", + getScalarReadFunctionName(types.protoType) + ) + } + + val addReadEnumCode = { types: Types -> + addCode( + "{·%T.%N(readEnum())·}", + types.iosType, + Const.Enum.getEnumForNumFunctionName + ) + } + + //Write default values + addCode(getDefaultEntry(attr.attributeType.keyTypes)) + addCode(", ") + addCode(getDefaultEntry(attr.attributeType.valueTypes)) + addCode(", ") + + when (attr.attributeType.keyTypes.protoType) { + ProtoType.INT_32, + ProtoType.INT_64, + ProtoType.BOOL, + ProtoType.STRING -> addReadScalarCode(attr.attributeType.keyTypes) + + ProtoType.ENUM, + ProtoType.MESSAGE, + ProtoType.MAP, + ProtoType.DOUBLE, + ProtoType.FLOAT -> throw IllegalStateException("Illegal key types") + } + addCode(", ") + when (attr.attributeType.valueTypes.protoType) { + ProtoType.DOUBLE, + ProtoType.FLOAT, + ProtoType.INT_32, + ProtoType.INT_64, + ProtoType.BOOL, + ProtoType.STRING -> addReadScalarCode(attr.attributeType.valueTypes) + + ProtoType.MESSAGE -> addCode( + "{·%M(this, %T.Companion::%N)}", + readKMMessage, + attr.attributeType.valueTypes.iosType, + Const.Message.Companion.WrapperDeserializationFunction.NAME + ) + + ProtoType.ENUM -> addReadEnumCode(attr.attributeType.valueTypes) + ProtoType.MAP -> throw IllegalStateException() + } + addCode(")\n") + } + } + } + + message.oneOfs.forEach { protoOneOf -> + protoOneOf.attributes.forEach { attr -> + addCode(getScalarAndRepeatedTagCode(attr)) + addCode( + "·->·%N·=·%T(", + getOneOfVarName(protoOneOf), + Const.Message.OneOf.childClassName(message, protoOneOf, attr) + ) + addCode(getScalarReadFieldCode(attr)) + addCode(")\n") + } + } + + endControlFlow() + + endControlFlow() + + addCode("return %T(", message.iosType) + message.attributes.filter { !it.isOneOfAttribute }.forEach { attr -> + addCode( + "%N·=·%N, ", + Const.Message.Attribute.propertyName(message, attr), + getAttrVarName(attr) + ) + } + + message.oneOfs.forEach { oneOf -> + addCode( + "%N·=·%N, ", + Const.Message.OneOf.propertyName(message, oneOf), + getOneOfVarName(oneOf) + ) + } + + addCode(")\n") + } + } + + private fun getDataTypeForProtoType(protoType: ProtoType) = + when (protoType) { + ProtoType.DOUBLE -> "DOUBLE" + ProtoType.FLOAT -> "FLOAT" + ProtoType.INT_32 -> "INT32" + ProtoType.INT_64 -> "INT64" + ProtoType.BOOL -> "BOOL" + ProtoType.STRING -> "STRING" + ProtoType.MESSAGE -> "MESSAGE" + ProtoType.ENUM -> "ENUM" + ProtoType.MAP -> throw IllegalStateException() + } + + protected fun isAttrPackable(attr: ProtoMessageAttribute) = when (attr.types.protoType) { + ProtoType.DOUBLE, + ProtoType.FLOAT, + ProtoType.INT_32, + ProtoType.INT_64, + ProtoType.BOOL, + ProtoType.ENUM -> true + + ProtoType.MESSAGE, ProtoType.STRING -> false + ProtoType.MAP -> throw IllegalStateException() + } + + companion object { + fun getWriteScalarFieldCode( + message: ProtoMessage, + attr: ProtoMessageAttribute, + streamParam: String, + performIsMessageSetCheck: Boolean + ): CodeBlock = + when (attr.types.protoType) { + ProtoType.DOUBLE, + ProtoType.FLOAT, + ProtoType.INT_32, + ProtoType.INT_64, + ProtoType.BOOL, + ProtoType.STRING -> { + val functionName = getWriteScalarFunctionName(attr.types.protoType) + + CodeBlock.of( + "%N.%N(%L, %N)\n", + streamParam, + functionName, + attr.protoId, + attr.name + ) + } + + ProtoType.MESSAGE -> { + CodeBlock.builder().apply { + if (performIsMessageSetCheck) { + beginControlFlow( + "if (%N)", + Const.Message.Attribute.Scalar.IosJvm.isMessageSetFunctionName( + message, + attr + ) + ) + } + + addStatement( + "%N.writeMessage(%L, %N)", + streamParam, + attr.protoId, + attr.name + ) + + if (performIsMessageSetCheck) endControlFlow() + }.build() + } + + ProtoType.ENUM -> { + CodeBlock.of( + "%N.writeEnum(%L, %N.%N)\n", + streamParam, + attr.protoId, + attr.name, + Const.Enum.VALUE_PROPERTY_NAME + ) + } + + ProtoType.MAP -> throw IllegalStateException() + } + + private fun getWriteScalarFunctionName(protoType: ProtoType) = + when (protoType) { + ProtoType.DOUBLE -> "writeDouble" + ProtoType.FLOAT -> "writeFloat" + ProtoType.INT_32 -> "writeInt32" + ProtoType.INT_64 -> "writeInt64" + ProtoType.BOOL -> "writeBool" + ProtoType.STRING -> "writeString" + ProtoType.ENUM -> "writeEnum" + ProtoType.MAP, ProtoType.MESSAGE -> throw IllegalStateException() + } + } +} \ No newline at end of file diff --git a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/proto_file/CommonProtoFileWriter.kt b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/proto_file/CommonProtoFileWriter.kt index 20c44df..88d2e4d 100644 --- a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/proto_file/CommonProtoFileWriter.kt +++ b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/proto_file/CommonProtoFileWriter.kt @@ -17,8 +17,7 @@ import java.io.File /** * File writer that writes Kotlin Multiplatform Proto Files. These are platform independent. */ -class CommonProtoFileWriter(private val protoFile: ProtoFile) : ProtoFileWriter(protoFile, false), - DefaultChildClassName { +class CommonProtoFileWriter(private val protoFile: ProtoFile) : ProtoFileWriter(protoFile, false) { override val scalarMessageMethodGenerator: ScalarMessageMethodGenerator = CommonScalarMessageMethodGenerator @@ -50,9 +49,6 @@ class CommonProtoFileWriter(private val protoFile: ProtoFile) : ProtoFileWriter( override fun applyToClass(builder: TypeSpec.Builder, message: ProtoMessage, messageClassName: ClassName) = Unit - override fun getChildClassName(parentClass: ClassName?, childName: String): ClassName = - getChildClassName(parentClass, childName, protoFile.pkg) - override fun applyToEqualsFunction( builder: FunSpec.Builder, message: ProtoMessage, diff --git a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/proto_file/IosJvmProtoFileWriteBase.kt b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/proto_file/IosJvmProtoFileWriteBase.kt index ead673b..a16a4b1 100644 --- a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/proto_file/IosJvmProtoFileWriteBase.kt +++ b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/proto_file/IosJvmProtoFileWriteBase.kt @@ -6,147 +6,22 @@ import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatfor import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.* import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.Const -abstract class IosJvmProtoFileWriteBase(private val protoFile: ProtoFile) : - ProtoFileWriter(protoFile, isActual = true), DefaultChildClassName { +abstract class IosJvmProtoFileWriteBase(protoFile: ProtoFile) : ActualProtoFileWriter(protoFile) { - abstract val serializeFunctionCode: CodeBlock - abstract val serializedDataType: ClassName + override fun applyToClass( + builder: TypeSpec.Builder, + message: ProtoMessage, + messageClassName: ClassName + ) { + super.applyToClass(builder, message, messageClassName) - abstract val deserializeFunctionCode: CodeBlock - - override fun applyToClass(builder: TypeSpec.Builder, message: ProtoMessage, messageClassName: ClassName) { builder.apply { - primaryConstructor( - FunSpec - .constructorBuilder() - .apply { - //one of attributes do not get a parameter, as they get the one of parameter - message.attributes.filter { !it.isOneOfAttribute }.forEach { attr -> - when (attr.attributeType) { - is Scalar -> { - if (attr.types.isNullable) { - addParameter( - ParameterSpec.builder(attr.name, attr.commonType.copy(nullable = true)) - .defaultValue("null") - .build() - ) - } else { - addParameter( - ParameterSpec - .builder(attr.name, attr.commonType) - .defaultValue(attr.commonDefaultValue(false, useEmptyMessage = false)) - .build() - ) - } - } - - is Repeated -> { - addParameter( - ParameterSpec - .builder( - Const.Message.Attribute.Repeated.listPropertyName(attr), - LIST.parameterizedBy(attr.commonType) - ) - .defaultValue("emptyList()") - .build() - ) - } - - is MapType -> { - addParameter( - ParameterSpec - .builder( - Const.Message.Attribute.propertyName(message, attr), - MAP.parameterizedBy( - attr.attributeType.keyTypes.iosType, - attr.attributeType.valueTypes.iosType - ) - ) - .defaultValue("emptyMap()") - .build() - ) - } - } - } - - message.oneOfs.forEach { oneOf -> - addParameter( - ParameterSpec - .builder( - Const.Message.OneOf.propertyName(message, oneOf), - Const.Message.OneOf.parentSealedClassName(message, oneOf) - ) - .defaultValue(oneOf.defaultValue(message)) - .build() - ) - } - } - .build() - ) - - addSuperinterface(kmMessage) - addProperty( PropertySpec .builder("requiredSize", INT, KModifier.OVERRIDE) .initializer(buildRequiredSizeInitializer(message)) .build() ) - - addFunction( - FunSpec - .builder(Const.Message.IOS.SerializeFunction.NAME) - .addModifiers(KModifier.OVERRIDE) - .addParameter(Const.Message.IOS.SerializeFunction.STREAM_PARAM, CodedOutputStream) - .apply { buildSerializeFunction(this, message) } - .build() - ) - - addFunction( - FunSpec - .builder("serialize") - .addModifiers(KModifier.OVERRIDE) - .returns(serializedDataType) - .addCode( - serializeFunctionCode - ) - - .build() - ) - - addType( - TypeSpec - .companionObjectBuilder() - .addSuperinterface(MessageDeserializer.parameterizedBy(messageClassName, serializedDataType)) - .addFunction( - //The function that builds the message from NSData - FunSpec - .builder(Const.Message.Companion.IOS.DataDeserializationFunction.NAME) - .addModifiers(KModifier.OVERRIDE) - .addParameter( - Const.Message.Companion.IOS.DataDeserializationFunction.DATA_PARAM, - serializedDataType - ) - .addCode( - deserializeFunctionCode - ) - .returns(message.commonType) - .build() - ) - .addFunction( - //The function that builds the message from a stream. - FunSpec - .builder(Const.Message.Companion.IOS.WrapperDeserializationFunction.NAME) - .addParameter( - Const.Message.Companion.IOS.WrapperDeserializationFunction.STREAM_PARAM, - CodedInputStream - ) - .returns(message.iosType) - .apply { buildWrapperDeserializationFunction(this, message) } - .build() - ) - .build() - ) } } @@ -168,9 +43,14 @@ abstract class IosJvmProtoFileWriteBase(private val protoFile: ProtoFile) : } is Repeated -> { - val attrFieldName = Const.Message.Attribute.Repeated.listPropertyName(attr) + val attrFieldName = + Const.Message.Attribute.Repeated.listPropertyName(attr) - add("if (%N.isEmpty()) 0 else %N.let·{·e ->\n", attrFieldName, attrFieldName) + add( + "if (%N.isEmpty()) 0 else %N.let·{·e ->\n", + attrFieldName, + attrFieldName + ) beginControlFlow("val dataSize = e.sumOf·{") when (attr.types.protoType) { ProtoType.DOUBLE, @@ -205,7 +85,10 @@ abstract class IosJvmProtoFileWriteBase(private val protoFile: ProtoFile) : val isPackable = isAttrPackable(attr) if (isPackable) { - addStatement("dataSize + tagSize + %M(tagSize)", ComputeInt32SizeNoTag) + addStatement( + "dataSize + tagSize + %M(tagSize)", + ComputeInt32SizeNoTag + ) } else { addStatement("dataSize + %N.size * tagSize", attrFieldName) } @@ -219,7 +102,10 @@ abstract class IosJvmProtoFileWriteBase(private val protoFile: ProtoFile) : computeMapSize, attr.protoId, Const.Message.Attribute.propertyName(message, attr), - getComputeDataTypeSizeMember(attr.attributeType.keyTypes.protoType, true) + getComputeDataTypeSizeMember( + attr.attributeType.keyTypes.protoType, + true + ) ) add(getComputeMapValueRequiredSizeCode(attr.attributeType.valueTypes)) @@ -246,532 +132,36 @@ abstract class IosJvmProtoFileWriteBase(private val protoFile: ProtoFile) : .build() } - /** - * Generate the serialize function, adds a write call for each attribute - */ - private fun buildSerializeFunction(builder: FunSpec.Builder, message: ProtoMessage) { + override fun buildMapAttributeSerializeCode( + builder: FunSpec.Builder, + message: ProtoMessage, + attr: ProtoMessageAttribute, + mapType: MapType + ) { builder.apply { - message.attributes.filter { !it.isOneOfAttribute }.forEach { attr -> - when (attr.attributeType) { - is Scalar -> { - addCode("if·(%N·!=·", Const.Message.Attribute.propertyName(message, attr)) - addCode(attr.commonDefaultValue(false, useEmptyMessage = false)) - beginControlFlow(")·{ ") - addCode( - getWriteScalarFieldCode( - message, - attr, - Const.Message.IOS.SerializeFunction.STREAM_PARAM, - performIsMessageSetCheck = true - ) - ) - endControlFlow() - } - - is Repeated -> { - val writeListPartialFunctionName = when (attr.types.protoType) { - ProtoType.DOUBLE -> "Double" - ProtoType.FLOAT -> "Float" - ProtoType.INT_32 -> "Int32" - ProtoType.INT_64 -> "Int64" - ProtoType.BOOL -> "Bool" - ProtoType.STRING -> "String" - ProtoType.MESSAGE -> "Message" - ProtoType.ENUM -> "Enum" - ProtoType.MAP -> throw IllegalStateException() - } - - val writeArrayFunction = "write${writeListPartialFunctionName}Array" - - when (attr.types.protoType) { - ProtoType.DOUBLE, - ProtoType.FLOAT, - ProtoType.INT_32, - ProtoType.INT_64, - ProtoType.BOOL -> { - //Write packed. - // From GPBDescriptor.m: GPBWireFormatForType(description->dataType, - // ((description->flags & GPBFieldPacked) != 0)) - addStatement( - "%N.%N(%L, %N, %M(%L, %M(%T.%N, true)).toUInt())", - Const.Message.IOS.SerializeFunction.STREAM_PARAM, - writeArrayFunction, - attr.protoId, - Const.Message.Attribute.Repeated.listPropertyName(attr), - WireFormatMakeTag, - attr.protoId, - WireFormatForType, - DataType, - getDataTypeForProtoType(attr.types.protoType) - ) - } - - ProtoType.ENUM -> { - addStatement( - "%N.%N(%L, %N.map·{ it.%N }, %M(%L, %M(%T.%N, true)).toUInt())", - Const.Message.IOS.SerializeFunction.STREAM_PARAM, - writeArrayFunction, - attr.protoId, - Const.Message.Attribute.Repeated.listPropertyName(attr), - Const.Enum.VALUE_PROPERTY_NAME, - WireFormatMakeTag, - attr.protoId, - WireFormatForType, - DataType, - getDataTypeForProtoType(attr.types.protoType) - ) - } - - ProtoType.STRING -> { - //Write unpacked. 0 means unpacked - addStatement( - "%N.%N(%L, %N)", - Const.Message.IOS.SerializeFunction.STREAM_PARAM, - writeArrayFunction, - attr.protoId, - Const.Message.Attribute.propertyName(message, attr) - ) - } - - ProtoType.MESSAGE -> { - addStatement( - "%N.%N(%L, %N)", - Const.Message.IOS.SerializeFunction.STREAM_PARAM, - writeArrayFunction, - attr.protoId, - Const.Message.Attribute.propertyName(message, attr) - ) - } - - ProtoType.MAP -> throw IllegalStateException() - } - } - - is MapType -> { - addCode( - "%M(%N, %L, %N, ::%M, ", - writeMap, - Const.Message.IOS.SerializeFunction.STREAM_PARAM, - attr.protoId, - Const.Message.Attribute.propertyName(message, attr), - getComputeDataTypeSizeMember(attr.attributeType.keyTypes.protoType, true), - ) - - addCode(getComputeMapValueRequiredSizeCode(attr.attributeType.valueTypes)) - - addCode(", ") - - val addWriteScalarCode = { types: Types -> - addCode( - "%T::%N", - CodedOutputStream, - getWriteScalarFunctionName(types.protoType) - ) - } - - val addWriteEnumCode = { - addCode( - "{·fieldNumber,·it·-> writeEnum(fieldNumber, it.%N)·}", - Const.Enum.VALUE_PROPERTY_NAME - ) - } - - when (attr.attributeType.keyTypes.protoType) { - ProtoType.INT_32, - ProtoType.INT_64, - ProtoType.BOOL, - ProtoType.STRING -> addWriteScalarCode(attr.attributeType.keyTypes) - - ProtoType.DOUBLE, ProtoType.FLOAT, ProtoType.MESSAGE, ProtoType.MAP, ProtoType.ENUM -> throw IllegalStateException( - "Illegal Map Key Type: ${attr.attributeType.keyTypes.protoType}" - ) - } - - addCode(", ") - - when (attr.attributeType.valueTypes.protoType) { - ProtoType.DOUBLE, - ProtoType.FLOAT, - ProtoType.INT_32, - ProtoType.INT_64, - ProtoType.BOOL, - ProtoType.STRING -> addWriteScalarCode(attr.attributeType.valueTypes) - - ProtoType.ENUM -> addWriteEnumCode() - ProtoType.MESSAGE -> addCode( - "{·fieldNumber,·msg·-> %N.writeMessage(fieldNumber, msg)·}", - Const.Message.IOS.SerializeFunction.STREAM_PARAM - ) - - ProtoType.MAP -> throw IllegalStateException() - } - - addCode(")\n") - } - } - } - - message.oneOfs.forEach { oneOf -> - addStatement( - "%N.%N(%N)", - Const.Message.OneOf.propertyName(message, oneOf), - Const.Message.OneOf.IosJvm.SERIALIZE_FUNCTION_NAME, - Const.Message.IOS.SerializeFunction.STREAM_PARAM - ) - } - } - } - - private fun buildWrapperDeserializationFunction(builder: FunSpec.Builder, message: ProtoMessage) { - val wrapperParamName = Const.Message.Companion.IOS.WrapperDeserializationFunction.STREAM_PARAM - - val getOneOfVarName = { oneOf: ProtoOneOf -> oneOf.name } - - val getAttrVarName = { attr: ProtoMessageAttribute -> - when (attr.attributeType) { - is MapType -> "${attr.name}Map" - is Repeated -> "${attr.name}List" - is Scalar -> attr.name - } - } - - builder.apply { - message.attributes.filter { !it.isOneOfAttribute }.forEach { attr -> - val (type, mutable) = when (attr.attributeType) { - is Scalar -> attr.types.iosType.copy(nullable = attr.types.isNullable) to true - is Repeated -> MUTABLE_LIST.parameterizedBy(attr.types.iosType) to false - is MapType -> MUTABLE_MAP.parameterizedBy( - attr.attributeType.keyTypes.iosType, - attr.attributeType.valueTypes.iosType - ) to false - } - - addCode( - if (mutable) "var %N: %T = " else "val %N: %T = ", - getAttrVarName(attr), - type - ) - addCode(attr.commonDefaultValue(true, useEmptyMessage = false)) - addCode("\n") - } - - message.oneOfs.forEach { oneOf -> - addCode( - "var %N: %T = ", - getOneOfVarName(oneOf), - Const.Message.OneOf.parentSealedClassName(message, oneOf) - ) - - addCode(oneOf.defaultValue(message)) - addCode("\n") - } - - beginControlFlow("while (true)") - addStatement( - "val tag = %N.readTag()", - wrapperParamName + addCode( + "%M(%N, %L, %N, ::%M, ", + writeMap, + Const.Message.IOS.SerializeFunction.STREAM_PARAM, + attr.protoId, + Const.Message.Attribute.propertyName(message, attr), + getComputeDataTypeSizeMember( + mapType.keyTypes.protoType, + true + ), ) - addStatement("if (tag == 0) break") - - beginControlFlow("when (tag)") - - val getScalarAndRepeatedTagCode = { attr: ProtoMessageAttribute -> - val isPacked = isAttrPackable(attr) && attr.attributeType is Repeated - - val dataType = getDataTypeForProtoType(attr.types.protoType) - - CodeBlock.of( - "%M(%L, %M(%T.%N, %L)).toInt()", - WireFormatMakeTag, - attr.protoId, - WireFormatForType, - DataType, - dataType, - isPacked - ) - } - - //The function name of that reads the type from the input stream - val getScalarReadFunctionName = { protoType: ProtoType -> - when (protoType) { - ProtoType.DOUBLE -> "readDouble" - ProtoType.FLOAT -> "readFloat" - ProtoType.INT_32 -> "readInt32" - ProtoType.INT_64 -> "readInt64" - ProtoType.BOOL -> "readBool" - ProtoType.STRING -> "readString" - ProtoType.ENUM, ProtoType.MAP, ProtoType.MESSAGE -> throw IllegalStateException() - } - } - - val getScalarReadFieldCode = { attr: ProtoMessageAttribute -> - when (attr.types.protoType) { - ProtoType.DOUBLE, - ProtoType.FLOAT, - ProtoType.INT_32, - ProtoType.INT_64, - ProtoType.BOOL, - ProtoType.STRING -> { - CodeBlock.of( - "%N.%N()", - wrapperParamName, - getScalarReadFunctionName(attr.types.protoType) - ) - } - - ProtoType.MESSAGE -> CodeBlock.of( - "%M(%N, %T.Companion::%N)", - readKMMessage, - wrapperParamName, - attr.types.iosType, - Const.Message.Companion.IOS.WrapperDeserializationFunction.NAME - ) - ProtoType.ENUM -> CodeBlock.of( - "%T.%N(%N.readEnum())", - attr.types.iosType, - Const.Enum.getEnumForNumFunctionName, - wrapperParamName - ) + addCode(getComputeMapValueRequiredSizeCode(mapType.valueTypes)) - else -> throw IllegalStateException() - } - } - - message.attributes.filter { !it.isOneOfAttribute }.forEach { attr -> - when (attr.attributeType) { - is Scalar, is Repeated -> { - addCode(getScalarAndRepeatedTagCode(attr)) - } - - is MapType -> addCode( - "%M(%L, %M(%T.%N, false)).toInt()", - WireFormatMakeTag, - attr.protoId, - WireFormatForType, - DataType, - "MESSAGE" - ) - } - - addCode(" -> ") - - when (attr.attributeType) { - is Scalar -> { - addCode("%N·=·", getAttrVarName(attr)) - addCode(getScalarReadFieldCode(attr)) - addCode("\n") - } - - is Repeated -> { - beginControlFlow("{") - when (attr.types.protoType) { - ProtoType.DOUBLE, - ProtoType.FLOAT, - ProtoType.INT_32, - ProtoType.INT_64, - ProtoType.BOOL, - ProtoType.ENUM -> { - //All packable - addStatement( - "val length·=·%N.readInt32()", - wrapperParamName - ) - addStatement( - "val limit·=·%N.pushLimit(length)", - wrapperParamName - ) - beginControlFlow( - "while·(!%N.isAtEnd)·{", - wrapperParamName - ) - //Enums need to first get mapped - if (attr.types.protoType == ProtoType.ENUM) { - addStatement( - "%N += %T.%N(%N.readEnum())", - getAttrVarName(attr), - attr.types.commonType, - Const.Enum.getEnumForNumFunctionName, - wrapperParamName - ) - } else { - val functionName = getScalarReadFunctionName(attr.types.protoType) - - addStatement( - "%N·+=·%N.%N()", - getAttrVarName(attr), - wrapperParamName, - functionName - ) - } - - endControlFlow() - - addStatement("%N.popLimit(limit)", wrapperParamName) - } - - ProtoType.MESSAGE -> addCode( - "%N·+=·%M(%N, %T.Companion::%N)\n", - getAttrVarName(attr), - readKMMessage, - wrapperParamName, - attr.types.iosType, - Const.Message.Companion.IOS.WrapperDeserializationFunction.NAME - ) - - ProtoType.STRING -> addCode( - "%N·+=·%N.readString()", - getAttrVarName(attr), - wrapperParamName - ) - - ProtoType.MAP -> throw IllegalStateException() - } - endControlFlow() - } - - is MapType -> { - addCode( - "%M(%N, %N, %T.%N, %T.%N, ", - readMapEntry, - Const.Message.Companion.IOS.WrapperDeserializationFunction.STREAM_PARAM, - getAttrVarName(attr), - DataType, - getDataTypeForProtoType(attr.attributeType.keyTypes.protoType), - DataType, - getDataTypeForProtoType(attr.attributeType.valueTypes.protoType), - ) - - val getDefaultEntry = { types: Types -> - when (types.protoType) { - ProtoType.DOUBLE -> CodeBlock.of("0.0") - ProtoType.FLOAT -> CodeBlock.of("0f") - ProtoType.INT_32 -> CodeBlock.of("0") - ProtoType.INT_64 -> CodeBlock.of("0L") - ProtoType.BOOL -> CodeBlock.of("false") - ProtoType.STRING -> CodeBlock.of("\"\"") - ProtoType.MAP -> throw IllegalStateException() - ProtoType.MESSAGE -> CodeBlock.of("%T()", types.iosType) - ProtoType.ENUM -> CodeBlock.of( - "%T.%N(0)", - types.iosType, - Const.Enum.getEnumForNumFunctionName - ) - } - } - - val addReadScalarCode = { types: Types -> - addCode( - "{·%N()·}", - getScalarReadFunctionName(types.protoType) - ) - } - - val addReadEnumCode = { types: Types -> - addCode( - "{·%T.%N(readEnum())·}", - types.iosType, - Const.Enum.getEnumForNumFunctionName - ) - } - - //Write default values - addCode(getDefaultEntry(attr.attributeType.keyTypes)) - addCode(", ") - addCode(getDefaultEntry(attr.attributeType.valueTypes)) - addCode(", ") - - when (attr.attributeType.keyTypes.protoType) { - ProtoType.INT_32, - ProtoType.INT_64, - ProtoType.BOOL, - ProtoType.STRING -> addReadScalarCode(attr.attributeType.keyTypes) - - ProtoType.ENUM, - ProtoType.MESSAGE, - ProtoType.MAP, - ProtoType.DOUBLE, - ProtoType.FLOAT -> throw IllegalStateException("Illegal key types") - } - addCode(", ") - when (attr.attributeType.valueTypes.protoType) { - ProtoType.DOUBLE, - ProtoType.FLOAT, - ProtoType.INT_32, - ProtoType.INT_64, - ProtoType.BOOL, - ProtoType.STRING -> addReadScalarCode(attr.attributeType.valueTypes) - - ProtoType.MESSAGE -> addCode( - "{·%M(this, %T.Companion::%N)}", - readKMMessage, - attr.attributeType.valueTypes.iosType, - Const.Message.Companion.IOS.WrapperDeserializationFunction.NAME - ) - - ProtoType.ENUM -> addReadEnumCode(attr.attributeType.valueTypes) - ProtoType.MAP -> throw IllegalStateException() - } - addCode(")\n") - } - } - } - - message.oneOfs.forEach { protoOneOf -> - protoOneOf.attributes.forEach { attr -> - addCode(getScalarAndRepeatedTagCode(attr)) - addCode( - "·->·%N·=·%T(", - getOneOfVarName(protoOneOf), - Const.Message.OneOf.childClassName(message, protoOneOf, attr) - ) - addCode(getScalarReadFieldCode(attr)) - addCode(")\n") - } - } - - endControlFlow() - - endControlFlow() - - addCode("return %T(", message.iosType) - message.attributes.filter { !it.isOneOfAttribute }.forEach { attr -> - addCode("%N·=·%N, ", Const.Message.Attribute.propertyName(message, attr), getAttrVarName(attr)) - } - - message.oneOfs.forEach { oneOf -> - addCode("%N·=·%N, ", Const.Message.OneOf.propertyName(message, oneOf), getOneOfVarName(oneOf)) - } + addCode(", ") + addMapKeyTypeSerializationCode(mapType.keyTypes) + addCode(", ") + addMapValueTypeSerializationCode(mapType.valueTypes) addCode(")\n") } } - private fun isAttrPackable(attr: ProtoMessageAttribute) = when (attr.types.protoType) { - ProtoType.DOUBLE, - ProtoType.FLOAT, - ProtoType.INT_32, - ProtoType.INT_64, - ProtoType.BOOL, - ProtoType.ENUM -> true - - ProtoType.MESSAGE, ProtoType.STRING -> false - ProtoType.MAP -> throw IllegalStateException() - } - - private fun getDataTypeForProtoType(protoType: ProtoType) = - when (protoType) { - ProtoType.DOUBLE -> "DOUBLE" - ProtoType.FLOAT -> "FLOAT" - ProtoType.INT_32 -> "INT32" - ProtoType.INT_64 -> "INT64" - ProtoType.BOOL -> "BOOL" - ProtoType.STRING -> "STRING" - ProtoType.MESSAGE -> "MESSAGE" - ProtoType.ENUM -> "ENUM" - ProtoType.MAP -> throw IllegalStateException() - } - private fun getComputeMapValueRequiredSizeCode(valueTypes: Types): CodeBlock { return when (valueTypes.protoType) { ProtoType.DOUBLE, @@ -798,7 +188,10 @@ abstract class IosJvmProtoFileWriteBase(private val protoFile: ProtoFile) : /** * @return the function that compute the size of the data type. */ - private fun getComputeDataTypeSizeMember(protoType: ProtoType, withTag: Boolean): MemberName { + private fun getComputeDataTypeSizeMember( + protoType: ProtoType, + withTag: Boolean + ): MemberName { return when (protoType) { ProtoType.DOUBLE, ProtoType.FLOAT, @@ -861,77 +254,5 @@ abstract class IosJvmProtoFileWriteBase(private val protoFile: ProtoFile) : ProtoType.MAP -> throw IllegalStateException() } - - fun getWriteScalarFieldCode( - message: ProtoMessage, - attr: ProtoMessageAttribute, - streamParam: String, - performIsMessageSetCheck: Boolean - ): CodeBlock = - when (attr.types.protoType) { - ProtoType.DOUBLE, - ProtoType.FLOAT, - ProtoType.INT_32, - ProtoType.INT_64, - ProtoType.BOOL, - ProtoType.STRING -> { - val functionName = getWriteScalarFunctionName(attr.types.protoType) - - CodeBlock.of( - "%N.%N(%L, %N)\n", - streamParam, - functionName, - attr.protoId, - attr.name - ) - } - - ProtoType.MESSAGE -> { - CodeBlock.builder().apply { - if (performIsMessageSetCheck) { - beginControlFlow( - "if (%N)", - Const.Message.Attribute.Scalar.IosJvm.isMessageSetFunctionName(message, attr) - ) - } - - addStatement( - "%N.writeMessage(%L, %N)", - streamParam, - attr.protoId, - attr.name - ) - - if (performIsMessageSetCheck) endControlFlow() - }.build() - } - - ProtoType.ENUM -> { - CodeBlock.of( - "%N.writeEnum(%L, %N.%N)\n", - streamParam, - attr.protoId, - attr.name, - Const.Enum.VALUE_PROPERTY_NAME - ) - } - - ProtoType.MAP -> throw IllegalStateException() - } - - private fun getWriteScalarFunctionName(protoType: ProtoType) = - when (protoType) { - ProtoType.DOUBLE -> "writeDouble" - ProtoType.FLOAT -> "writeFloat" - ProtoType.INT_32 -> "writeInt32" - ProtoType.INT_64 -> "writeInt64" - ProtoType.BOOL -> "writeBool" - ProtoType.STRING -> "writeString" - ProtoType.ENUM -> "writeEnum" - ProtoType.MAP, ProtoType.MESSAGE -> throw IllegalStateException() - } } - - override fun getChildClassName(parentClass: ClassName?, childName: String): ClassName = - getChildClassName(parentClass, childName, protoFile.pkg) } \ No newline at end of file diff --git a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/proto_file/IOSProtoFileWriter.kt b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/proto_file/IosProtoFileWriter.kt similarity index 84% rename from plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/proto_file/IOSProtoFileWriter.kt rename to plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/proto_file/IosProtoFileWriter.kt index 4152d26..9674ae9 100644 --- a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/proto_file/IOSProtoFileWriter.kt +++ b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/proto_file/IosProtoFileWriter.kt @@ -4,25 +4,25 @@ import com.squareup.kotlinpoet.* import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.* import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.* import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.Const -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.map.IosJvmMapMessageMethodGenerator +import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.map.ActualMapMessageMethodGenerator import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.map.MapMessageMethodGenerator import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.oneof.IosJvmOneOfMethodAndClassGenerator import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.oneof.OneOfMethodAndClassGenerator -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.repeated.IosJvmRepeatedMessageMethodGenerator +import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.repeated.ActualRepeatedMessageMethodGenerator import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.repeated.RepeatedMessageMethodGenerator -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.scalar.IosJvmScalarMessageMethodGenerator +import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.scalar.ActualScalarMessageMethodGenerator import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.scalar.ScalarMessageMethodGenerator -class IOSProtoFileWriter(protoFile: ProtoFile) : IosJvmProtoFileWriteBase(protoFile) { +class IosProtoFileWriter(protoFile: ProtoFile) : IosJvmProtoFileWriteBase(protoFile) { override val scalarMessageMethodGenerator: ScalarMessageMethodGenerator - get() = IosJvmScalarMessageMethodGenerator + get() = ActualScalarMessageMethodGenerator override val repeatedMessageMethodGenerator: RepeatedMessageMethodGenerator - get() = IosJvmRepeatedMessageMethodGenerator + get() = ActualRepeatedMessageMethodGenerator override val oneOfMethodAndClassGenerator: OneOfMethodAndClassGenerator get() = IosJvmOneOfMethodAndClassGenerator override val mapMessageMethodGenerator: MapMessageMethodGenerator - get() = IosJvmMapMessageMethodGenerator + get() = ActualMapMessageMethodGenerator override val serializeFunctionCode: CodeBlock @@ -49,7 +49,7 @@ class IOSProtoFileWriter(protoFile: ProtoFile) : IosJvmProtoFileWriteBase(protoF ) .addStatement( "return %N(stream)", - Const.Message.Companion.IOS.WrapperDeserializationFunction.NAME + Const.Message.Companion.WrapperDeserializationFunction.NAME ) .build() } \ No newline at end of file diff --git a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/proto_file/JsProtoFileWriter.kt b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/proto_file/JsProtoFileWriter.kt index 75f73e9..4fdc0aa 100644 --- a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/proto_file/JsProtoFileWriter.kt +++ b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/proto_file/JsProtoFileWriter.kt @@ -1,44 +1,82 @@ package io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.proto_file -import com.squareup.kotlinpoet.* -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.* +import com.squareup.kotlinpoet.CodeBlock +import com.squareup.kotlinpoet.Dynamic +import com.squareup.kotlinpoet.FunSpec +import com.squareup.kotlinpoet.TypeName +import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.CodedInputStream +import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.CodedOutputStream +import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.JSPB_BINARY_READER +import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.JSPB_BINARY_WRITER +import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.MapType +import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.ProtoFile +import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.ProtoMessage +import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.ProtoMessageAttribute import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.Const -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.map.JsMapMessageMethodGenerator +import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.map.ActualMapMessageMethodGenerator import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.map.MapMessageMethodGenerator import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.oneof.JsOneOfMethodAndClassGenerator import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.oneof.OneOfMethodAndClassGenerator -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.repeated.JsRepeatedMessageMethodGenerator +import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.repeated.ActualRepeatedMessageMethodGenerator import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.repeated.RepeatedMessageMethodGenerator -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.scalar.JsScalarMessageMethodGenerator +import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.scalar.ActualScalarMessageMethodGenerator import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.scalar.ScalarMessageMethodGenerator +import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.writeMap -class JsProtoFileWriter(private val protoFile: ProtoFile) : ProtoFileWriter(protoFile, true), DefaultChildClassName { - override val scalarMessageMethodGenerator: ScalarMessageMethodGenerator = JsScalarMessageMethodGenerator +class JsProtoFileWriter(protoFile: ProtoFile) : ActualProtoFileWriter(protoFile) { - override val repeatedMessageMethodGenerator: RepeatedMessageMethodGenerator = JsRepeatedMessageMethodGenerator + override val scalarMessageMethodGenerator: ScalarMessageMethodGenerator + get() = ActualScalarMessageMethodGenerator + override val repeatedMessageMethodGenerator: RepeatedMessageMethodGenerator + get() = ActualRepeatedMessageMethodGenerator + override val oneOfMethodAndClassGenerator: OneOfMethodAndClassGenerator + get() = JsOneOfMethodAndClassGenerator + override val mapMessageMethodGenerator: MapMessageMethodGenerator + get() = ActualMapMessageMethodGenerator - override val oneOfMethodAndClassGenerator: OneOfMethodAndClassGenerator = JsOneOfMethodAndClassGenerator + override val serializeFunctionCode: CodeBlock + get() = CodeBlock.builder() + .addStatement("val writer = %T()", JSPB_BINARY_WRITER) + .addStatement("val stream = %T(writer)", CodedOutputStream) + .addStatement("serialize(stream)") + .addStatement("return writer.getResultBuffer()") + .build() - override val mapMessageMethodGenerator: MapMessageMethodGenerator = JsMapMessageMethodGenerator + override val serializedDataType: TypeName = Dynamic - override fun applyToClass(builder: TypeSpec.Builder, message: ProtoMessage, messageClassName: ClassName) { - val paramName = Const.Message.Constructor.JS.PARAM_IMPL + override val deserializeFunctionCode: CodeBlock + get() = CodeBlock + .builder() + .addStatement( + "val stream = %T(%T(data))", + CodedInputStream, + JSPB_BINARY_READER + ) + .addStatement( + "return %N(stream)", + Const.Message.Companion.WrapperDeserializationFunction.NAME + ) + .build() - builder.addProperty( - PropertySpec - .builder(paramName, message.jsType, KModifier.PUBLIC, KModifier.OVERRIDE) - .initializer(paramName) - .build() - ) + override fun buildMapAttributeSerializeCode( + builder: FunSpec.Builder, + message: ProtoMessage, + attr: ProtoMessageAttribute, + mapType: MapType + ) { + builder.apply { + addCode( + "%M(%N, %L, %N, ", + writeMap, + Const.Message.IOS.SerializeFunction.STREAM_PARAM, + attr.protoId, + Const.Message.Attribute.propertyName(message, attr) + ) - builder.primaryConstructor( - FunSpec - .constructorBuilder() - .addParameter(paramName, message.jsType) - .build() - ) + addMapKeyTypeSerializationCode(mapType.keyTypes) + addCode(", ") + addMapValueTypeSerializationCode(mapType.valueTypes) + addCode(")\n") + } } - - override fun getChildClassName(parentClass: ClassName?, childName: String): ClassName = - getChildClassName(parentClass, childName, protoFile.pkg) } \ No newline at end of file diff --git a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/proto_file/JvmProtoFileWriter.kt b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/proto_file/JvmProtoFileWriter.kt index 03b4b73..7b2cfd4 100644 --- a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/proto_file/JvmProtoFileWriter.kt +++ b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/proto_file/JvmProtoFileWriter.kt @@ -7,26 +7,25 @@ import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatfor import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.ProtoFile import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.ProtoMessage import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.Const -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.map.IosJvmMapMessageMethodGenerator +import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.map.ActualMapMessageMethodGenerator import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.map.MapMessageMethodGenerator import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.oneof.IosJvmOneOfMethodAndClassGenerator import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.oneof.OneOfMethodAndClassGenerator -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.repeated.IosJvmRepeatedMessageMethodGenerator +import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.repeated.ActualRepeatedMessageMethodGenerator import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.repeated.RepeatedMessageMethodGenerator -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.scalar.IosJvmScalarMessageMethodGenerator +import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.scalar.ActualScalarMessageMethodGenerator import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.scalar.ScalarMessageMethodGenerator import java.nio.ByteBuffer -import kotlin.reflect.jvm.internal.impl.protobuf.CodedInputStream class JvmProtoFileWriter(protoFile: ProtoFile) : IosJvmProtoFileWriteBase(protoFile) { - override val scalarMessageMethodGenerator: ScalarMessageMethodGenerator = IosJvmScalarMessageMethodGenerator + override val scalarMessageMethodGenerator: ScalarMessageMethodGenerator = ActualScalarMessageMethodGenerator - override val repeatedMessageMethodGenerator: RepeatedMessageMethodGenerator = IosJvmRepeatedMessageMethodGenerator + override val repeatedMessageMethodGenerator: RepeatedMessageMethodGenerator = ActualRepeatedMessageMethodGenerator override val oneOfMethodAndClassGenerator: OneOfMethodAndClassGenerator = IosJvmOneOfMethodAndClassGenerator - override val mapMessageMethodGenerator: MapMessageMethodGenerator = IosJvmMapMessageMethodGenerator + override val mapMessageMethodGenerator: MapMessageMethodGenerator = ActualMapMessageMethodGenerator override val serializeFunctionCode: CodeBlock get() = CodeBlock.builder() @@ -53,7 +52,7 @@ class JvmProtoFileWriter(protoFile: ProtoFile) : IosJvmProtoFileWriteBase(protoF ) .addStatement( "return %N(stream)", - Const.Message.Companion.IOS.WrapperDeserializationFunction.NAME + Const.Message.Companion.WrapperDeserializationFunction.NAME ) .build() @@ -193,7 +192,7 @@ class JvmProtoFileWriter(protoFile: ProtoFile) : IosJvmProtoFileWriteBase(protoF ClassName(PACKAGE_PROTOBUF, "ExtensionRegistryLite").copy(nullable = true) ) .returns(messageClassName) - .addStatement("return deserialize(%T(input))", CodedInputStream) + .addStatement("return %N(%T(input))", Const.Message.Companion.WrapperDeserializationFunction.NAME, CodedInputStream) .build() ) .build() diff --git a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/proto_file/ProtoFileWriter.kt b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/proto_file/ProtoFileWriter.kt index 7bf94c6..fe7df64 100644 --- a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/proto_file/ProtoFileWriter.kt +++ b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/proto_file/ProtoFileWriter.kt @@ -1,6 +1,15 @@ package io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.proto_file import com.squareup.kotlinpoet.* +import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy +import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.CodedInputStream +import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.CodedOutputStream +import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.DataType +import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.MessageDeserializer +import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.ProtoType +import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.Types +import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.WireFormatForType +import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.WireFormatMakeTag import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.Const import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.map.MapMessageMethodGenerator import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.oneof.OneOfMethodAndClassGenerator @@ -9,8 +18,14 @@ import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatfor import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.unrecognizedEnumField import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.* import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.kmMessage +import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.readKMMessage +import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.readMapEntry +import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.writeMap import java.io.File +/** + * Generates the kotlin code for the .proto files. + */ abstract class ProtoFileWriter(private val protoFile: ProtoFile, private val isActual: Boolean) { abstract val scalarMessageMethodGenerator: ScalarMessageMethodGenerator @@ -42,7 +57,10 @@ abstract class ProtoFileWriter(private val protoFile: ProtoFile, private val isA /** * Recursive function that adds a proto message class. */ - private fun generateProtoMessageClass(parentClass: ClassName?, message: ProtoMessage): TypeSpec { + private fun generateProtoMessageClass( + parentClass: ClassName?, + message: ProtoMessage + ): TypeSpec { val isNested = parentClass != null val messageClassName = getChildClassName(parentClass, message.commonName) @@ -62,7 +80,11 @@ abstract class ProtoFileWriter(private val protoFile: ProtoFile, private val isA when (attr.attributeType) { is Scalar -> addSimpleMessageFunctions(this, message, attr) is Repeated -> addRepeatedMessageFunctions(this, message, attr) - is MapType -> mapMessageMethodGenerator.generateFunctions(this, message, attr) + is MapType -> mapMessageMethodGenerator.generateFunctions( + this, + message, + attr + ) } } @@ -72,7 +94,7 @@ abstract class ProtoFileWriter(private val protoFile: ProtoFile, private val isA applyToClass(this, message, messageClassName) - //Write child messages + // Write child messages message.children.forEach { childMessage -> addType( generateProtoMessageClass( @@ -94,7 +116,10 @@ abstract class ProtoFileWriter(private val protoFile: ProtoFile, private val isA //Write eq function addFunction( FunSpec.builder(Const.Message.BasicFunctions.EqualsFunction.NAME) - .addModifiers(if (isActual) KModifier.ACTUAL else KModifier.EXPECT, KModifier.OVERRIDE) + .addModifiers( + if (isActual) KModifier.ACTUAL else KModifier.EXPECT, + KModifier.OVERRIDE + ) .addParameter( Const.Message.BasicFunctions.EqualsFunction.OTHER_PARAM, ANY.copy(nullable = true) @@ -109,7 +134,10 @@ abstract class ProtoFileWriter(private val protoFile: ProtoFile, private val isA addFunction( FunSpec.builder(Const.Message.BasicFunctions.HashCodeFunction.NAME) .returns(INT) - .addModifiers(if (isActual) KModifier.ACTUAL else KModifier.EXPECT, KModifier.OVERRIDE) + .addModifiers( + if (isActual) KModifier.ACTUAL else KModifier.EXPECT, + KModifier.OVERRIDE + ) .apply { applyToHashCodeFunction(this, message) } @@ -239,7 +267,11 @@ abstract class ProtoFileWriter(private val protoFile: ProtoFile, private val isA repeatedMessageMethodGenerator.generateFunctions(builder, message, attr) } - private fun addOneOfEnumAndFunctions(builder: TypeSpec.Builder, message: ProtoMessage, oneOf: ProtoOneOf) { + private fun addOneOfEnumAndFunctions( + builder: TypeSpec.Builder, + message: ProtoMessage, + oneOf: ProtoOneOf + ) { oneOfMethodAndClassGenerator.generateMethodsAndClasses(builder, message, oneOf) } @@ -247,7 +279,8 @@ abstract class ProtoFileWriter(private val protoFile: ProtoFile, private val isA * @param parentClass the parent class, or null if this is a top level class. * @return the ClassName of the class. Should consider recursion, meaning that this child class could also be the child of another child class. */ - abstract fun getChildClassName(parentClass: ClassName?, childName: String): ClassName + private fun getChildClassName(parentClass: ClassName?, childName: String): ClassName = + parentClass?.nestedClass(childName) ?: ClassName(protoFile.pkg, childName) /** * @param getChildClassName a function that will return a child class name for the parent. So when the parent is a Class then diff --git a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/repeated/IosJvmRepeatedMessageMethodGenerator.kt b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/repeated/ActualRepeatedMessageMethodGenerator.kt similarity index 93% rename from plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/repeated/IosJvmRepeatedMessageMethodGenerator.kt rename to plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/repeated/ActualRepeatedMessageMethodGenerator.kt index 223b633..11ad2aa 100644 --- a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/repeated/IosJvmRepeatedMessageMethodGenerator.kt +++ b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/repeated/ActualRepeatedMessageMethodGenerator.kt @@ -7,7 +7,7 @@ import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatfor import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.ProtoMessageAttribute import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.Const -object IosJvmRepeatedMessageMethodGenerator : RepeatedMessageMethodGenerator(true) { +object ActualRepeatedMessageMethodGenerator : RepeatedMessageMethodGenerator(true) { override val attrs: List = listOf(KModifier.ACTUAL) diff --git a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/repeated/JsRepeatedMessageMethodGenerator.kt b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/repeated/JsRepeatedMessageMethodGenerator.kt deleted file mode 100644 index c91cc89..0000000 --- a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/repeated/JsRepeatedMessageMethodGenerator.kt +++ /dev/null @@ -1,42 +0,0 @@ -package io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.repeated - -import com.squareup.kotlinpoet.CodeBlock -import com.squareup.kotlinpoet.KModifier -import com.squareup.kotlinpoet.PropertySpec -import com.squareup.kotlinpoet.TypeName -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.ProtoType -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.ProtoMessage -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.ProtoMessageAttribute -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.Const - -object JsRepeatedMessageMethodGenerator : RepeatedMessageMethodGenerator(true) { - - override val attrs: List = listOf(KModifier.ACTUAL) - - override fun getType(messageAttribute: ProtoMessageAttribute): TypeName = messageAttribute.commonType - - override fun modifyListProperty(builder: PropertySpec.Builder, message: ProtoMessage, attr: ProtoMessageAttribute) { - val initBlock = CodeBlock.builder() - - initBlock.add( - "jsImpl.%N()", - Const.Message.Attribute.Repeated.JS.getListFunctionName(attr) - ) - - if (attr.types.isEnum) { - initBlock.add( - ".map·{ %T.%N(it) }.toList()", - attr.types.commonType, - Const.Enum.getEnumForNumFunctionName - ) - } else if (attr.types.doDiffer) { - initBlock.add(".map·{ %M(it) }.toList()", Const.Message.CommonFunction.JS.commonFunction(attr)) - } else if (attr.types.protoType == ProtoType.INT_64) { - initBlock.add(".map·{ it.toLong() }") - } else { - initBlock.add(".toList()") - } - - builder.initializer(initBlock.build()) - } -} \ No newline at end of file diff --git a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/scalar/IosJvmScalarMessageMethodGenerator.kt b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/scalar/ActualScalarMessageMethodGenerator.kt similarity index 97% rename from plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/scalar/IosJvmScalarMessageMethodGenerator.kt rename to plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/scalar/ActualScalarMessageMethodGenerator.kt index 7ce009d..9986454 100644 --- a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/scalar/IosJvmScalarMessageMethodGenerator.kt +++ b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/scalar/ActualScalarMessageMethodGenerator.kt @@ -6,7 +6,7 @@ import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatfor import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.ProtoMessageAttribute import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.Const -object IosJvmScalarMessageMethodGenerator : ScalarMessageMethodGenerator(true) { +object ActualScalarMessageMethodGenerator : ScalarMessageMethodGenerator(true) { override val attrs: List = listOf(KModifier.ACTUAL) diff --git a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/scalar/JsScalarMessageMethodGenerator.kt b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/scalar/JsScalarMessageMethodGenerator.kt deleted file mode 100644 index 03d972e..0000000 --- a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/scalar/JsScalarMessageMethodGenerator.kt +++ /dev/null @@ -1,75 +0,0 @@ -package io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.scalar - -import com.squareup.kotlinpoet.* -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.ProtoMessage -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.ProtoMessageAttribute -import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.Const - -object JsScalarMessageMethodGenerator : ScalarMessageMethodGenerator(true) { - - override val attrs: List = listOf(KModifier.ACTUAL) - - override fun getTypeForAttribute(protoMessageAttribute: ProtoMessageAttribute): TypeName = - protoMessageAttribute.commonType - - override fun modifyProperty(builder: PropertySpec.Builder, message: ProtoMessage, attr: ProtoMessageAttribute) { - builder.getter(FunSpec.getterBuilder().apply { - if (attr.types.isEnum) { - addCode( - "return %T.%N(jsImpl.%N())", - attr.commonType, - Const.Enum.getEnumForNumFunctionName, - Const.Message.Attribute.Scalar.JS.getFunction(message, attr).simpleName - ) - } else { - val getter = - CodeBlock.of( - "jsImpl.%N()", - Const.Message.Attribute.Scalar.JS.getFunction(message, attr).simpleName - ) - - addCode("return ") - if (attr.types.doDiffer) { -// if (attr.types.hasDefaultValue) { - addCode("%M(", Const.Message.CommonFunction.JS.commonFunction(attr)) - addCode(getter) - addCode(")") -// } else { -// addCode(getter) -// addCode( -// ".let·{ if (it == null) null else %M(it) }", -// Const.Message.CommonFunction.JS.commonFunction(attr) -// ) -// } - } else { - addCode(getter) - } - } - }.build()) - } - -// override fun modifySetter(builder: FunSpec.Builder, message: ProtoMessage, attr: ProtoMessageAttribute) { -// val value = -// if (attr.types.isEnum) "value.value" -// else if (attr.types.doDiffer) { -// if (attr.types.hasDefaultValue) { -// "value.jsImpl" -// } else "value?.jsImpl ?: undefined" -// } else { -// if (attr.types.hasDefaultValue) { -// "value" -// } else { -// "value ?: undefined" -// } -// } -// -// builder.addStatement("jsImpl.%N($value)", Const.Message.Attribute.Scalar.JS.setFunction(message, attr).simpleName) -// } - - override fun modifyHasFunction(builder: FunSpec.Builder, message: ProtoMessage, attr: ProtoMessageAttribute) { - builder.addStatement( - "return jsImpl.%N()", - Const.Message.Attribute.Scalar.JS.getHasFunction(message, attr).simpleName - ) - } -} \ No newline at end of file diff --git a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/service/IOSServiceWriter.kt b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/service/IosServiceWriter.kt similarity index 98% rename from plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/service/IOSServiceWriter.kt rename to plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/service/IosServiceWriter.kt index fb48a4f..bba8296 100644 --- a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/service/IOSServiceWriter.kt +++ b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/service/IosServiceWriter.kt @@ -8,7 +8,7 @@ import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatfor import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.content.ProtoService import io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatform_sources.generators.Const -object IOSServiceWriter : ActualServiceWriter() { +object IosServiceWriter : ActualServiceWriter() { override val classAndFunctionModifiers: List = listOf(KModifier.ACTUAL) override val channelConstructorModifiers: List = listOf(KModifier.ACTUAL) diff --git a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/service/JsServiceWriter.kt b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/service/JsServiceWriter.kt index a0123db..4caf563 100644 --- a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/service/JsServiceWriter.kt +++ b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/generators/service/JsServiceWriter.kt @@ -63,7 +63,7 @@ object JsServiceWriter : ActualServiceWriter() { val clientCallCode = CodeBlock .builder() .add("client.${rpc.rpcName}(") - .add("request.jsImpl, ") + .add("request, ") .add("actMetadata.%M", MemberName("io.github.timortel.kotlin_multiplatform_grpc_lib", "jsMetadata")) .apply { when (rpc.method) { @@ -75,12 +75,8 @@ object JsServiceWriter : ActualServiceWriter() { addCode("return ") - if (rpc.method == ProtoRpc.Method.UNARY) { - addCode("%M(", responseCommonMember) - } - addCode( - "%M<%T> {\n", + "%M {\n", when (rpc.method) { ProtoRpc.Method.UNARY -> MemberName( "io.github.timortel.kotlin_multiplatform_grpc_lib.rpc", @@ -90,23 +86,13 @@ object JsServiceWriter : ActualServiceWriter() { ProtoRpc.Method.SERVER_STREAMING -> MemberName( "io.github.timortel.kotlin_multiplatform_grpc_lib.rpc", "serverSideStreamingCallImplementation" ) - }, - rpc.response.jsType + } ) if (rpc.method == ProtoRpc.Method.UNARY) addCode(" callback -> ") addCode(clientCallCode) addCode("\n}") - - when (rpc.method) { - ProtoRpc.Method.UNARY -> addCode(")") - ProtoRpc.Method.SERVER_STREAMING -> addCode( - ".%M { %M(it) }", - MemberName("kotlinx.coroutines.flow", "map"), - responseCommonMember - ) - } } } diff --git a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/libClasses.kt b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/libClasses.kt index fec48a5..9bcd54e 100644 --- a/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/libClasses.kt +++ b/plugin/src/main/java/io/github/timortel/kotlin_multiplatform_grpc_plugin/generate_mulitplatform_sources/libClasses.kt @@ -2,6 +2,7 @@ package io.github.timortel.kotlin_multiplatform_grpc_plugin.generate_mulitplatfo import com.squareup.kotlinpoet.ClassName import com.squareup.kotlinpoet.MemberName +import com.squareup.kotlinpoet.MemberName.Companion.member val kmMetadata = ClassName("io.github.timortel.kotlin_multiplatform_grpc_lib", "KMMetadata") val kmStub = ClassName("io.github.timortel.kotlin_multiplatform_grpc_lib.stub", "KMStub") @@ -26,4 +27,8 @@ val writeMap = MemberName(PACKAGE_IO, "writeMap") val readMapEntry = MemberName(PACKAGE_IO, "readMapEntry") val iosUnaryCallImplementation = MemberName("io.github.timortel.kotlin_multiplatform_grpc_lib.rpc", "unaryCallImplementation") -val iosServerSideStreamingCallImplementation = MemberName("io.github.timortel.kotlin_multiplatform_grpc_lib.rpc", "serverSideStreamingCallImplementation") \ No newline at end of file +val iosServerSideStreamingCallImplementation = MemberName("io.github.timortel.kotlin_multiplatform_grpc_lib.rpc", "serverSideStreamingCallImplementation") + +private val JSPB = ClassName("io.github.timortel.kotlin_multiplatform_grpc_lib", "JSPB") +val JSPB_BINARY_WRITER = JSPB.nestedClass("BinaryWriter") +val JSPB_BINARY_READER = JSPB.nestedClass("BinaryReader") \ No newline at end of file diff --git a/readme.md b/readme.md index 674c441..5f258f3 100644 --- a/readme.md +++ b/readme.md @@ -168,26 +168,25 @@ kotlin { } sourceSets { - val commonMain by getting { + commonMain { dependencies { implementation(kotlin("stdlib-common")) api("com.github.TimOrtel.GRPC-Kotlin-Multiplatform:grpc-multiplatform-lib:") api("org.jetbrains.kotlinx:kotlinx-coroutines-core:") } } - - val jvmMain by getting { - } } } grpcKotlinMultiplatform { - targetSourcesMap.put(OutputTarget.COMMON, listOf(kotlin.sourceSets.getByName("commonMain"))) - targetSourcesMap.put(OutputTarget.IOS, listOf(kotlin.sourceSets.getByName("iosMain"))) - targetSourcesMap.put(OutputTarget.JVM, listOf(kotlin.sourceSets.getByName("androidMain"))) - targetSourcesMap.put(OutputTarget.JS, listOf(kotlin.sourceSets.getByName("jsMain"))) + with(kotlin) { + targetSourcesMap.put(OutputTarget.COMMON, listOf(kotlin.sourceSets.commonMain.get())) + targetSourcesMap.put(OutputTarget.IOS, listOf(kotlin.sourceSets.iosMain.get())) + targetSourcesMap.put(OutputTarget.JVM, listOf(kotlin.sourceSets.androidMain.get())) + targetSourcesMap.put(OutputTarget.JS, listOf(kotlin.sourceSets.jsMain.get())) + } - //Specify the folders where your proto files are located, you can list multiple. + // Specify the folders where your proto files are located, you can list multiple. protoSourceFolders.set(listOf(projectDir.resolve("../protos/src/main/proto"))) } @@ -222,7 +221,7 @@ Please send all pull requests to the develop branch, as the master always holds - The iOS implementation uses the objective-c implementation of gRPC. The necessary message implementations are generated by the gradle plugin. ## License -Copyright 2023 Tim Ortel +Copyright 2024 Tim Ortel Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at diff --git a/settings.gradle.kts b/settings.gradle.kts index 7b2476e..19e7433 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -7,16 +7,15 @@ pluginManagement { gradlePluginPortal() google() } - val kotlinVersion = "1.8.10" plugins { - kotlin("jvm") version kotlinVersion - kotlin("multiplatform") version kotlinVersion - id("com.android.library") version "7.4.2" + kotlin("jvm") version "1.9.0" + kotlin("multiplatform") version "1.9.0" + id("com.android.library") - id("com.google.protobuf") version "0.8.18" apply false + id("com.google.protobuf") } } -//include("plugin") + include("grpc-multiplatform-lib") include("grpc-mp-test")