All Downloads are FREE. Search and download functionalities are using the official Maven repository.

kotlinx.rpc.codegen.extension.RPCDeclarationScanner.kt Maven / Gradle / Ivy

There is a newer version: 2.0.21-0.4.0
Show newest version
/*
 * Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
 */

package kotlinx.rpc.codegen.extension

import kotlinx.rpc.codegen.common.RpcNames
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.IrProperty
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.types.classOrNull
import org.jetbrains.kotlin.ir.util.dumpKotlinLike
import org.jetbrains.kotlin.ir.util.packageFqName

/**
 * This class scans user declared RPC service
 * and returns all necessary information for code generation by [RPCStubGenerator].
 *
 * Some checks are preformed during scanning,
 * but all user-friendly errors are expected to be thrown by frontend plugins
 */
internal object RPCDeclarationScanner {
    fun scanServiceDeclaration(service: IrClass, ctx: RPCIrContext): ServiceDeclaration {
        var stubClass: IrClass? = null

        val declarations = service.declarations.memoryOptimizedMap { declaration ->
            when (declaration) {
                is IrSimpleFunction -> {
                    if (declaration.isFakeOverride) {
                        return@memoryOptimizedMap null
                    }

                    ServiceDeclaration.Method(
                        function = declaration,
                        arguments = declaration.valueParameters.memoryOptimizedMap { param ->
                            ServiceDeclaration.Method.Argument(param, param.type)
                        },
                    )
                }

                is IrProperty -> {
                    if (declaration.isFakeOverride) {
                        return@memoryOptimizedMap null
                    }

                    val symbol = declaration.getter!!.returnType.classOrNull

                    val flowType = when (symbol) {
                        ctx.flow -> ServiceDeclaration.FlowField.Kind.Plain
                        ctx.sharedFlow -> ServiceDeclaration.FlowField.Kind.Shared
                        ctx.stateFlow -> ServiceDeclaration.FlowField.Kind.State
                        else -> unsupportedDeclaration(service, declaration)
                    }

                    ServiceDeclaration.FlowField(declaration, flowType)
                }

                is IrClass -> {
                    if (declaration.name == RpcNames.SERVICE_STUB_NAME) {
                        stubClass = declaration
                        return@memoryOptimizedMap null
                    }

                    unsupportedDeclaration(service, declaration)
                }

                else -> {
                    unsupportedDeclaration(service, declaration)
                }
            }
        }

        val packageName = service.packageFqName?.asString()
            ?: error("Expected package name of the ${service.name.asString()}")

        val stubClassNotNull = stubClass
            // only for KSP generation
            ?: ctx.getIrClassSymbol(
                packageName = packageName,
                name = "${service.name.asString()}${RpcNames.SERVICE_STUB_NAME_KSP.asString()}"
            ).owner

        return ServiceDeclaration(
            service = service,
            stubClass = stubClassNotNull,
            methods = declarations.filterIsInstance(),
            fields = declarations.filterIsInstance(),
        )
    }
}

private fun unsupportedDeclaration(service: IrClass, declaration: IrDeclaration): Nothing {
    error("Unsupported declaration in RPC interface ${service.name}: ${declaration.dumpKotlinLike()}")
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy