Skip to content

Commit

Permalink
Code optimization (#668)
Browse files Browse the repository at this point in the history
  • Loading branch information
Foso authored Sep 6, 2024
1 parent 7512827 commit 315969d
Show file tree
Hide file tree
Showing 10 changed files with 119 additions and 83 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ fun generateImplClass(
with(classData) {
val fileSource = classData.getImplClassSpec(resolver, ktorfitOptions).toString()

val fileName = "_${name}Impl"
val fileName = classData.implName
val commonMainModuleName = "commonMain"
val moduleName =
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import de.jensklingenberg.ktorfit.model.annotations.FormUrlEncoded
import de.jensklingenberg.ktorfit.model.annotations.Multipart
import de.jensklingenberg.ktorfit.model.annotations.ParameterAnnotation
import de.jensklingenberg.ktorfit.model.annotations.ParameterAnnotation.Field
import de.jensklingenberg.ktorfit.utils.getKsFile

/**
* @param name of the interface that contains annotations
Expand All @@ -29,7 +30,10 @@ data class ClassData(
val properties: List<KSPropertyDeclaration> = emptyList(),
val modifiers: List<KModifier> = emptyList(),
val ksFile: KSFile,
)
) {
val implName = "_${name}Impl"
val providerName = "_${name}Provider"
}

/**
* Convert a [KSClassDeclaration] to [ClassData]
Expand Down Expand Up @@ -127,5 +131,3 @@ private fun checkClassForErrors(
return
}
}

private fun KSClassDeclaration.getKsFile(): KSFile = this.containingFile ?: throw Error("Containing File for ${this.simpleName} was null")
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package de.jensklingenberg.ktorfit.model

import com.google.devtools.ksp.processing.KSPLogger
import com.google.devtools.ksp.symbol.KSFunctionDeclaration
import com.squareup.kotlinpoet.KModifier
import de.jensklingenberg.ktorfit.model.annotations.FunctionAnnotation
import de.jensklingenberg.ktorfit.model.annotations.HttpMethod
import de.jensklingenberg.ktorfit.model.annotations.HttpMethodAnnotation
Expand All @@ -12,6 +13,7 @@ import de.jensklingenberg.ktorfit.model.annotations.ParameterAnnotation.FieldMap
import de.jensklingenberg.ktorfit.model.annotations.ParameterAnnotation.Path
import de.jensklingenberg.ktorfit.model.annotations.ParameterAnnotation.Url
import de.jensklingenberg.ktorfit.model.annotations.ParameterAnnotation.RequestBuilder
import de.jensklingenberg.ktorfit.poetspec.findTypeName
import de.jensklingenberg.ktorfit.utils.anyInstance
import de.jensklingenberg.ktorfit.utils.getFormUrlEncodedAnnotation
import de.jensklingenberg.ktorfit.utils.getHeaderAnnotation
Expand All @@ -28,6 +30,7 @@ data class FunctionData(
val parameterDataList: List<ParameterData>,
val annotations: List<FunctionAnnotation> = emptyList(),
val httpMethodAnnotation: HttpMethodAnnotation,
val modifiers: List<KModifier> = emptyList()
)

/**
Expand Down Expand Up @@ -59,6 +62,7 @@ fun KSFunctionDeclaration.toFunctionData(logger: KSPLogger): FunctionData {
ReturnTypeData(
name = resolvedReturnType.resolveTypeName(),
parameterType = resolvedReturnType,
typeName = findTypeName(resolvedReturnType, funcDeclaration.containingFile!!.filePath),
)

val functionAnnotationList = mutableListOf<FunctionAnnotation>()
Expand Down Expand Up @@ -215,12 +219,20 @@ fun KSFunctionDeclaration.toFunctionData(logger: KSPLogger): FunctionData {
)
}

val modifiers =
mutableListOf(KModifier.OVERRIDE).also {
if (this.isSuspend) {
it.add(KModifier.SUSPEND)
}
}

return FunctionData(
functionName,
returnType,
funcDeclaration.isSuspend,
functionParameters,
functionAnnotationList,
firstHttpMethodAnnotation,
modifiers
)
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package de.jensklingenberg.ktorfit.model

import com.google.devtools.ksp.containingFile
import com.google.devtools.ksp.processing.KSPLogger
import com.google.devtools.ksp.symbol.KSValueParameter
import de.jensklingenberg.ktorfit.model.annotations.ParameterAnnotation
import de.jensklingenberg.ktorfit.model.annotations.ParameterAnnotation.RequestBuilder
import de.jensklingenberg.ktorfit.model.annotations.getParamAnnotationList
import de.jensklingenberg.ktorfit.poetspec.findTypeName
import de.jensklingenberg.ktorfit.utils.anyInstance
import de.jensklingenberg.ktorfit.utils.resolveTypeName

Expand Down Expand Up @@ -49,11 +51,13 @@ fun KSValueParameter.createParameterData(logger: KSPLogger): ParameterData {
ReturnTypeData(
"HttpRequestBuilder.()->Unit",
parameterType,
findTypeName(parameterType, ksValueParameter.containingFile!!.filePath)
)
} else {
ReturnTypeData(
parameterType.resolveTypeName(),
parameterType,
findTypeName(parameterType, ksValueParameter.containingFile!!.filePath)
)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package de.jensklingenberg.ktorfit.model

import com.google.devtools.ksp.symbol.KSType
import com.squareup.kotlinpoet.TypeName

data class ReturnTypeData(
val name: String,
val parameterType: KSType,
val typeName: TypeName? = null
)
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ fun ClassData.getImplClassSpec(
ktorfitOptions: KtorfitOptions,
): FileSpec {
val classData = this
val optinAnnotation =
val internalApiAnnotation =
AnnotationSpec
.builder(ClassName("kotlin", "OptIn"))
.addMember(
Expand All @@ -47,11 +47,6 @@ fun ClassData.getImplClassSpec(

val createExtensionFunctionSpec = getCreateExtensionFunctionSpec(classData)

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

val helperProperty =
PropertySpec
.builder(converterHelper.objectName, converterHelper.toClassName())
Expand All @@ -60,63 +55,83 @@ fun ClassData.getImplClassSpec(
.build()

val providerClass =
TypeSpec
.classBuilder("_${classData.name}Provider")
.addModifiers(classData.modifiers)
.addSuperinterface(
providerClass.toClassName().parameterizedBy(ClassName(classData.packageName, classData.name)),
).addFunction(
FunSpec
.builder("create")
.returns(ClassName(classData.packageName, classData.name))
.addStatement("return _${classData.name}Impl(${ktorfitClass.objectName})")
.addModifiers(KModifier.OVERRIDE)
.addParameter(ktorfitClass.objectName, ktorfitClass.toClassName())
.build(),
).build()
createProviderClassSpec(classData)

val implClassName = "_${classData.name}Impl"
val implClassName = classData.implName
val implClassProperties =
classData.properties.map { property ->
propertySpec(property)
}

val implClassSpec =
TypeSpec
.classBuilder(implClassName)
.primaryConstructor(
FunSpec
.constructorBuilder()
.addParameter(ktorfitClass.objectName, ktorfitClass.toClassName())
.build(),
).addProperty(
PropertySpec
.builder(ktorfitClass.objectName, ktorfitClass.toClassName())
.initializer(ktorfitClass.objectName)
.addModifiers(KModifier.PRIVATE)
.build(),
).addAnnotation(optinAnnotation)
.addModifiers(classData.modifiers)
.addSuperinterface(ClassName(classData.packageName, classData.name))
.addKtorfitSuperInterface(classData.superClasses)
.addProperties(listOf(helperProperty) + implClassProperties)
.addFunctions(
classData.functions.map {
it.toFunSpec(
resolver,
ktorfitOptions.setQualifiedType,
ksFile.filePath
)
}
).build()
createImplClassTypeSpec(
implClassName,
internalApiAnnotation,
classData,
helperProperty,
implClassProperties,
classData.functions.map {
it.toFunSpec(
resolver,
ktorfitOptions.setQualifiedType
)
}
)

return FileSpec
.builder(classData.packageName, implClassName)
.addAnnotation(suppressAnnotation)
.addFileComment("Generated by Ktorfit")
.addImports(classData.imports)
.addType(implClassSpec)
.addType(providerClass)
.addTypes(listOf(implClassSpec, providerClass))
.addFunction(createExtensionFunctionSpec)
.build()
}

private fun createProviderClassSpec(classData: ClassData) =
TypeSpec
.classBuilder(classData.providerName)
.addModifiers(classData.modifiers)
.addSuperinterface(
providerClass.toClassName().parameterizedBy(ClassName(classData.packageName, classData.name)),
).addFunction(
FunSpec
.builder("create")
.addModifiers(KModifier.OVERRIDE)
.addParameter(ktorfitClass.objectName, ktorfitClass.toClassName())
.addStatement("return ${classData.implName}(${ktorfitClass.objectName})")
.returns(ClassName(classData.packageName, classData.name))
.build(),
).build()

private fun createImplClassTypeSpec(
implClassName: String,
internalApiAnnotation: AnnotationSpec,
classData: ClassData,
helperProperty: PropertySpec,
implClassProperties: List<PropertySpec>,
funSpecs: List<FunSpec>
) = TypeSpec
.classBuilder(implClassName)
.addAnnotation(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
Expand Down Expand Up @@ -174,7 +189,7 @@ private fun getCreateExtensionFunctionSpec(classData: ClassData): FunSpec {
return FunSpec
.builder(functionName)
.addModifiers(classData.modifiers)
.addStatement("return _${classData.name}Impl(this)")
.addStatement("return ${classData.implName}(this)")
.receiver(ktorfitClass.toClassName())
.returns(ClassName(classData.packageName, classData.name))
.build()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package de.jensklingenberg.ktorfit.poetspec

import com.google.devtools.ksp.KspExperimental
import com.google.devtools.ksp.getKotlinClassByName
import com.google.devtools.ksp.processing.Resolver
import com.squareup.kotlinpoet.FunSpec
import com.squareup.kotlinpoet.KModifier
import com.squareup.kotlinpoet.TypeName
import de.jensklingenberg.ktorfit.model.FunctionData
import de.jensklingenberg.ktorfit.model.converterHelper
Expand All @@ -15,47 +16,49 @@ import de.jensklingenberg.ktorfit.utils.removeWhiteSpaces
fun FunctionData.toFunSpec(
resolver: Resolver,
setQualifiedTypeName: Boolean,
filePath: String,
): FunSpec {
val returnTypeName = findTypeName(returnType.parameterType, filePath)
val returnTypeName = returnType.typeName ?: throw IllegalStateException("Return type not found")

return FunSpec
.builder(this.name)
.addModifiers(
mutableListOf(KModifier.OVERRIDE).also {
if (this.isSuspend) {
it.add(KModifier.SUSPEND)
}
},
).returns(returnTypeName)
.builder(name)
.addModifiers(modifiers)
.addParameters(
parameterDataList.map {
it.parameterSpec(filePath)
it.parameterSpec()
},
).addBody(this, resolver, setQualifiedTypeName, returnTypeName)
.returns(returnTypeName)
.build()
}

@OptIn(KspExperimental::class)
private fun FunSpec.Builder.addBody(
functionData: FunctionData,
resolver: Resolver,
setQualifiedTypeName: Boolean,
returnTypeName: TypeName
) = apply {
val listType =
resolver.getKotlinClassByName("kotlin.collections.List")?.asStarProjectedType() ?: error("List not found")

val arrayType = resolver.builtIns.arrayType.starProjection()
addRequestConverterText(functionData.parameterDataList)
.addStatement(
getReqBuilderExtensionText(
functionData,
resolver,
listType,
arrayType,
),
).addStatement(
"val ${typeDataClass.objectName} = ${typeDataClass.name}.createTypeData(",
"val ${typeDataClass.objectName} = ${typeDataClass.name}.createTypeData("
).addStatement("typeInfo = typeInfo<%T>(),", returnTypeName)
.addStatement(
if (setQualifiedTypeName) {
"qualifiedTypename = \"${
returnTypeName.toString().removeWhiteSpaces()
}\")"
buildString {
append("qualifiedTypename = \"")
append(returnTypeName.toString().removeWhiteSpaces())
append("\")")
}
} else {
")"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package de.jensklingenberg.ktorfit.poetspec
import com.squareup.kotlinpoet.ParameterSpec
import de.jensklingenberg.ktorfit.model.ParameterData

fun ParameterData.parameterSpec(filePath: String): ParameterSpec {
val parameterType = findTypeName(this.type.parameterType, filePath)
fun ParameterData.parameterSpec(): ParameterSpec {
val parameterType = this.type.typeName ?: throw IllegalStateException("Type ${this.name} not found")
return ParameterSpec(this.name, parameterType)
}
Original file line number Diff line number Diff line change
@@ -1,24 +1,18 @@
package de.jensklingenberg.ktorfit.reqBuilderExtension

import com.google.devtools.ksp.KspExperimental
import com.google.devtools.ksp.getKotlinClassByName
import com.google.devtools.ksp.processing.Resolver
import com.google.devtools.ksp.symbol.KSType
import de.jensklingenberg.ktorfit.model.FunctionData
import de.jensklingenberg.ktorfit.model.extDataClass

/**
* This will generate the code for the HttpRequestBuilder
*/
@OptIn(KspExperimental::class)
fun getReqBuilderExtensionText(
functionData: FunctionData,
resolver: Resolver,
listType: KSType,
arrayType: KSType,
): String {
val method = getMethodCode(functionData.httpMethodAnnotation)
val listType =
resolver.getKotlinClassByName("kotlin.collections.List")?.asStarProjectedType() ?: error("List not found")

val arrayType = resolver.builtIns.arrayType.starProjection()

val headers =
getHeadersCode(
Expand Down
Loading

0 comments on commit 315969d

Please sign in to comment.