@@ -30,35 +30,46 @@ class ModelToKotlinGenerator(
3030 )
3131 }
3232
33+ private var currentPackage: FqName = FqName .Package .Root
34+
3335 private fun FileDeclaration.generatePublicKotlinFile (): FileGenerator {
36+ currentPackage = packageName
37+
3438 return file(codeGenerationParameters, logger = logger) {
3539 filename = this @generatePublicKotlinFile.name
36- packageName = this @generatePublicKotlinFile.packageName.fullName ()
37- packagePath = this @generatePublicKotlinFile.packageName.fullName ()
40+ packageName = this @generatePublicKotlinFile.packageName.safeFullName ()
41+ packagePath = this @generatePublicKotlinFile.packageName.safeFullName ()
3842
3943 dependencies.forEach { dependency ->
40- importPackage(dependency.packageName.fullName ())
44+ importPackage(dependency.packageName.safeFullName ())
4145 }
4246
47+ fileOptIns = listOf (" ExperimentalRpcApi::class" , " InternalRpcApi::class" )
48+
4349 generatePublicDeclaredEntities(this @generatePublicKotlinFile)
4450
51+ import(" kotlinx.rpc.internal.utils.*" )
52+
4553 additionalPublicImports.forEach {
4654 import(it)
4755 }
4856 }
4957 }
5058
5159 private fun FileDeclaration.generateInternalKotlinFile (): FileGenerator {
60+ currentPackage = packageName
61+
5262 return file(codeGenerationParameters, logger = logger) {
5363 filename = this @generateInternalKotlinFile.name
54- packageName = this @generateInternalKotlinFile.packageName.fullName ()
64+ packageName = this @generateInternalKotlinFile.packageName.safeFullName ()
5565 packagePath =
56- this @generateInternalKotlinFile.packageName.fullName().packageNameSuffixed(RPC_INTERNAL_PACKAGE_SUFFIX )
66+ this @generateInternalKotlinFile.packageName.safeFullName()
67+ .packageNameSuffixed(RPC_INTERNAL_PACKAGE_SUFFIX )
5768
5869 fileOptIns = listOf (" ExperimentalRpcApi::class" , " InternalRpcApi::class" )
5970
6071 dependencies.forEach { dependency ->
61- importPackage(dependency.packageName.fullName ())
72+ importPackage(dependency.packageName.safeFullName ())
6273 }
6374
6475 generateInternalDeclaredEntities(this @generateInternalKotlinFile)
@@ -87,7 +98,7 @@ class ModelToKotlinGenerator(
8798 }
8899
89100 private fun MessageDeclaration.fields () = actualFields.map {
90- it.transformToFieldDeclaration() to it.type
101+ it.transformToFieldDeclaration() to it
91102 }
92103
93104 @Suppress(" detekt.CyclomaticComplexMethod" )
@@ -125,14 +136,22 @@ class ModelToKotlinGenerator(
125136 clazz(
126137 name = " ${declaration.name.simpleName} Builder" ,
127138 declarationType = DeclarationType .Class ,
128- superTypes = listOf (declaration.name.fullName ()),
139+ superTypes = listOf (declaration.name.safeFullName ()),
129140 ) {
130- declaration.fields().forEach { (fieldDeclaration, type) ->
131- val value = if (type is FieldType .Reference ) {
132- additionalInternalImports.add(" kotlin.properties.Delegates" )
133- " by Delegates.notNull()"
134- } else {
135- " = ${type.defaultValue} "
141+ declaration.fields().forEach { (fieldDeclaration, field) ->
142+ val value = when {
143+ field.type is FieldType .Reference && field.nullable -> {
144+ " = null"
145+ }
146+
147+ field.type is FieldType .Reference -> {
148+ additionalInternalImports.add(" kotlin.properties.Delegates" )
149+ " by Delegates.notNull()"
150+ }
151+
152+ else -> {
153+ " = ${field.type.defaultValue} "
154+ }
136155 }
137156
138157 code(" override var $fieldDeclaration $value " )
@@ -144,35 +163,55 @@ class ModelToKotlinGenerator(
144163 name = " invoke" ,
145164 modifiers = " operator" ,
146165 args = " body: ${declaration.name.simpleName} Builder.() -> Unit" ,
147- contextReceiver = " ${declaration.name.fullName ()} .Companion" ,
148- returnType = declaration.name.fullName (),
166+ contextReceiver = " ${declaration.name.safeFullName ()} .Companion" ,
167+ returnType = declaration.name.safeFullName (),
149168 ) {
150169 code(" return ${declaration.name.simpleName} Builder().apply(body)" )
151170 }
152171
153- val platformType = " ${declaration.outerClassName.fullName ()} .${declaration.name.simpleName} "
172+ val platformType = " ${declaration.outerClassName.safeFullName ()} .${declaration.name.simpleName} "
154173
155174 function(
156175 name = " toPlatform" ,
157- contextReceiver = declaration.name.fullName (),
176+ contextReceiver = declaration.name.safeFullName (),
158177 returnType = platformType,
159178 ) {
160179 scope(" return $platformType .newBuilder().apply" , " .build()" ) {
161180 declaration.actualFields.forEach { field ->
162- val call = " this@toPlatform.${field.name}${field.toPlatformCast()} "
163- code(" set${field.name.replaceFirstChar { ch -> ch.uppercase() }} ($call )" )
181+ val uppercaseName = field.name.replaceFirstChar { ch -> ch.uppercase() }
182+ val setFieldCall = " set$uppercaseName "
183+
184+ if (field.nullable) {
185+ code(" this@toPlatform.${field.name} ?.let { $setFieldCall (it${field.toPlatformCast()} ) }" )
186+ } else {
187+ code(" $setFieldCall (this@toPlatform.${field.name}${field.toPlatformCast()} )" )
188+ }
164189 }
165190 }
166191 }
167192
168193 function(
169194 name = " toKotlin" ,
170195 contextReceiver = platformType,
171- returnType = declaration.name.fullName (),
196+ returnType = declaration.name.safeFullName (),
172197 ) {
173198 scope(" return ${declaration.name.simpleName} " ) {
174199 declaration.actualFields.forEach { field ->
175- code(" ${field.name} = this@toKotlin.${field.name}${field.toKotlinCast()} " )
200+ val getter = " this@toKotlin.${field.name}${field.toKotlinCast()} "
201+ if (field.nullable) {
202+ ifBranch(
203+ prefix = " ${field.name} = " ,
204+ condition = " has${field.name.replaceFirstChar { ch -> ch.uppercase() }} ()" ,
205+ ifBlock = {
206+ code(getter)
207+ },
208+ elseBlock = {
209+ code(" null" )
210+ }
211+ )
212+ } else {
213+ code(" ${field.name} = $getter " )
214+ }
176215 }
177216 }
178217 }
@@ -185,7 +224,11 @@ class ModelToKotlinGenerator(
185224 FieldType .IntegralType .UINT32 -> " .toInt()"
186225 FieldType .IntegralType .UINT64 -> " .toLong()"
187226 FieldType .IntegralType .BYTES -> " .let { bytes -> com.google.protobuf.ByteString.copyFrom(bytes) }"
188- is FieldType .Reference -> " .toPlatform()"
227+ is FieldType .Reference -> " .toPlatform()" .also {
228+ val fq by type.value
229+ importRootDeclarationIfNeeded(fq, " toPlatform" , true )
230+ }
231+
189232 else -> " "
190233 }
191234 }
@@ -197,7 +240,11 @@ class ModelToKotlinGenerator(
197240 FieldType .IntegralType .UINT32 -> " .toUInt()"
198241 FieldType .IntegralType .UINT64 -> " .toULong()"
199242 FieldType .IntegralType .BYTES -> " .toByteArray()"
200- is FieldType .Reference -> " .toKotlin()"
243+ is FieldType .Reference -> " .toKotlin()" .also {
244+ val fq by type.value
245+ importRootDeclarationIfNeeded(fq, " toKotlin" , true )
246+ }
247+
201248 else -> " "
202249 }
203250 }
@@ -211,7 +258,7 @@ class ModelToKotlinGenerator(
211258 // KRPC-156 Reference Types
212259 is FieldType .Reference -> {
213260 val value by type.value
214- value.fullName ()
261+ value.safeFullName ()
215262 }
216263
217264 is FieldType .IntegralType -> {
@@ -230,7 +277,11 @@ class ModelToKotlinGenerator(
230277 else -> {
231278 error(" Unsupported type: $type " )
232279 }
233- }
280+ }.withNullability(nullable)
281+ }
282+
283+ private fun String.withNullability (nullable : Boolean ): String {
284+ return " $this${if (nullable) " ?" else " " } "
234285 }
235286
236287 @Suppress(" unused" )
@@ -278,11 +329,11 @@ class ModelToKotlinGenerator(
278329
279330 @Suppress(" unused" )
280331 private fun CodeGenerator.generateInternalEnum (declaration : EnumDeclaration ) {
281- val platformType = " ${declaration.outerClassName.fullName ()} .${declaration.name.simpleName} "
332+ val platformType = " ${declaration.outerClassName.safeFullName ()} .${declaration.name.simpleName} "
282333
283334 function(
284335 name = " toPlatform" ,
285- contextReceiver = declaration.name.fullName (),
336+ contextReceiver = declaration.name.safeFullName (),
286337 returnType = platformType,
287338 ) {
288339 scope(" return when (this)" ) {
@@ -299,7 +350,7 @@ class ModelToKotlinGenerator(
299350 function(
300351 name = " toKotlin" ,
301352 contextReceiver = platformType,
302- returnType = declaration.name.fullName (),
353+ returnType = declaration.name.safeFullName (),
303354 ) {
304355 scope(" return when (this)" ) {
305356 declaration.aliases.forEach { field ->
@@ -324,8 +375,8 @@ class ModelToKotlinGenerator(
324375 function(
325376 name = method.name,
326377 modifiers = " suspend" ,
327- args = " message: ${inputType.name.fullName ()} " ,
328- returnType = outputType.name.fullName (),
378+ args = " message: ${inputType.name.safeFullName ()} " ,
379+ returnType = outputType.name.safeFullName (),
329380 )
330381 }
331382 }
@@ -337,7 +388,7 @@ class ModelToKotlinGenerator(
337388 modifiers = " private" ,
338389 name = " ${service.name.simpleName} Delegate" ,
339390 declarationType = DeclarationType .Object ,
340- superTypes = listOf (" kotlinx.rpc.grpc.descriptor.GrpcDelegate<${service.name.fullName ()} >" ),
391+ superTypes = listOf (" kotlinx.rpc.grpc.descriptor.GrpcDelegate<${service.name.safeFullName ()} >" ),
341392 ) {
342393 function(
343394 name = " clientProvider" ,
@@ -351,7 +402,7 @@ class ModelToKotlinGenerator(
351402 function(
352403 name = " definitionFor" ,
353404 modifiers = " override" ,
354- args = " impl: ${service.name.fullName ()} " ,
405+ args = " impl: ${service.name.safeFullName ()} " ,
355406 returnType = " kotlinx.rpc.grpc.ServerServiceDefinition" ,
356407 ) {
357408 scope(" return ${service.name.simpleName} ServerDelegate(impl).bindService()" )
@@ -364,7 +415,7 @@ class ModelToKotlinGenerator(
364415 name = " ${service.name.simpleName} ServerDelegate" ,
365416 declarationType = DeclarationType .Class ,
366417 superTypes = listOf (" ${service.name.simpleName} GrpcKt.${service.name.simpleName} CoroutineImplBase()" ),
367- constructorArgs = listOf (" private val impl: ${service.name.fullName ()} " ),
418+ constructorArgs = listOf (" private val impl: ${service.name.safeFullName ()} " ),
368419 ) {
369420 service.methods.forEach { method ->
370421 val grpcName = method.name.replaceFirstChar { it.lowercase() }
@@ -379,6 +430,9 @@ class ModelToKotlinGenerator(
379430 returnType = outputType.toPlatformMessageType(),
380431 ) {
381432 code(" return impl.${method.name} (request.toKotlin()).toPlatform()" )
433+
434+ importRootDeclarationIfNeeded(inputType.name, " toPlatform" , true )
435+ importRootDeclarationIfNeeded(outputType.name, " toKotlin" , true )
382436 }
383437 }
384438 }
@@ -415,9 +469,13 @@ class ModelToKotlinGenerator(
415469 scope(" return when (call.callableName)" ) {
416470 service.methods.forEach { method ->
417471 val inputType by method.inputType
472+ val outputType by method.outputType
418473 val grpcName = method.name.replaceFirstChar { it.lowercase() }
419- val result = " stub.$grpcName ((message as ${inputType.name.fullName ()} ).toPlatform())"
474+ val result = " stub.$grpcName ((message as ${inputType.name.safeFullName ()} ).toPlatform())"
420475 code(" \" ${method.name} \" -> $result .toKotlin() as R" )
476+
477+ importRootDeclarationIfNeeded(inputType.name, " toPlatform" , true )
478+ importRootDeclarationIfNeeded(outputType.name, " toKotlin" , true )
421479 }
422480
423481 code(" else -> error(\" Illegal call: \$ {call.callableName}\" )" )
@@ -437,7 +495,26 @@ class ModelToKotlinGenerator(
437495 }
438496
439497 private fun MessageDeclaration.toPlatformMessageType (): String {
440- return " ${outerClassName.fullName()} .${name.simpleName} "
498+ return " ${outerClassName.safeFullName()} .${name.simpleName} "
499+ }
500+
501+ private fun FqName.safeFullName (): String {
502+ importRootDeclarationIfNeeded(this )
503+
504+ return fullName()
505+ }
506+
507+ private fun importRootDeclarationIfNeeded (
508+ declaration : FqName ,
509+ nameToImport : String = declaration.simpleName,
510+ internalOnly : Boolean = false,
511+ ) {
512+ if (declaration.parent == FqName .Package .Root && currentPackage != FqName .Package .Root && nameToImport.isNotBlank()) {
513+ additionalInternalImports.add(nameToImport)
514+ if (! internalOnly) {
515+ additionalPublicImports.add(nameToImport)
516+ }
517+ }
441518 }
442519}
443520
0 commit comments