Skip to content

Commit 277b4b7

Browse files
committed
KRPC-162 Internal vs Public Codegen separation in gRPC (#285)
1 parent 84701d0 commit 277b4b7

File tree

3 files changed

+88
-65
lines changed

3 files changed

+88
-65
lines changed

protobuf-plugin/src/main/kotlin/kotlinx/rpc/protobuf/CodeGenerator.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -255,13 +255,14 @@ class FileGenerator(
255255
codeGenerationParameters: CodeGenerationParameters,
256256
var filename: String? = null,
257257
var packageName: String? = null,
258+
var packagePath: String? = packageName,
258259
var fileOptIns: List<String> = emptyList(),
259260
logger: Logger = NOPLogger.NOP_LOGGER,
260261
) : CodeGenerator(codeGenerationParameters, "", logger = logger) {
261262
private val imports = mutableListOf<String>()
262263

263264
fun importPackage(name: String) {
264-
if (name != packageName) {
265+
if (name != packageName && name.isNotBlank()) {
265266
imports.add("$name.*")
266267
}
267268
}
@@ -304,4 +305,5 @@ fun file(
304305
packageName: String? = null,
305306
logger: Logger = NOPLogger.NOP_LOGGER,
306307
block: FileGenerator.() -> Unit,
307-
): FileGenerator = FileGenerator(codeGenerationParameters, name, packageName, emptyList(), logger).apply(block)
308+
): FileGenerator = FileGenerator(codeGenerationParameters, name, packageName, packageName, emptyList(), logger)
309+
.apply(block)

protobuf-plugin/src/main/kotlin/kotlinx/rpc/protobuf/ModelToKotlinGenerator.kt

Lines changed: 80 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -8,74 +8,89 @@ import kotlinx.rpc.protobuf.CodeGenerator.DeclarationType
88
import kotlinx.rpc.protobuf.model.*
99
import org.slf4j.Logger
1010

11+
private const val RPC_INTERNAL_PACKAGE_SUFFIX = "_rpc_internal"
12+
1113
class ModelToKotlinGenerator(
1214
private val model: Model,
1315
private val logger: Logger,
1416
private val codeGenerationParameters: CodeGenerationParameters,
1517
) {
1618
fun generateKotlinFiles(): List<FileGenerator> {
17-
return model.files.map { it.generateKotlinFile() }
19+
return model.files.flatMap { it.generateKotlinFiles() }
20+
}
21+
22+
private fun FileDeclaration.generateKotlinFiles(): List<FileGenerator> {
23+
return listOf(
24+
generatePublicKotlinFile(),
25+
generateInternalKotlinFile(),
26+
)
1827
}
1928

20-
private fun FileDeclaration.generateKotlinFile(): FileGenerator {
29+
private fun FileDeclaration.generatePublicKotlinFile(): FileGenerator {
2130
return file(codeGenerationParameters, logger = logger) {
2231
filename = name.simpleName
2332
packageName = name.packageName
24-
fileOptIns = listOf("ExperimentalRpcApi::class", "InternalRpcApi::class")
33+
packagePath = name.packageName
2534

2635
dependencies.forEach { dependency ->
2736
importPackage(dependency.name.packageName)
2837
}
2938

30-
generateDeclaredEntities(this@generateKotlinFile)
39+
generatePublicDeclaredEntities(this@generatePublicKotlinFile)
3140

32-
additionalImports.forEach {
41+
additionalPublicImports.forEach {
3342
import(it)
3443
}
44+
}
45+
}
46+
47+
private fun FileDeclaration.generateInternalKotlinFile(): FileGenerator {
48+
return file(codeGenerationParameters, logger = logger) {
49+
filename = name.simpleName
50+
packageName = name.packageName
51+
packagePath = name.packageName.packageNameSuffixed(RPC_INTERNAL_PACKAGE_SUFFIX)
52+
53+
fileOptIns = listOf("ExperimentalRpcApi::class", "InternalRpcApi::class")
54+
55+
dependencies.forEach { dependency ->
56+
importPackage(dependency.name.packageName)
57+
}
58+
59+
generateInternalDeclaredEntities(this@generateInternalKotlinFile)
60+
3561
import("kotlinx.rpc.internal.utils.*")
3662
}
3763
}
3864

39-
private val additionalImports = mutableSetOf<String>()
65+
private val additionalPublicImports = mutableSetOf<String>()
4066

41-
private fun CodeGenerator.generateDeclaredEntities(fileDeclaration: FileDeclaration) {
42-
fileDeclaration.messageDeclarations.forEach { generateMessage(it) }
67+
private fun CodeGenerator.generatePublicDeclaredEntities(fileDeclaration: FileDeclaration) {
68+
fileDeclaration.messageDeclarations.forEach { generatePublicMessage(it) }
4369
// KRPC-141 Enum Types
4470
// fileDeclaration.enumDeclarations.forEach { generateEnum(it) }
45-
fileDeclaration.serviceDeclarations.forEach { generateService(it) }
71+
fileDeclaration.serviceDeclarations.forEach { generatePublicService(it) }
4672
}
4773

48-
@Suppress("detekt.CyclomaticComplexMethod")
49-
private fun CodeGenerator.generateMessage(declaration: MessageDeclaration) {
50-
val fields = declaration.actualFields.map { it.generateFieldDeclaration() to it.type.defaultValue }
51-
52-
val isInterfaceMode = parameters.messageMode == RpcProtobufPlugin.MessageMode.Interface
53-
54-
val (declarationType, modifiers) = when {
55-
isInterfaceMode -> {
56-
DeclarationType.Interface to ""
57-
}
58-
59-
fields.isEmpty() -> {
60-
DeclarationType.Object to ""
61-
}
74+
private fun CodeGenerator.generateInternalDeclaredEntities(fileDeclaration: FileDeclaration) {
75+
fileDeclaration.messageDeclarations.forEach { generateInternalMessage(it) }
76+
// KRPC-141 Enum Types
77+
// fileDeclaration.enumDeclarations.forEach { generateEnum(it) }
78+
fileDeclaration.serviceDeclarations.forEach { generateInternalService(it) }
79+
}
6280

63-
else -> {
64-
DeclarationType.Class to "data"
65-
}
66-
}
81+
private fun MessageDeclaration.fields() = actualFields.map {
82+
it.transformToFieldDeclaration() to it.type.defaultValue
83+
}
6784

85+
@Suppress("detekt.CyclomaticComplexMethod")
86+
private fun CodeGenerator.generatePublicMessage(declaration: MessageDeclaration) {
6887
clazz(
6988
name = declaration.name.simpleName,
70-
modifiers = modifiers,
71-
constructorArgs = if (isInterfaceMode) emptyList() else fields.map { "val ${it.first}" to it.second },
72-
declarationType = declarationType,
89+
declarationType = DeclarationType.Interface,
7390
) {
74-
if (isInterfaceMode) {
75-
fields.forEach {
76-
code("val ${it.first}")
77-
newLine()
78-
}
91+
declaration.fields().forEach {
92+
code("val ${it.first}")
93+
newLine()
7994
}
8095

8196
// KRPC-147 OneOf Types
@@ -93,32 +108,31 @@ class ModelToKotlinGenerator(
93108
// generateEnum(enum)
94109
// }
95110

96-
if (isInterfaceMode) {
97-
clazz("", modifiers = "companion", declarationType = DeclarationType.Object)
98-
}
111+
clazz("", modifiers = "companion", declarationType = DeclarationType.Object)
99112
}
113+
}
100114

101-
if (isInterfaceMode) {
102-
clazz(
103-
name = "${declaration.name.simpleName}Builder",
104-
declarationType = DeclarationType.Class,
105-
superTypes = listOf(declaration.name.simpleName),
106-
) {
107-
fields.forEach {
108-
code("override var ${it.first} = ${it.second}")
109-
newLine()
110-
}
115+
@Suppress("detekt.CyclomaticComplexMethod")
116+
private fun CodeGenerator.generateInternalMessage(declaration: MessageDeclaration) {
117+
clazz(
118+
name = "${declaration.name.simpleName}Builder",
119+
declarationType = DeclarationType.Class,
120+
superTypes = listOf(declaration.name.simpleName),
121+
) {
122+
declaration.fields().forEach {
123+
code("override var ${it.first} = ${it.second}")
124+
newLine()
111125
}
126+
}
112127

113-
function(
114-
name = "invoke",
115-
modifiers = "operator",
116-
args = "body: ${declaration.name.simpleName}Builder.() -> Unit",
117-
contextReceiver = "${declaration.name.simpleName}.Companion",
118-
returnType = declaration.name.simpleName,
119-
) {
120-
code("return ${declaration.name.simpleName}Builder().apply(body)")
121-
}
128+
function(
129+
name = "invoke",
130+
modifiers = "operator",
131+
args = "body: ${declaration.name.simpleName}Builder.() -> Unit",
132+
contextReceiver = "${declaration.name.simpleName}.Companion",
133+
returnType = declaration.name.simpleName,
134+
) {
135+
code("return ${declaration.name.simpleName}Builder().apply(body)")
122136
}
123137

124138
val platformType = "${declaration.outerClassName.simpleName}.${declaration.name.simpleName}"
@@ -175,7 +189,7 @@ class ModelToKotlinGenerator(
175189
}
176190
}
177191

178-
private fun FieldDeclaration.generateFieldDeclaration(): String {
192+
private fun FieldDeclaration.transformToFieldDeclaration(): String {
179193
return "${name}: ${typeFqName()}"
180194
}
181195

@@ -219,7 +233,7 @@ class ModelToKotlinGenerator(
219233
superTypes = listOf(interfaceName),
220234
)
221235

222-
additionalImports.add("kotlin.jvm.JvmInline")
236+
additionalPublicImports.add("kotlin.jvm.JvmInline")
223237
}
224238
}
225239
}
@@ -247,7 +261,7 @@ class ModelToKotlinGenerator(
247261
}
248262

249263
@Suppress("detekt.LongMethod")
250-
private fun CodeGenerator.generateService(service: ServiceDeclaration) {
264+
private fun CodeGenerator.generatePublicService(service: ServiceDeclaration) {
251265
code("@kotlinx.rpc.grpc.annotations.Grpc")
252266
clazz(service.name.simpleName, declarationType = DeclarationType.Interface) {
253267
service.methods.forEach { method ->
@@ -262,9 +276,9 @@ class ModelToKotlinGenerator(
262276
)
263277
}
264278
}
279+
}
265280

266-
newLine()
267-
281+
private fun CodeGenerator.generateInternalService(service: ServiceDeclaration) {
268282
code("@Suppress(\"unused\", \"all\")")
269283
clazz(
270284
modifiers = "private",
@@ -373,3 +387,7 @@ class ModelToKotlinGenerator(
373387
return "${outerClassName.simpleName}.${name.simpleName.removePrefix(name.parentNameAsPrefix)}"
374388
}
375389
}
390+
391+
internal fun String.packageNameSuffixed(suffix: String): String {
392+
return if (isEmpty()) suffix else "$this.$suffix"
393+
}

protobuf-plugin/src/main/kotlin/kotlinx/rpc/protobuf/RpcProtobufPlugin.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import com.google.protobuf.compiler.PluginProtos.CodeGeneratorResponse.Feature
1414
import org.slf4j.Logger
1515
import org.slf4j.LoggerFactory
1616
import org.slf4j.helpers.NOPLogger
17+
import java.io.File
1718

1819
class RpcProtobufPlugin {
1920
companion object {
@@ -68,7 +69,9 @@ class RpcProtobufPlugin {
6869
.map { file ->
6970
CodeGeneratorResponse.File.newBuilder()
7071
.apply {
71-
val dir = file.packageName?.replace('.', '/')?.plus("/") ?: ""
72+
val dir = file.packagePath
73+
?.replace('.', File.separatorChar)?.plus(File.separatorChar)
74+
?: ""
7275

7376
// some filename already contain package (true for Google's default .proto files)
7477
val filename = file.filename?.removePrefix(dir) ?: error("File name can not be null")

0 commit comments

Comments
 (0)