Skip to content

Commit

Permalink
Merge pull request #7 from wowselim/migrate-to-ksp
Browse files Browse the repository at this point in the history
prepare 4.0 with ksp. closes #6
  • Loading branch information
wowselim authored Aug 26, 2022
2 parents 50c30a4 + 6606111 commit 70952c5
Show file tree
Hide file tree
Showing 9 changed files with 169 additions and 179 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ allprojects {
apply plugin: 'maven-publish'

group 'co.selim.goldfinch'
version '3.0.1'
version '4.0.0'

java {
withSourcesJar()
Expand Down
2 changes: 1 addition & 1 deletion codegen/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ repositories {

dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib"
implementation "com.google.devtools.ksp:symbol-processing-api:1.7.10-1.0.6"
implementation "com.squareup:kotlinpoet:1.12.0"
implementation "com.squareup:kotlinpoet-metadata:1.12.0"
implementation project(':annotation')
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
package co.selim.goldfinch.codegen

import co.selim.goldfinch.annotation.GenerateProperties
import co.selim.goldfinch.annotation.Level
import co.selim.goldfinch.annotation.Visibility
import com.google.devtools.ksp.KspExperimental
import com.google.devtools.ksp.getAnnotationsByType
import com.google.devtools.ksp.getVisibility
import com.google.devtools.ksp.processing.*
import com.google.devtools.ksp.symbol.KSAnnotated
import com.google.devtools.ksp.symbol.KSClassDeclaration
import com.google.devtools.ksp.symbol.KSNode
import com.google.devtools.ksp.symbol.KSType
import com.squareup.kotlinpoet.ClassName
import com.squareup.kotlinpoet.FileSpec
import com.squareup.kotlinpoet.KModifier
import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
import com.squareup.kotlinpoet.TypeName

class GoldfinchSymbolProcessor(private val logger: KSPLogger, private val codeGenerator: CodeGenerator) : SymbolProcessor {

private fun ClassName.safelyParameterizedBy(typeNames: List<TypeName>?): TypeName {
return if (typeNames.isNullOrEmpty()) {
this
} else {
this.parameterizedBy(typeNames)
}
}

private fun logError(msg: String, node: KSNode? = null): Nothing {
logger.error(msg, node)
error(msg)
}

private fun generatePropertyExtension(
receiver: ClassName,
properties: Map<String, TypeName>,
visibilityModifier: KModifier,
nestProperties: Boolean,
): FileSpec {
val sealedInterfaceName = ClassName(receiver.packageName, "${receiver.simpleName}Property")
val sealedInterface = generateSealedInterface(sealedInterfaceName, visibilityModifier)
val propertySpecs = properties.map { (propertyName, propertyType) ->
generatePropertyContainer(sealedInterfaceName, propertyName, propertyType, visibilityModifier, nestProperties)
}
return generateFile(receiver)
.apply {
if (nestProperties)
addType(sealedInterface.addTypes(propertySpecs).build())
else {
addType(sealedInterface.build())
propertySpecs.forEach(::addType)
}
}
.addProperty(
generatePropertyMapper(
sealedInterfaceName,
receiver,
properties,
visibilityModifier,
nestProperties
)
)
.build()
}

private fun KSType.getFullType(): TypeName {
val typeParams = arguments.mapNotNull { argument ->
val resolvedType = argument.type?.resolve()

resolvedType?.getFullType()?.copy(nullable = resolvedType.isMarkedNullable)
}

val fullName = declaration.qualifiedName!!.asString()
val simpleName = fullName.substringAfter(declaration.packageName.asString())
return ClassName(declaration.packageName.asString(), simpleName)
.safelyParameterizedBy(typeParams)
.copy(nullable = isMarkedNullable)
}

@OptIn(KspExperimental::class)
override fun process(resolver: Resolver): List<KSAnnotated> {
resolver.getSymbolsWithAnnotation(GenerateProperties::class.java.name)
.filterIsInstance(KSClassDeclaration::class.java)
.forEach { ksClassDeclaration ->
val annotatedClass = ksClassDeclaration.qualifiedName!!.asString()
val properties = ksClassDeclaration.getAllProperties()
.associate { ksPropertyDeclaration ->
val propertyName = ksPropertyDeclaration.simpleName.getShortName()
propertyName to ksPropertyDeclaration.type.resolve().getFullType()
}

val annotation = ksClassDeclaration.getAnnotationsByType(GenerateProperties::class).first()
val visibilityModifier = annotation.visibility.toKModifier(ksClassDeclaration)
val dependencies = Dependencies(true, ksClassDeclaration.containingFile!!)

codeGenerator.createNewFile(
dependencies,
ksClassDeclaration.packageName.asString(),
"${annotatedClass}Properties"
)
.bufferedWriter()
.use { writer ->
generatePropertyExtension(
ClassName.bestGuess(annotatedClass),
properties,
visibilityModifier,
annotation.level == Level.NESTED
).writeTo(writer)
}
}

return emptyList()
}


private fun Visibility.toKModifier(classDeclaration: KSClassDeclaration): KModifier {
return when (this) {
Visibility.PUBLIC -> KModifier.PUBLIC
Visibility.INTERNAL -> KModifier.INTERNAL
Visibility.INHERIT -> classDeclaration.getVisibilityKModifier()
}
}

private fun KSClassDeclaration.getVisibilityKModifier(): KModifier {
return when (val visibility = this.getVisibility()) {
com.google.devtools.ksp.symbol.Visibility.PUBLIC -> KModifier.PUBLIC
com.google.devtools.ksp.symbol.Visibility.PROTECTED -> KModifier.PROTECTED
com.google.devtools.ksp.symbol.Visibility.INTERNAL -> KModifier.INTERNAL
com.google.devtools.ksp.symbol.Visibility.PRIVATE,
com.google.devtools.ksp.symbol.Visibility.LOCAL,
com.google.devtools.ksp.symbol.Visibility.JAVA_PACKAGE -> {
val msg = "Visibility '$visibility' on class '${this.qualifiedName!!.asString()}' is not supported"
logError(msg, this)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package co.selim.goldfinch.codegen

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

class GoldfinchSymbolProcessorProvider : SymbolProcessorProvider {
override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor {
return GoldfinchSymbolProcessor(environment.logger, environment.codeGenerator)
}
}
171 changes: 0 additions & 171 deletions codegen/src/main/kotlin/co/selim/goldfinch/codegen/ServiceProcessor.kt

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
co.selim.goldfinch.codegen.GoldfinchSymbolProcessorProvider

This file was deleted.

17 changes: 15 additions & 2 deletions example/build.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
plugins {
id 'org.jetbrains.kotlin.jvm'
id 'org.jetbrains.kotlin.kapt'
id 'com.google.devtools.ksp' version '1.7.10-1.0.6'
}

repositories {
Expand All @@ -10,5 +10,18 @@ repositories {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib"
implementation project(':annotation')
kapt project(':codegen')
ksp project(':codegen')
}

sourceSets {
main {
java {
srcDir(file("build/generated/ksp/main/kotlin"))
}
}
test {
java {
srcDir(file("build/generated/ksp/test/kotlin"))
}
}
}
5 changes: 2 additions & 3 deletions example/src/main/kotlin/co/selim/goldfinch/example/Sample.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,10 @@ internal data class Person(
)

@GenerateProperties
internal data class Animal(
val name: String
)
internal data class Animal(val name: String)

fun main() {

val selim = Person("Selim Dinçer", LocalDate.of(1970, 1, 1))

selim.properties
Expand Down

0 comments on commit 70952c5

Please sign in to comment.