Skip to content

Commit

Permalink
add argument type
Browse files Browse the repository at this point in the history
  • Loading branch information
RudolfHladik committed Oct 4, 2024
1 parent 359511f commit ff39892
Show file tree
Hide file tree
Showing 8 changed files with 102 additions and 90 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ ksp {
arg("KOIN_DEFAULT_MODULE","false")

// setup for component processor
arg("viewModel", "app.futured.kmptemplate.util.arch.ViewModelComponent")
arg("viewModelExt", "app.futured.kmptemplate.util.ext.viewModel")
arg("appComponentContext", "app.futured.kmptemplate.util.arch.AppComponentContext")
arg("viewModelExt", "app.futured.kmptemplate.util.arch.viewModel")
}

// https:/gradle/gradle/issues/15383
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package app.futured.kmptemplate.feature.data.model.ui.args

data class SecondScreenArgs(
val id: Int = 0,
val name: String = ""
)
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package app.futured.kmptemplate.feature.navigation.signedin.tab.b

import app.futured.kmptemplate.feature.data.model.ui.args.SecondScreenArgs
import app.futured.kmptemplate.feature.ui.first.FirstComponent
import app.futured.kmptemplate.feature.ui.first.FirstScreen
import app.futured.kmptemplate.feature.ui.second.SecondComponent
Expand All @@ -24,7 +25,7 @@ sealed class TabBDestination : Destination<TabBNavEntry> {
@Serializable
data object Second : TabBDestination() {
override fun createComponent(componentContext: AppComponentContext): TabBNavEntry {
return TabBNavEntry.Second(SecondComponent(componentContext))
return TabBNavEntry.Second(SecondComponent(SecondScreenArgs(), componentContext))
}
}

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package app.futured.kmptemplate.feature.ui.second

import app.futured.kmptemplate.feature.data.model.ui.args.SecondScreenArgs
import com.rudolfhladik.annotation.Component
import kotlinx.coroutines.flow.StateFlow

@Component(argType = SecondScreenArgs::class)
interface SecondScreen {
val viewState: StateFlow<SecondViewState>
val actions: Actions
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
package com.rudolfhladik.componentprocessor

import com.google.devtools.ksp.processing.CodeGenerator
import com.google.devtools.ksp.processing.Dependencies
import com.google.devtools.ksp.processing.KSPLogger
import com.google.devtools.ksp.processing.Resolver
import com.google.devtools.ksp.processing.SymbolProcessor
import com.google.devtools.ksp.symbol.KSAnnotated
import com.google.devtools.ksp.symbol.KSClassDeclaration
import com.rudolfhladik.annotation.Component
import com.rudolfhladik.componentprocessor.content.ComponentContentGenerator
import com.rudolfhladik.componentprocessor.content.ComponentPoetGenerator
import java.io.OutputStream
import kotlin.reflect.KClass

class ComponentProcessor(
Expand All @@ -30,32 +27,16 @@ class ComponentProcessor(
}

private fun generateComponent(component: KSClassDeclaration) {
val componentContentGenerator = ComponentContentGenerator()
val fileName = componentContentGenerator.getFileName(component)

val file = codeGenerator.createNewFile(
dependencies = Dependencies(false),
packageName = component.packageName.asString(),
fileName = fileName,
)

val contents = componentContentGenerator.generateContents(component, fileName, args)

val viewModelComponentPackage = args.get("viewModel") ?: error("specify ViewModelComponent path")
val appComponentContextPackage = args.get("appComponentContext") ?: error("specify ViewModelComponent path")
val viewModelExtPackage = args.get("viewModelExt") ?: error("specify viewModel extension path")

val poet = ComponentPoetGenerator(logger)
poet.tryPoet(viewModelExtPackage, component, codeGenerator)
// don't use old hard coded generation
// val componentContentGenerator = ComponentContentGenerator()
// componentContentGenerator.generateContents(component, args, codeGenerator)

file.use {
it.writeAll(contents)
}
}

private fun OutputStream.writeAll(contents: List<String>) {
contents.forEach { content ->
this.write(content.toByteArray())
}
// use poet generation
val poet = ComponentPoetGenerator(logger)
poet.tryPoet(appComponentContextPackage, viewModelExtPackage, component, codeGenerator)
}

private fun Resolver.findAnnotationsForClass(kClass: KClass<*>): Sequence<KSClassDeclaration> =
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package com.rudolfhladik.componentprocessor.content

import com.google.devtools.ksp.processing.CodeGenerator
import com.google.devtools.ksp.processing.Dependencies
import com.google.devtools.ksp.symbol.KSAnnotation
import com.google.devtools.ksp.symbol.KSClassDeclaration
import com.rudolfhladik.annotation.Component
import java.io.OutputStream

internal class ComponentContentGenerator {
internal companion object {
Expand All @@ -12,7 +15,18 @@ internal class ComponentContentGenerator {
private const val eventsName = "events"
}

fun generateContents(component: KSClassDeclaration, fileName: String, args: Map<String, String>): List<String> {
fun generateContents(
component: KSClassDeclaration,
args: Map<String, String>,
codeGenerator: CodeGenerator,
) {
val fileName = getFileName(component)

val file = codeGenerator.createNewFile(
dependencies = Dependencies(false),
packageName = component.packageName.asString(),
fileName = fileName,
)
val annotation: KSAnnotation = component.annotations.first {
it.shortName.asString() == Component::class.simpleName
}
Expand All @@ -21,7 +35,6 @@ internal class ComponentContentGenerator {
.firstOrNull { arg -> arg.name?.asString() == "argType" }
?.value?.toString()


val contents = mutableListOf<String>()

val packageLine = "package ${component.packageName.asString()}\n\n"
Expand All @@ -35,10 +48,12 @@ internal class ComponentContentGenerator {
contents.add(it)
}

return contents
file.use {
it.writeAll(contents)
}
}

fun getFileName(component: KSClassDeclaration): String =
private fun getFileName(component: KSClassDeclaration): String =
getComponentName(component).plus("Component")

private fun getClassAndBody(component: KSClassDeclaration, fileName: String, nameArgument: String?): List<String> {
Expand Down Expand Up @@ -95,9 +110,8 @@ internal class ComponentContentGenerator {
val declarations = component.declarations
.map { it.simpleName.asString() }

val viewModelComponentPackage = args.get("viewModel") ?: error("specify ViewModelComponent path")
// val viewModelComponentPackage = args.get("viewModel") ?: error("specify ViewModelComponent path")
val viewModelExtPackage = args.get("viewModelExt") ?: error("specify viewModel extension path")
val viewModelComponentImport = "import $viewModelComponentPackage\n"
val viewModelExtImport = "import $viewModelExtPackage\n"

val componentContextImport = "import com.arkivanov.decompose.ComponentContext\n"
Expand All @@ -113,7 +127,6 @@ internal class ComponentContentGenerator {
componentContextImport,
flowImport,
stateFlowImport,
viewModelComponentImport,
viewModelExtImport,
parametersOfImport,
"\n",
Expand All @@ -128,4 +141,10 @@ internal class ComponentContentGenerator {
.qualifiedName
?.asString()
?.substringAfterLast(".") ?: "Error"

private fun OutputStream.writeAll(contents: List<String>) {
contents.forEach { content ->
this.write(content.toByteArray())
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ package com.rudolfhladik.componentprocessor.content

import com.google.devtools.ksp.processing.CodeGenerator
import com.google.devtools.ksp.processing.KSPLogger
import com.google.devtools.ksp.symbol.KSAnnotation
import com.google.devtools.ksp.symbol.KSClassDeclaration
import com.google.devtools.ksp.symbol.KSType
import com.rudolfhladik.annotation.Component
import com.squareup.kotlinpoet.ClassName
import com.squareup.kotlinpoet.CodeBlock
import com.squareup.kotlinpoet.FileSpec
Expand All @@ -13,56 +16,64 @@ import com.squareup.kotlinpoet.TypeSpec
import com.squareup.kotlinpoet.ksp.toTypeName
import com.squareup.kotlinpoet.ksp.writeTo

class ComponentPoetGenerator(
internal class ComponentPoetGenerator(
private val logger: KSPLogger,
) { // TODO cant access classes from other modules
) {
fun tryPoet(
appComponentContextPackage: String,
viewModelExtPackage: String,
component: KSClassDeclaration,
codeGenerator: CodeGenerator,
) {
val annotation: KSAnnotation = component.annotations.first {
it.shortName.asString() == Component::class.simpleName
}
// Getting the 'argType' argument object from the @Component.
val arg: KSType? = annotation.arguments.first().value as? KSType

///
val baseName = component.qualifiedName?.asString()?.removeSuffix("Screen") ?: error("Unable to get base name")
val classname = ClassName(
packageName = component.packageName.asString(),
simpleNames = listOf(baseName.plus("Component")),
)

val appComponentContext = ClassName(
packageName = component.packageName.asString(), // todo get from args
packageName = component.packageName.asString(),
simpleNames = listOf("AppComponentContext"),
)

val properties = component.getAllProperties()

properties.forEach {
logger.warn("${it.simpleName.getShortName()}")
val type = it.type.toTypeName()
logger.warn("$type")
}

val viewStateType = properties
.find { it.type.toTypeName().toString().contains("ViewState") }
?: error("Cannot find ViewState type")

val actionsType = properties
.find { it.type.toTypeName().toString().contains("Actions") }
?: error("Cannot find ViewState type")

logger.warn("$viewStateType")


val VMClass = ClassName(
val vMClass = ClassName(
packageName = component.packageName.asString(),
simpleNames = listOf(baseName.plus("ViewModel")),
)
val constructorBuilder = FunSpec.constructorBuilder()
if (arg != null) {
constructorBuilder.addParameter("arg", (arg.declaration as KSClassDeclaration).asStarProjectedType().toTypeName())
}

val helloWorld = TypeSpec.classBuilder("HelloWorld")
constructorBuilder.addParameter("componentContext", appComponentContext)

val vmStatement = if (arg != null) {
"viewModel { parametersOf(arg) }"
} else {
"viewModel()"
}

val componentBuilder = TypeSpec.classBuilder(baseName.substringAfterLast('.').plus("Component"))
.addModifiers(KModifier.INTERNAL)
.primaryConstructor(
FunSpec.constructorBuilder()
.addParameter("componentContext", appComponentContext)
.build(),
)
.primaryConstructor(constructorBuilder.build())
.addSuperinterface(
delegate = CodeBlock.builder().addStatement("componentContext").build(),
superinterface = appComponentContext,
Expand All @@ -71,38 +82,43 @@ class ComponentPoetGenerator(
component.asType(emptyList()).toTypeName(),
)
.addProperty(
PropertySpec.builder("viewModel", VMClass)
PropertySpec.builder("viewModel", vMClass)
.delegate(
CodeBlock.builder()
.addStatement("viewModel()").build(),
.addStatement(vmStatement).build(),
)
.addModifiers(KModifier.PRIVATE)
.build(),
)
helloWorld
.addProperty(
PropertySpec.builder(viewStateType.simpleName.getShortName(), viewStateType.type.toTypeName())
.initializer(CodeBlock.builder().addStatement("viewModel.viewState").build())
.addModifiers(KModifier.OVERRIDE)
.build(),
)

helloWorld
.addProperty(
PropertySpec.builder(actionsType.simpleName.getShortName(), actionsType.type.toTypeName())
.initializer(CodeBlock.builder().addStatement("viewModel").build())
.addModifiers(KModifier.OVERRIDE)
.build(),
)
if (viewStateType != null) {
componentBuilder
.addProperty(
PropertySpec.builder(viewStateType.simpleName.getShortName(), viewStateType.type.toTypeName())
.initializer(CodeBlock.builder().addStatement("viewModel.viewState").build())
.addModifiers(KModifier.OVERRIDE)
.build(),
)
}

.build()
if (actionsType != null) {
componentBuilder
.addProperty(
PropertySpec.builder(actionsType.simpleName.getShortName(), actionsType.type.toTypeName())
.initializer(CodeBlock.builder().addStatement("viewModel").build())
.addModifiers(KModifier.OVERRIDE)
.build(),
)
}

val spec = FileSpec.builder(classname)
.addImport(viewModelExtPackage.removeSuffix("viewModel"), "viewModel")
.addType(helloWorld.build())
.build()

if (arg != null) {
spec.addImport("org.koin.core.parameter", "parametersOf")
}
spec.addImport(viewModelExtPackage.removeSuffix("viewModel"), "viewModel")
.addImport(appComponentContextPackage.removeSuffix("AppComponentContext"), "AppComponentContext")
.addType(componentBuilder.build())

spec.writeTo(codeGenerator, aggregating = true)
spec.build().writeTo(codeGenerator, aggregating = true)
}
}

0 comments on commit ff39892

Please sign in to comment.