Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -797,23 +797,15 @@ internal class RpcStubGenerator(
type = ctx.rpcCallable.typeWith(declaration.serviceType),
symbol = ctx.rpcCallableDefault.constructors.single(),
typeArgumentsCount = 1,
valueArgumentsCount = 5,
valueArgumentsCount = 6,
constructorTypeArgumentsCount = 1,
)
}.apply {
putConstructorTypeArgument(0, declaration.serviceType)

callable as ServiceDeclaration.Method

val returnType = when {
callable.function.isNonSuspendingWithFlowReturn() -> {
(callable.function.returnType as IrSimpleType).arguments.single().typeOrFail
}

else -> {
callable.function.returnType
}
}
val returnType = callable.function.returnType

val invokator = invokators[callable.name]
?: error("Expected invokator for ${callable.name} in ${declaration.service.name}")
Expand Down Expand Up @@ -889,6 +881,8 @@ internal class RpcStubGenerator(
}
}

val returnsFlowFlag = (callable.function.returnType.classOrNull == ctx.flow)

arguments {
values {
+stringConst(callable.name)
Expand All @@ -900,6 +894,7 @@ internal class RpcStubGenerator(
+arrayOfCall

+booleanConst(!callable.function.isSuspend)
+booleanConst(returnsFlowFlag)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ public interface RpcCallable<@Rpc T : Any> {
public val invokator: RpcInvokator<T>
public val parameters: Array<out RpcParameter>
public val isNonSuspendFunction: Boolean
/**
* True if the method returns Flow<...> and should be treated as a streaming return.
* The [returnType] remains the original declared KType (including Flow<...>),
* consumers can use this flag to branch logic without relying on type unwrapping.
*/
public val returnsFlow: Boolean
}

@ExperimentalRpcApi
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public class RpcCallableDefault<@Rpc T : Any>(
override val invokator: RpcInvokator<T>,
override val parameters: Array<out RpcParameter>,
override val isNonSuspendFunction: Boolean,
override val returnsFlow: Boolean,
) : RpcCallable<T>

@InternalRpcApi
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package kotlinx.rpc.descriptor

import kotlinx.coroutines.flow.Flow
import kotlinx.rpc.annotations.Rpc
import kotlin.reflect.typeOf
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue

@Rpc
interface NewsServiceForFlowTest {
fun stream(): Flow<String>
suspend fun greet(name: String): String
}

class IsFlowIntegrationTest {
@Test
fun stream_isFlow_and_returnType_is_Flow() {
val descriptor = serviceDescriptorOf<NewsServiceForFlowTest>()
val stream = descriptor.callables["stream"] ?: error("stream not found")

assertEquals(typeOf<Flow<String>>().toString(), stream.returnType.kType.toString())
assertTrue(stream.returnsFlow)
assertTrue(stream.isNonSuspendFunction)
}

@Test
fun greet_notFlow_and_returnType_is_String() {
val descriptor = serviceDescriptorOf<NewsServiceForFlowTest>()
val greet = descriptor.callables["greet"] ?: error("greet not found")

assertEquals(typeOf<String>().toString(), greet.returnType.kType.toString())
assertFalse(greet.returnsFlow)
// greet is suspend now
kotlin.test.assertFalse(greet.isNonSuspendFunction)
}
}