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

main.de.jensklingenberg.ktorfit.poetspec.ImplClassSpec2.kt Maven / Gradle / Ivy

The newest version!
package de.jensklingenberg.ktorfit.poetspec

import com.google.devtools.ksp.processing.Resolver
import com.google.devtools.ksp.symbol.KSPropertyDeclaration
import com.google.devtools.ksp.symbol.KSTypeReference
import com.squareup.kotlinpoet.AnnotationSpec
import com.squareup.kotlinpoet.ClassName
import com.squareup.kotlinpoet.CodeBlock
import com.squareup.kotlinpoet.FunSpec
import com.squareup.kotlinpoet.KModifier
import com.squareup.kotlinpoet.PropertySpec
import com.squareup.kotlinpoet.TypeSpec
import com.squareup.kotlinpoet.ksp.toAnnotationSpec
import com.squareup.kotlinpoet.ksp.toTypeName
import de.jensklingenberg.ktorfit.KtorfitOptions
import de.jensklingenberg.ktorfit.model.ClassData
import de.jensklingenberg.ktorfit.model.KtorfitError.Companion.PROPERTIES_NOT_SUPPORTED
import de.jensklingenberg.ktorfit.model.converterHelper
import de.jensklingenberg.ktorfit.model.internalApi
import de.jensklingenberg.ktorfit.model.ktorfitClass
import de.jensklingenberg.ktorfit.model.toClassName

/**
 * Transform a [ClassData] to a [FileSpec] for KotlinPoet
 */
fun ClassData.getImplClassSpec(
    resolver: Resolver,
    ktorfitOptions: KtorfitOptions,
): TypeSpec {
    val classData = this

    val implClassProperties =
        classData.properties.map { property ->
            propertySpec(property)
        }

    val implClassSpec =
        createImplClassTypeSpec(
            classData.implName,
            classData,
            implClassProperties,
            classData.functions.map {
                it.toFunSpec(
                    resolver,
                    ktorfitOptions.setQualifiedType
                )
            }
        )

    return implClassSpec
}

private fun createImplClassTypeSpec(
    implClassName: String,
    classData: ClassData,
    implClassProperties: List,
    funSpecs: List
): TypeSpec {
    val optInAnnotations =
        classData.annotations.filter { it.shortName.getShortName() == "OptIn" }.map { it.toAnnotationSpec() }
    val helperProperty =
        PropertySpec
            .builder(converterHelper.objectName, converterHelper.toClassName())
            .initializer("${converterHelper.name}(${ktorfitClass.objectName})")
            .addModifiers(KModifier.PRIVATE)
            .build()

    val internalApiAnnotation =
        AnnotationSpec
            .builder(ClassName("kotlin", "OptIn"))
            .addMember(
                "%T::class",
                internalApi,
            ).build()
    return TypeSpec
        .classBuilder(implClassName)
        .addAnnotations(optInAnnotations + internalApiAnnotation)
        .addModifiers(classData.modifiers)
        .primaryConstructor(
            FunSpec
                .constructorBuilder()
                .addParameter(ktorfitClass.objectName, ktorfitClass.toClassName())
                .build(),
        ).addProperty(
            PropertySpec
                .builder(ktorfitClass.objectName, ktorfitClass.toClassName())
                .initializer(ktorfitClass.objectName)
                .addModifiers(KModifier.PRIVATE)
                .build(),
        ).addSuperinterface(ClassName(classData.packageName, classData.name))
        .addKtorfitSuperInterface(classData.superClasses)
        .addProperties(listOf(helperProperty) + implClassProperties)
        .addFunctions(funSpecs)
        .build()
}

private fun propertySpec(property: KSPropertyDeclaration): PropertySpec {
    val propBuilder =
        PropertySpec
            .builder(
                property.simpleName.asString(),
                property.type.toTypeName(),
            ).addModifiers(KModifier.OVERRIDE)
            .mutable(property.isMutable)
            .getter(
                FunSpec
                    .getterBuilder()
                    .addStatement(PROPERTIES_NOT_SUPPORTED)
                    .build(),
            )

    if (property.isMutable) {
        propBuilder.setter(
            FunSpec
                .setterBuilder()
                .addParameter("value", property.type.toTypeName())
                .build(),
        )
    }

    return propBuilder.build()
}

/**
 * Support for extending multiple interfaces, is done with Kotlin delegation.
 * For every know class of [classDataList], there will
 * be a generated implementation for each interface that we can use.
 * @param superClasses List of qualifiedNames of interface that a Ktorfit interface extends
 */
private fun TypeSpec.Builder.addKtorfitSuperInterface(superClasses: List): TypeSpec.Builder {
    (superClasses).forEach { superClassReference ->
        val hasNoDelegationAnnotation = superClassReference.annotations.any { it.shortName.getShortName() == "NoDelegation" }

        val superClassDeclaration = superClassReference.resolve().declaration
        val superTypeClassName = superClassDeclaration.simpleName.asString()
        val superTypePackage = superClassDeclaration.packageName.asString()
        if (!hasNoDelegationAnnotation) {
            this.addSuperinterface(
                ClassName(superTypePackage, superTypeClassName),
                CodeBlock.of(
                    "%L._%LImpl(${ktorfitClass.objectName})",
                    superTypePackage,
                    superTypeClassName,
                )
            )
        }
    }

    return this
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy