Skip to content

Commit

Permalink
Extract assisted factory dependency matching code
Browse files Browse the repository at this point in the history
  • Loading branch information
vganin authored and daugeldauge committed Dec 5, 2023
1 parent 9fa2d34 commit 64f84c2
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 73 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.daugeldauge.kinzhal.processor

import com.daugeldauge.kinzhal.annotations.Assisted
import com.daugeldauge.kinzhal.processor.model.AssistedFactoryType
import com.daugeldauge.kinzhal.processor.model.Key
import com.google.devtools.ksp.symbol.KSFunctionDeclaration
import com.google.devtools.ksp.symbol.KSValueParameter

internal class AssistedFactoryDependency(
val name: String,
val key: Key,
val isAssisted: Boolean,
val parameter: KSValueParameter,
)

internal object AssistedFactoryDependenciesResolver {
fun match(
sourceDeclaration: KSFunctionDeclaration,
assistedFactoryType: AssistedFactoryType,
): List<AssistedFactoryDependency> {
val dependencies = sourceDeclaration.parameters.map {
val isAssisted = it.isAssisted
AssistedFactoryDependency(
name = if (isAssisted) {
it.name!!.asString()
} else {
"${it.name!!.asString()}Provider"
},
key = it.type.toKey(it.annotations),
isAssisted = isAssisted,
parameter = it
)
}

val assistedSourceParameters = dependencies
.asSequence()
.filter(AssistedFactoryDependency::isAssisted)
.map { it.name to it.key.type }
.toList()

val assistedFactoryParameters = assistedFactoryType.factoryMethod.parameters
.map { it.name?.asString() to it.type.resolve() }

if (assistedSourceParameters != assistedFactoryParameters) {
throw NonRecoverableProcessorException(
"Factory method in @AssistedFactory must have assisted parameters by same names, types and in the same order as @AssistedInject constructor",
assistedFactoryType.factoryMethod
)
}

return dependencies
}
}

private val KSValueParameter.isAssisted: Boolean
get() = annotations.any {
it.annotationType.resolve().declaration.qualifiedName?.asString() == Assisted::class.requireQualifiedName()
}
Original file line number Diff line number Diff line change
@@ -1,22 +1,13 @@
package com.daugeldauge.kinzhal.processor

import com.daugeldauge.kinzhal.annotations.AssistedFactory
import com.daugeldauge.kinzhal.annotations.AssistedInject
import com.daugeldauge.kinzhal.annotations.Component
import com.daugeldauge.kinzhal.annotations.Inject
import com.daugeldauge.kinzhal.annotations.Qualifier
import com.daugeldauge.kinzhal.annotations.*
import com.daugeldauge.kinzhal.processor.generation.*
import com.daugeldauge.kinzhal.processor.generation.asTypeName
import com.daugeldauge.kinzhal.processor.generation.capitalized
import com.daugeldauge.kinzhal.processor.generation.generateComponent
import com.daugeldauge.kinzhal.processor.generation.generateFactory
import com.daugeldauge.kinzhal.processor.model.*
import com.google.devtools.ksp.isAbstract
import com.google.devtools.ksp.isConstructor
import com.google.devtools.ksp.processing.*
import com.google.devtools.ksp.symbol.*
import com.squareup.kotlinpoet.*
import java.lang.RuntimeException
import com.squareup.kotlinpoet.ClassName

// TODO scope validation
// TODO fix possible conflicts in component provider names
Expand Down Expand Up @@ -100,7 +91,11 @@ internal class KinzhalSymbolProcessor(private val codeGenerator: CodeGenerator,
addCreateInstanceCall = { add("%T", injectableKey.asTypeName()) },
packageName = assistedFactoryType.type.declaration.packageName.asString(),
factoryBaseName = assistedFactoryType.type.declaration.simpleName.asString(),
assistedFactoryType = assistedFactoryType
assistedFactoryType = assistedFactoryType,
dependencies = AssistedFactoryDependenciesResolver.match(
sourceDeclaration = injectable,
assistedFactoryType = assistedFactoryType,
)
)
}
.toList()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,13 @@
package com.daugeldauge.kinzhal.processor.generation

import com.daugeldauge.kinzhal.annotations.Assisted
import com.daugeldauge.kinzhal.processor.NonRecoverableProcessorException
import com.daugeldauge.kinzhal.processor.AssistedFactoryDependency
import com.daugeldauge.kinzhal.processor.model.AssistedFactoryType
import com.daugeldauge.kinzhal.processor.model.FactoryBinding
import com.daugeldauge.kinzhal.processor.model.Key
import com.daugeldauge.kinzhal.processor.requireQualifiedName
import com.daugeldauge.kinzhal.processor.toKey
import com.google.devtools.ksp.processing.CodeGenerator
import com.google.devtools.ksp.symbol.KSFunctionDeclaration
import com.google.devtools.ksp.symbol.KSValueParameter
import com.squareup.kotlinpoet.ClassName
import com.squareup.kotlinpoet.CodeBlock
import com.squareup.kotlinpoet.FunSpec
import com.squareup.kotlinpoet.KModifier
import com.squareup.kotlinpoet.LambdaTypeName
import com.squareup.kotlinpoet.ParameterSpec
import com.squareup.kotlinpoet.PropertySpec
import com.squareup.kotlinpoet.TypeName
import com.squareup.kotlinpoet.TypeSpec
import com.squareup.kotlinpoet.*
import com.squareup.kotlinpoet.ksp.toTypeName
import com.squareup.kotlinpoet.withIndent

internal fun generateAssistedFactory(
codeGenerator: CodeGenerator,
Expand All @@ -30,44 +17,8 @@ internal fun generateAssistedFactory(
packageName: String,
factoryBaseName: String,
assistedFactoryType: AssistedFactoryType,
dependencies: List<AssistedFactoryDependency>,
): FactoryBinding {
class Dependency(
val name: String,
val key: Key,
val isAssisted: Boolean,
val parameter: KSValueParameter,
)

val dependencies = sourceDeclaration.parameters.map {
val isAssisted = it.isAssisted
Dependency(
name = if (isAssisted) {
it.name!!.asString()
} else {
"${it.name!!.asString()}Provider"
},
key = it.type.toKey(it.annotations),
isAssisted = isAssisted,
parameter = it
)
}

val assistedSourceParameters = dependencies
.asSequence()
.filter(Dependency::isAssisted)
.map { it.name to it.key.type }
.toList()

val assistedFactoryParameters = assistedFactoryType.factoryMethod.parameters
.map { it.name?.asString() to it.type.resolve() }

if (assistedSourceParameters != assistedFactoryParameters) {
throw NonRecoverableProcessorException(
"Factory method in @AssistedFactory must have assisted parameters by same names, types and in the same order as @AssistedInject constructor",
assistedFactoryType.factoryMethod
)
}

val providers: List<Pair<String, TypeName>> = dependencies
.asSequence()
.filterNot { it.isAssisted }
Expand All @@ -85,7 +36,6 @@ internal fun generateAssistedFactory(
packageName = packageName,
fileName = implName,
) {

val properties = providers.map { (name, type) ->
PropertySpec.builder(
name,
Expand Down Expand Up @@ -150,16 +100,11 @@ internal fun generateAssistedFactory(
injectableKey = Key(assistedFactoryType.type),
scoped = true,
sourceDeclaration = null,
parameters = dependencies.asSequence().filterNot(Dependency::isAssisted).map(Dependency::parameter).toList(),
parameters = dependencies.asSequence().filterNot(AssistedFactoryDependency::isAssisted).map(AssistedFactoryDependency::parameter).toList(),
containingFile = containingFile,
addCreateInstanceCall = { add("%T", ClassName(packageName, implName)) },
providersAreTransitive = true,
packageName = packageName,
factoryBaseName = implName
)
}

private val KSValueParameter.isAssisted: Boolean
get() = annotations.any {
it.annotationType.resolve().declaration.qualifiedName?.asString() == Assisted::class.requireQualifiedName()
}
Original file line number Diff line number Diff line change
Expand Up @@ -255,10 +255,10 @@ internal class ProcessorErrorsTest {
}

@Test
fun `no factory for assisted inject type`() {
fun `no factory for assisted type`() {
expectError("@AssistedFactory annotated type doesn't exist for type TypeWithoutFactory", kotlin("source.kt", """
import com.daugeldauge.kinzhal.annotations.AssistedInject
import com.daugeldauge.kinzhal.annotations.Assisted
import com.daugeldauge.kinzhal.annotations.AssistedInject
class TypeWithoutFactory @AssistedInject constructor(
@Assisted param1: Int,
Expand All @@ -267,6 +267,26 @@ internal class ProcessorErrorsTest {
""".trimIndent()))
}

@Test
fun `failed to match assisted factory with constructor`() {
expectError("Factory method in @AssistedFactory must have assisted parameters by same names, types and in the same order as @AssistedInject constructor", kotlin("source.kt", """
import com.daugeldauge.kinzhal.annotations.Assisted
import com.daugeldauge.kinzhal.annotations.AssistedFactory
import com.daugeldauge.kinzhal.annotations.AssistedInject
class AssistedType @AssistedInject constructor(
@Assisted param1: Int,
@Assisted param2: Int,
param3: String
)
@AssistedFactory
interface AssistedFactoryImpl {
fun build(param2: Int, param1: Int): AssistedType
}
""".trimIndent()))
}


private fun expectError(message: String, vararg sourceFiles: SourceFile) {
val result = compile(*sourceFiles)
Expand Down

0 comments on commit 64f84c2

Please sign in to comment.