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

jvmMain.com.caesarealabs.rpc4k.processor.Rpc4kProcessorProvider.kt Maven / Gradle / Ivy

The newest version!
package com.caesarealabs.rpc4k.processor

import com.caesarealabs.rpc4k.processor.utils.checkRequirement
import com.caesarealabs.rpc4k.processor.utils.findDuplicate
import com.caesarealabs.rpc4k.processor.utils.getClassesWithAnnotation
import com.caesarealabs.rpc4k.processor.utils.getQualifiedName
import com.caesarealabs.rpc4k.runtime.user.Api
import com.google.devtools.ksp.processing.Resolver
import com.google.devtools.ksp.processing.SymbolProcessor
import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
import com.google.devtools.ksp.processing.SymbolProcessorProvider
import com.google.devtools.ksp.symbol.KSAnnotated
import com.google.devtools.ksp.symbol.KSClassDeclaration
import com.squareup.kotlinpoet.ksp.writeTo
import kotlin.system.measureTimeMillis

internal class Rpc4kProcessorProvider : SymbolProcessorProvider {
    override fun create(
        environment: SymbolProcessorEnvironment
    ): SymbolProcessor = Rpc4kProcessor(environment)
}

internal class Rpc4kProcessor(private val env: SymbolProcessorEnvironment) : SymbolProcessor {
    private var processed = false

    override fun process(resolver: Resolver): List = with(env) {
        if (processed) return listOf()
        processed = true
        val validator = ApiClassValidator(env, resolver)


        val time = measureTimeMillis {
            val apiClasses = resolver.getClassesWithAnnotation(Api::class)
                .filter { validator.validate(it) }
                .toHashSet()

            for (symbol in apiClasses) {
                generateRpc(symbol, resolver)
            }
        }
        env.logger.warn("Generating RPC Classes took ${time}ms")

        // Nothing needs to be deferred
        return listOf()
    }


    private fun SymbolProcessorEnvironment.generateRpc(apiClass: KSClassDeclaration, resolver: Resolver) {
        val time = measureTimeMillis {
            val api = KspToApiDefinition(resolver).toApiDefinition(apiClass)
            if (api == null) {
                env.logger.warn("Source code is invalid, code won't be generated for ${apiClass.getQualifiedName()}.")
                return
            }

            val nameDuplicate = api.models.findDuplicate { it.name.substringAfterLast(".") }

            // Since models are not namespaced, they cannot contain duplicate names
            apiClass.checkRequirement(env, nameDuplicate == null) { "There's two types with the name '$nameDuplicate', which is not allowed" }
            if (nameDuplicate != null) return

            val file = apiClass.containingFile!!
            val files = listOf(file)
            //LOWPRIO: Improve server testing with "in-memory-server" client generation
            // ApiDefinitionToClientInterface.convert(api).writeTo(codeGenerator, false, files)
            ApiDefinitionToNetworkClient.convert(api).writeTo(codeGenerator, false, files)
            ApiDefinitionToServerCode(api).convert().writeTo(codeGenerator, false, files)
            ApiDefinitionWriter.writeRpcJsons(api, codeGenerator, file)
        }

        env.logger.warn("Generated RPC classes for: ${apiClass.qualifiedName!!.asString()} in $time millis")
    }

}

internal fun KSClassDeclaration.shouldGenerateClient(): Boolean {
    // Checks if the @Api annotation has the only argument (generateClient) set to true
    return annotations.first { it.shortName.asString() == Api::class.simpleName }.arguments[0].value == true
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy