@@ -30,35 +30,46 @@ class ModelToKotlinGenerator(
30
30
)
31
31
}
32
32
33
+ private var currentPackage: FqName = FqName .Package .Root
34
+
33
35
private fun FileDeclaration.generatePublicKotlinFile (): FileGenerator {
36
+ currentPackage = packageName
37
+
34
38
return file(codeGenerationParameters, logger = logger) {
35
39
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 ()
38
42
39
43
dependencies.forEach { dependency ->
40
- importPackage(dependency.packageName.fullName ())
44
+ importPackage(dependency.packageName.safeFullName ())
41
45
}
42
46
47
+ fileOptIns = listOf (" ExperimentalRpcApi::class" , " InternalRpcApi::class" )
48
+
43
49
generatePublicDeclaredEntities(this @generatePublicKotlinFile)
44
50
51
+ import(" kotlinx.rpc.internal.utils.*" )
52
+
45
53
additionalPublicImports.forEach {
46
54
import(it)
47
55
}
48
56
}
49
57
}
50
58
51
59
private fun FileDeclaration.generateInternalKotlinFile (): FileGenerator {
60
+ currentPackage = packageName
61
+
52
62
return file(codeGenerationParameters, logger = logger) {
53
63
filename = this @generateInternalKotlinFile.name
54
- packageName = this @generateInternalKotlinFile.packageName.fullName ()
64
+ packageName = this @generateInternalKotlinFile.packageName.safeFullName ()
55
65
packagePath =
56
- this @generateInternalKotlinFile.packageName.fullName().packageNameSuffixed(RPC_INTERNAL_PACKAGE_SUFFIX )
66
+ this @generateInternalKotlinFile.packageName.safeFullName()
67
+ .packageNameSuffixed(RPC_INTERNAL_PACKAGE_SUFFIX )
57
68
58
69
fileOptIns = listOf (" ExperimentalRpcApi::class" , " InternalRpcApi::class" )
59
70
60
71
dependencies.forEach { dependency ->
61
- importPackage(dependency.packageName.fullName ())
72
+ importPackage(dependency.packageName.safeFullName ())
62
73
}
63
74
64
75
generateInternalDeclaredEntities(this @generateInternalKotlinFile)
@@ -87,7 +98,7 @@ class ModelToKotlinGenerator(
87
98
}
88
99
89
100
private fun MessageDeclaration.fields () = actualFields.map {
90
- it.transformToFieldDeclaration() to it.type
101
+ it.transformToFieldDeclaration() to it
91
102
}
92
103
93
104
@Suppress(" detekt.CyclomaticComplexMethod" )
@@ -125,14 +136,22 @@ class ModelToKotlinGenerator(
125
136
clazz(
126
137
name = " ${declaration.name.simpleName} Builder" ,
127
138
declarationType = DeclarationType .Class ,
128
- superTypes = listOf (declaration.name.fullName ()),
139
+ superTypes = listOf (declaration.name.safeFullName ()),
129
140
) {
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
+ }
136
155
}
137
156
138
157
code(" override var $fieldDeclaration $value " )
@@ -144,35 +163,55 @@ class ModelToKotlinGenerator(
144
163
name = " invoke" ,
145
164
modifiers = " operator" ,
146
165
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 (),
149
168
) {
150
169
code(" return ${declaration.name.simpleName} Builder().apply(body)" )
151
170
}
152
171
153
- val platformType = " ${declaration.outerClassName.fullName ()} .${declaration.name.simpleName} "
172
+ val platformType = " ${declaration.outerClassName.safeFullName ()} .${declaration.name.simpleName} "
154
173
155
174
function(
156
175
name = " toPlatform" ,
157
- contextReceiver = declaration.name.fullName (),
176
+ contextReceiver = declaration.name.safeFullName (),
158
177
returnType = platformType,
159
178
) {
160
179
scope(" return $platformType .newBuilder().apply" , " .build()" ) {
161
180
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
+ }
164
189
}
165
190
}
166
191
}
167
192
168
193
function(
169
194
name = " toKotlin" ,
170
195
contextReceiver = platformType,
171
- returnType = declaration.name.fullName (),
196
+ returnType = declaration.name.safeFullName (),
172
197
) {
173
198
scope(" return ${declaration.name.simpleName} " ) {
174
199
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
+ }
176
215
}
177
216
}
178
217
}
@@ -185,7 +224,11 @@ class ModelToKotlinGenerator(
185
224
FieldType .IntegralType .UINT32 -> " .toInt()"
186
225
FieldType .IntegralType .UINT64 -> " .toLong()"
187
226
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
+
189
232
else -> " "
190
233
}
191
234
}
@@ -197,7 +240,11 @@ class ModelToKotlinGenerator(
197
240
FieldType .IntegralType .UINT32 -> " .toUInt()"
198
241
FieldType .IntegralType .UINT64 -> " .toULong()"
199
242
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
+
201
248
else -> " "
202
249
}
203
250
}
@@ -211,7 +258,7 @@ class ModelToKotlinGenerator(
211
258
// KRPC-156 Reference Types
212
259
is FieldType .Reference -> {
213
260
val value by type.value
214
- value.fullName ()
261
+ value.safeFullName ()
215
262
}
216
263
217
264
is FieldType .IntegralType -> {
@@ -230,7 +277,11 @@ class ModelToKotlinGenerator(
230
277
else -> {
231
278
error(" Unsupported type: $type " )
232
279
}
233
- }
280
+ }.withNullability(nullable)
281
+ }
282
+
283
+ private fun String.withNullability (nullable : Boolean ): String {
284
+ return " $this${if (nullable) " ?" else " " } "
234
285
}
235
286
236
287
@Suppress(" unused" )
@@ -278,11 +329,11 @@ class ModelToKotlinGenerator(
278
329
279
330
@Suppress(" unused" )
280
331
private fun CodeGenerator.generateInternalEnum (declaration : EnumDeclaration ) {
281
- val platformType = " ${declaration.outerClassName.fullName ()} .${declaration.name.simpleName} "
332
+ val platformType = " ${declaration.outerClassName.safeFullName ()} .${declaration.name.simpleName} "
282
333
283
334
function(
284
335
name = " toPlatform" ,
285
- contextReceiver = declaration.name.fullName (),
336
+ contextReceiver = declaration.name.safeFullName (),
286
337
returnType = platformType,
287
338
) {
288
339
scope(" return when (this)" ) {
@@ -299,7 +350,7 @@ class ModelToKotlinGenerator(
299
350
function(
300
351
name = " toKotlin" ,
301
352
contextReceiver = platformType,
302
- returnType = declaration.name.fullName (),
353
+ returnType = declaration.name.safeFullName (),
303
354
) {
304
355
scope(" return when (this)" ) {
305
356
declaration.aliases.forEach { field ->
@@ -324,8 +375,8 @@ class ModelToKotlinGenerator(
324
375
function(
325
376
name = method.name,
326
377
modifiers = " suspend" ,
327
- args = " message: ${inputType.name.fullName ()} " ,
328
- returnType = outputType.name.fullName (),
378
+ args = " message: ${inputType.name.safeFullName ()} " ,
379
+ returnType = outputType.name.safeFullName (),
329
380
)
330
381
}
331
382
}
@@ -337,7 +388,7 @@ class ModelToKotlinGenerator(
337
388
modifiers = " private" ,
338
389
name = " ${service.name.simpleName} Delegate" ,
339
390
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 ()} >" ),
341
392
) {
342
393
function(
343
394
name = " clientProvider" ,
@@ -351,7 +402,7 @@ class ModelToKotlinGenerator(
351
402
function(
352
403
name = " definitionFor" ,
353
404
modifiers = " override" ,
354
- args = " impl: ${service.name.fullName ()} " ,
405
+ args = " impl: ${service.name.safeFullName ()} " ,
355
406
returnType = " kotlinx.rpc.grpc.ServerServiceDefinition" ,
356
407
) {
357
408
scope(" return ${service.name.simpleName} ServerDelegate(impl).bindService()" )
@@ -364,7 +415,7 @@ class ModelToKotlinGenerator(
364
415
name = " ${service.name.simpleName} ServerDelegate" ,
365
416
declarationType = DeclarationType .Class ,
366
417
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 ()} " ),
368
419
) {
369
420
service.methods.forEach { method ->
370
421
val grpcName = method.name.replaceFirstChar { it.lowercase() }
@@ -379,6 +430,9 @@ class ModelToKotlinGenerator(
379
430
returnType = outputType.toPlatformMessageType(),
380
431
) {
381
432
code(" return impl.${method.name} (request.toKotlin()).toPlatform()" )
433
+
434
+ importRootDeclarationIfNeeded(inputType.name, " toPlatform" , true )
435
+ importRootDeclarationIfNeeded(outputType.name, " toKotlin" , true )
382
436
}
383
437
}
384
438
}
@@ -415,9 +469,13 @@ class ModelToKotlinGenerator(
415
469
scope(" return when (call.callableName)" ) {
416
470
service.methods.forEach { method ->
417
471
val inputType by method.inputType
472
+ val outputType by method.outputType
418
473
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())"
420
475
code(" \" ${method.name} \" -> $result .toKotlin() as R" )
476
+
477
+ importRootDeclarationIfNeeded(inputType.name, " toPlatform" , true )
478
+ importRootDeclarationIfNeeded(outputType.name, " toKotlin" , true )
421
479
}
422
480
423
481
code(" else -> error(\" Illegal call: \$ {call.callableName}\" )" )
@@ -437,7 +495,26 @@ class ModelToKotlinGenerator(
437
495
}
438
496
439
497
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
+ }
441
518
}
442
519
}
443
520
0 commit comments