Skip to content

Commit

Permalink
start add variations generator
Browse files Browse the repository at this point in the history
  • Loading branch information
InsanusMokrassar committed Feb 11, 2025
1 parent db34b25 commit f807f2b
Show file tree
Hide file tree
Showing 11 changed files with 263 additions and 4 deletions.
12 changes: 8 additions & 4 deletions ksp/generator/src/main/kotlin/FilesWorkaround.kt
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package dev.inmo.micro_ksp.generator

import com.google.devtools.ksp.symbol.KSClassDeclaration
import com.google.devtools.ksp.symbol.KSDeclaration
import com.google.devtools.ksp.symbol.KSFile
import com.google.devtools.ksp.symbol.KSFunctionDeclaration
import com.squareup.kotlinpoet.FileSpec
import java.io.File

fun KSClassDeclaration.writeFile(
fun KSDeclaration.writeFile(
prefix: String = "",
suffix: String = "",
relatedPath: String = "",
Expand All @@ -21,8 +23,9 @@ fun KSClassDeclaration.writeFile(
"$prefix${simpleName.asString()}$suffix.kt"
).takeIf { force || !it.exists() } ?.apply {
parentFile.mkdirs()
val fileSpec = fileSpecBuilder()
writer().use { writer ->
fileSpecBuilder().writeTo(writer)
fileSpec.writeTo(writer)
}
}
}
Expand All @@ -42,8 +45,9 @@ fun KSFile.writeFile(
"$prefix${fileName.dropLastWhile { it != '.' }.removeSuffix(".")}$suffix.kt"
).takeIf { force || !it.exists() } ?.apply {
parentFile.mkdirs()
val fileSpec = fileSpecBuilder()
writer().use { writer ->
fileSpecBuilder().writeTo(writer)
fileSpec.writeTo(writer)
}
}
}
}
7 changes: 7 additions & 0 deletions ksp/variations/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
plugins {
id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.kotlin.plugin.serialization"
id "com.android.library"
}

apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project"
21 changes: 21 additions & 0 deletions ksp/variations/generator/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
plugins {
id "org.jetbrains.kotlin.jvm"
}

apply from: "$publish_jvm"

repositories {
mavenCentral()
}

dependencies {
api project(":micro_utils.ksp.generator")
api project(":micro_utils.ksp.variations")
api libs.kotlin.poet
api libs.ksp
}

java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
148 changes: 148 additions & 0 deletions ksp/variations/generator/src/main/kotlin/Processor.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
package dev.inmo.micro_utils.ksp.variations.generator

import com.google.devtools.ksp.KspExperimental
import com.google.devtools.ksp.getAnnotationsByType
import com.google.devtools.ksp.processing.CodeGenerator
import com.google.devtools.ksp.processing.Resolver
import com.google.devtools.ksp.processing.SymbolProcessor
import com.google.devtools.ksp.symbol.*
import com.squareup.kotlinpoet.*
import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
import com.squareup.kotlinpoet.ksp.toClassName
import com.squareup.kotlinpoet.ksp.toKModifier
import com.squareup.kotlinpoet.ksp.toTypeName
import dev.inmo.micro_ksp.generator.companion
import dev.inmo.micro_ksp.generator.findSubClasses
import dev.inmo.micro_ksp.generator.writeFile
import dev.inmo.micro_utils.ksp.variations.GenerateVariations
import dev.inmo.micro_utils.ksp.variations.GenerationVariant

class Processor(
private val codeGenerator: CodeGenerator
) : SymbolProcessor {
private fun KSClassDeclaration.findSealedConnection(potentialSealedParent: KSClassDeclaration): Boolean {
val targetClassname = potentialSealedParent.qualifiedName ?.asString()
return superTypes.any {
val itAsDeclaration = it.resolve().declaration as? KSClassDeclaration ?: return@any false
targetClassname == (itAsDeclaration.qualifiedName ?.asString()) || (itAsDeclaration.getSealedSubclasses().any() && itAsDeclaration.findSealedConnection(potentialSealedParent))
}
}

private fun KSClassDeclaration.resolveSubclasses(
searchIn: Sequence<KSAnnotated>,
allowNonSealed: Boolean
): Sequence<KSClassDeclaration> {
return findSubClasses(searchIn).let {
if (allowNonSealed) {
it
} else {
it.filter {
it.findSealedConnection(this)
}
}
}
}

@OptIn(KspExperimental::class)
private fun FileSpec.Builder.generateVariations(
ksFunctionDeclaration: KSFunctionDeclaration,
resolver: Resolver
) {
val annotation = ksFunctionDeclaration.getAnnotationsByType(GenerateVariations::class).first()
val variations: List<Pair<List<GenerationVariant>, KSValueParameter>> = ksFunctionDeclaration.parameters.mapNotNull {
val variationAnnotations = it.getAnnotationsByType(GenerationVariant::class).toList().ifEmpty { return@mapNotNull null }
variationAnnotations to it
}
val accumulatedGenerations = mutableSetOf<FunSpec>()
variations.forEach { (variations, parameter) ->
if (accumulatedGenerations.isEmpty()) {
variations.forEach { variation ->
accumulatedGenerations.add(
FunSpec.builder(ksFunctionDeclaration.simpleName.asString()).apply {
modifiers.addAll(ksFunctionDeclaration.modifiers.mapNotNull { it.toKModifier() })
ksFunctionDeclaration.parameters.forEach {
parameters.add(
(if (it == parameter) {
ParameterSpec
.builder(
variation.argName,
if (variation.varargTypes.isEmpty()) {
variation.type.asTypeName()
} else {
variation.type.parameterizedBy(*variation.varargTypes)
}
)
.apply {
val name = it.name ?.asString() ?: "this"
if (it.isVararg) {
defaultValue(
"""
*$name.map { it.${variation.conversion} }.toTypedArray()
""".trimIndent()
)
} else {
defaultValue("$name.${variation.conversion}")
}
}
} else {
ParameterSpec
.builder(
it.name ?.asString() ?: return@forEach,
it.type.toTypeName(),
)
})
.apply {
if (it.isCrossInline) {
addModifiers(KModifier.CROSSINLINE)
}
if (it.isVal) {
addModifiers(KModifier.VALUE)
}
if (it.isNoInline) {
addModifiers(KModifier.NOINLINE)
}
if (it.isVararg) {
addModifiers(KModifier.VARARG)
}
}
.build()
)
}
}.build()
)
}
} else {

}
}
accumulatedGenerations.forEach {
addFunction(it)
}
}

@OptIn(KspExperimental::class)
override fun process(resolver: Resolver): List<KSAnnotated> {
(resolver.getSymbolsWithAnnotation(GenerateVariations::class.qualifiedName!!)).filterIsInstance<KSFunctionDeclaration>().forEach {
val prefix = (it.getAnnotationsByType(GenerateVariations::class)).firstOrNull() ?.prefix ?.takeIf {
it.isNotEmpty()
} ?: it.simpleName.asString().replaceFirst(it.simpleName.asString(), "")
it.writeFile(prefix = prefix, suffix = "GeneratedVariation") {
FileSpec.builder(
it.packageName.asString(),
"${it.simpleName.getShortName()}GeneratedVariation"
).apply {
addFileComment(
"""
THIS CODE HAVE BEEN GENERATED AUTOMATICALLY
TO REGENERATE IT JUST DELETE FILE
ORIGINAL FILE: ${it.containingFile ?.fileName}
""".trimIndent()
)
generateVariations(it, resolver)
}.build()
}
}

return emptyList()
}
}
11 changes: 11 additions & 0 deletions ksp/variations/generator/src/main/kotlin/Provider.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package dev.inmo.micro_utils.ksp.variations.generator

import com.google.devtools.ksp.processing.SymbolProcessor
import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
import com.google.devtools.ksp.processing.SymbolProcessorProvider

class Provider : SymbolProcessorProvider {
override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor = Processor(
environment.codeGenerator
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
dev.inmo.micro_utils.ksp.variations.generator.Provider
27 changes: 27 additions & 0 deletions ksp/variations/generator/test/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
plugins {
id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.kotlin.plugin.serialization"
id "com.android.library"
id "com.google.devtools.ksp"
}

apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project"


kotlin {
sourceSets {
commonMain {
dependencies {
api project(":micro_utils.ksp.variations")
}
}
}
}


dependencies {
add("kspCommonMainMetadata", project(":micro_utils.ksp.variations.generator"))
}

ksp {
}
16 changes: 16 additions & 0 deletions ksp/variations/generator/test/src/commonMain/kotlin/SampleFun.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import dev.inmo.micro_utils.ksp.variations.GenerateVariations
import dev.inmo.micro_utils.ksp.variations.GenerationVariant

data class Sample(
val value: String
)

@GenerateVariations
fun sample(
@GenerationVariant(
"example",
Sample::class,
"value"
)
example: String = "12"
) = println(example)
7 changes: 7 additions & 0 deletions ksp/variations/src/commonMain/kotlin/GenerateVariations.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package dev.inmo.micro_utils.ksp.variations

@Retention(AnnotationRetention.BINARY)
@Target(AnnotationTarget.FUNCTION)
annotation class GenerateVariations(
val prefix: String = ""
)
13 changes: 13 additions & 0 deletions ksp/variations/src/commonMain/kotlin/GenerationVariant.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package dev.inmo.micro_utils.ksp.variations

import kotlin.reflect.KClass

@Retention(AnnotationRetention.BINARY)
@Repeatable
@Target(AnnotationTarget.TYPE_PARAMETER, AnnotationTarget.VALUE_PARAMETER)
annotation class GenerationVariant(
val argName: String,
val type: KClass<*>,
val conversion: String,
vararg val varargTypes: KClass<*>
)
4 changes: 4 additions & 0 deletions settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ String[] includes = [
":ksp:classcasts:generator",
":ksp:classcasts:generator:test",

":ksp:variations",
":ksp:variations:generator",
":ksp:variations:generator:test",

":dokka"
]

Expand Down

0 comments on commit f807f2b

Please sign in to comment.