Skip to content

Commit

Permalink
1.0.2 (#7)
Browse files Browse the repository at this point in the history
* refactor: `DSLImpl` renamed to `DSLBuilderImpl`

* refactor: `DSLImpl` renamed to `DSLBuilderImpl`

* chore: version updated to `1.0.2`

* refactor: codegen refactoring (MutableImpl, ImmutableImpl, FactoryFunctionImpl) & little changes

* refactor: removed file with old codegen

* refactor: codegen rewriting

Co-authored-by: y9neon <[email protected]>
  • Loading branch information
y9vad9 and y9neon authored Dec 8, 2021
1 parent 83580cd commit a288ebb
Show file tree
Hide file tree
Showing 20 changed files with 381 additions and 247 deletions.
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@

# implier

Kotlin Symbol Processor library for creating [**Mutable**](https://github.com/y9vad9/implier/blob/fb5cba3c62defe23ce5773287fc9f37367d800fd/src/main/kotlin/com/y9vad9/implier/annotations.kt#L10), [**Immutable**](https://github.com/y9vad9/implier/blob/fb5cba3c62defe23ce5773287fc9f37367d800fd/src/main/kotlin/com/y9vad9/implier/annotations.kt#L18), [**Builders**](https://github.com/y9vad9/implier/blob/fb5cba3c62defe23ce5773287fc9f37367d800fd/src/main/kotlin/com/y9vad9/implier/annotations.kt#L35), [**DSL Builders**](https://github.com/y9vad9/implier/blob/1.0.1/src/main/kotlin/com/y9vad9/implier/annotations.kt#L50) from interfaces & abstract classes with properties.
Kotlin Symbol Processor library for creating [**
Mutable**](https://github.com/y9vad9/implier/blob/fb5cba3c62defe23ce5773287fc9f37367d800fd/src/main/kotlin/com/y9vad9/implier/annotations.kt#L10)
, [**
Immutable**](https://github.com/y9vad9/implier/blob/fb5cba3c62defe23ce5773287fc9f37367d800fd/src/main/kotlin/com/y9vad9/implier/annotations.kt#L18)
, [**
Builders**](https://github.com/y9vad9/implier/blob/fb5cba3c62defe23ce5773287fc9f37367d800fd/src/main/kotlin/com/y9vad9/implier/annotations.kt#L35)
, [**DSL Builders**](https://github.com/y9vad9/implier/blob/1.0.1/src/main/kotlin/com/y9vad9/implier/annotations.kt#L50)
from interfaces & abstract classes with properties.

## Examples

Expand Down
2 changes: 1 addition & 1 deletion build-logic/dependencies/src/main/kotlin/AppInfo.kt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
object AppInfo {
const val PACKAGE = "com.y9vad9.implier"
const val VERSION = "1.0.0"
const val VERSION = "1.0.2"
}
5 changes: 4 additions & 1 deletion build-logic/dependencies/src/main/kotlin/Deps.kt
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,10 @@ object Deps {
const val JUnit = "org.jetbrains.kotlin:kotlin-test-junit5:$kotlinVersion"
}

const val KotlinPoet = "com.squareup:kotlinpoet:1.10.2"
object KotlinPoet {
const val KotlinPoet = "com.squareup:kotlinpoet:1.10.2"
const val KSP = "com.squareup:kotlinpoet-ksp:1.10.2"
}

object KotlinGang {
object KDS {
Expand Down
3 changes: 2 additions & 1 deletion ksp/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ kotlin {
val jvmMain by getting {
dependencies {
implementation(Deps.Libs.KSP.Api)
implementation(Deps.Libs.KotlinPoet)
implementation(Deps.Libs.KotlinPoet.KotlinPoet)
implementation(Deps.Libs.KotlinPoet.KSP)
implementation(Deps.Libs.Kotlin.Reflection)
implementation(project(":"))
}
Expand Down
4 changes: 2 additions & 2 deletions ksp/src/main/kotlin/com/y9vad9/implier/AnnotationsVisitor.kt
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@ class AnnotationsVisitor(private val codeGenerator: CodeGenerator) : KSVisitorVo
classDeclaration
)
}
if (classDeclaration.isAnnotationPresent(DSLImpl::class)) {
if (classDeclaration.isAnnotationPresent(DSLBuilderImpl::class)) {
DSLImplAnnotatedClassProcessor.process(
classDeclaration.getAnnotationsByType(DSLImpl::class).first(),
classDeclaration.getAnnotationsByType(DSLBuilderImpl::class).first(),
codeGenerator,
classDeclaration
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ class ImplierAnnotationProcessor(private val codeGenerator: CodeGenerator) : Sym
resolver.getAllFiles().flatMap { it.declarations }.filter {
it.isAnnotationPresent(MutableImpl::class)
|| it.isAnnotationPresent(ImmutableImpl::class)
|| it.isAnnotationPresent(FactoryFunctionImpl::class
|| it.isAnnotationPresent(
FactoryFunctionImpl::class
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import com.squareup.kotlinpoet.*
import com.y9vad9.implier.BuilderImpl
import com.y9vad9.implier.ImmutableImpl
import com.y9vad9.implier.MutableImpl
import com.y9vad9.implier.codegen.BuilderFileCodeGeneration
import com.y9vad9.implier.codegen.BuilderFileCodeGeneration.generate
import java.io.OutputStreamWriter
import java.util.*

Expand All @@ -25,76 +27,14 @@ object BuilderImplAnnotatedClassProcessor : AnnotatedClassProcessor<BuilderImpl>
classDeclaration.simpleName.asString().plus("Builder")
).use { output ->
OutputStreamWriter(output).use { writer ->
generateBuilderImplementation(
BuilderFileCodeGeneration.Data(
type = annotation.type,
initVariantCode = (if (!(classDeclaration.isAnnotationPresent(ImmutableImpl::class))) "Immutable" else "Mutable").plus(
classDeclaration.simpleName.asString()
),
classDeclaration
).writeTo(writer)
).generate().writeTo(writer)
}
}
}
}

private fun generateBuilderImplementation(
type: BuilderImpl.Type,
initVariantCode: String,
declaration: KSClassDeclaration
): FileSpec {
val file = FileSpec.builder(
declaration.packageName.asString(),
declaration.simpleName.asString().plus("Builder")
)
val builderClassName = ClassName(declaration.packageName.asString(), declaration.simpleName.asString().plus("Builder"))
val builderClass = TypeSpec.classBuilder(file.name)
for (member in declaration.getAllProperties()) {
val resolvedMember = member.type.resolve()
val memberType = ClassName(
resolvedMember.declaration.packageName.asString(),
resolvedMember.declaration.simpleName.asString()
)
builderClass.addProperty(
PropertySpec.builder(member.simpleName.asString(), memberType)
.mutable(true)
.delegate("kotlin.properties.Delegates.notNull()")
.addModifiers(KModifier.PRIVATE)
.build()
)
builderClass.addFunction(
FunSpec.builder(
if (type == BuilderImpl.Type.WITHOUT_ACCESSORS) member.simpleName.asString() else "set${
member.simpleName.asString()
.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() }
}"
).addParameter(
"value",
ClassName(
resolvedMember.declaration.packageName.asString(),
resolvedMember.declaration.simpleName.asString()
)
)
.returns(builderClassName)
.addCode("${member.simpleName.asString()} = value\n")
.addCode("return this")
.build()
)
}
builderClass.addFunction(
FunSpec.builder("build")
.addCode(
"return $initVariantCode(${
declaration.getAllProperties().joinToString(",") { it.simpleName.asString() }
})"
)
.returns(
ClassName(
declaration.packageName.asString(),
declaration.simpleName.asString()
)
)
.build()
)
file.addType(builderClass.build())
return file.build()
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,21 @@ import com.google.devtools.ksp.processing.CodeGenerator
import com.google.devtools.ksp.processing.Dependencies
import com.google.devtools.ksp.symbol.KSClassDeclaration
import com.squareup.kotlinpoet.*
import com.y9vad9.implier.DSLImpl
import com.y9vad9.implier.DSLBuilderImpl
import com.y9vad9.implier.ImmutableImpl
import com.y9vad9.implier.MutableImpl
import com.y9vad9.implier.codegen.DSLFileCodeGeneration
import com.y9vad9.implier.codegen.DSLFileCodeGeneration.generate
import java.io.OutputStreamWriter
import java.util.*

object DSLImplAnnotatedClassProcessor : AnnotatedClassProcessor<DSLImpl> {
object DSLImplAnnotatedClassProcessor : AnnotatedClassProcessor<DSLBuilderImpl> {
@OptIn(KspExperimental::class)
override fun process(annotation: DSLImpl, codeGenerator: CodeGenerator, classDeclaration: KSClassDeclaration) {
override fun process(
annotation: DSLBuilderImpl,
codeGenerator: CodeGenerator,
classDeclaration: KSClassDeclaration
) {
if (!(classDeclaration.isAnnotationPresent(ImmutableImpl::class)
&& classDeclaration.isAnnotationPresent(MutableImpl::class))
)
Expand All @@ -25,97 +31,15 @@ object DSLImplAnnotatedClassProcessor : AnnotatedClassProcessor<DSLImpl> {
classDeclaration.simpleName.asString().plus("BuilderScope")
).use { output ->
OutputStreamWriter(output).use { writer ->
generateDSLImplementation(
DSLFileCodeGeneration.Data(
type = annotation.type,
initVariantCode = (if (!(classDeclaration.isAnnotationPresent(ImmutableImpl::class))) "Immutable" else "Mutable").plus(
classDeclaration.simpleName.asString()
),
functionName = annotation.functionName,
declaration = classDeclaration
).writeTo(writer)
).generate().writeTo(writer)
}
}
}
}

private fun generateDSLImplementation(
type: DSLImpl.Type,
functionName: String,
initVariantCode: String,
declaration: KSClassDeclaration
): FileSpec {
val name = declaration.simpleName.asString().plus("BuilderScope")
val file = FileSpec.builder(
declaration.packageName.asString(),
name
)
val builderClassName = ClassName(declaration.packageName.asString(), name)
val builderClass = TypeSpec.classBuilder(file.name)
for (member in declaration.getAllProperties()) {
val resolvedMember = member.type.resolve()
val memberType = ClassName(
resolvedMember.declaration.packageName.asString(),
resolvedMember.declaration.simpleName.asString()
)
when (type) {
DSLImpl.Type.PROPERTY_ACCESS -> builderClass.addProperty(
PropertySpec.builder(member.simpleName.asString(), memberType)
.mutable(true)
.delegate("kotlin.properties.Delegates.notNull()")
.build()
)
else -> {
builderClass.addProperty(
PropertySpec.builder(member.simpleName.asString(), memberType)
.mutable(true)
.delegate("kotlin.properties.Delegates.notNull()")
.addModifiers(KModifier.PUBLIC)
.build()
)
builderClass.addFunction(
FunSpec.builder(
if (type == DSLImpl.Type.WITHOUT_ACCESSORS) member.simpleName.asString() else "set${
member.simpleName.asString()
.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() }
}"
).addParameter(
"value",
ClassName(
resolvedMember.declaration.packageName.asString(),
resolvedMember.declaration.simpleName.asString()
)
)
.returns(builderClassName)
.addCode("${member.simpleName.asString()} = value\n")
.addCode("return this")
.build()
)
}
}
}
file.addFunction(
FunSpec.builder(functionName)
.addParameter(
"block",
LambdaTypeName.get(receiver = builderClassName, returnType = Unit::class.asTypeName())
)
.addCode(
"""
val dslBuilder = $name()
dslBuilder.apply(block)
return $initVariantCode(${
declaration.getAllProperties().joinToString(",") { "dslBuilder." + it.simpleName.asString() }
})
""".trimIndent()
)
.returns(
ClassName(
declaration.packageName.asString(),
declaration.simpleName.asString()
)
)
.build()
)
file.addType(builderClass.build())
return file.build()
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,37 @@ import com.google.devtools.ksp.isAnnotationPresent
import com.google.devtools.ksp.processing.CodeGenerator
import com.google.devtools.ksp.processing.Dependencies
import com.google.devtools.ksp.symbol.KSClassDeclaration
import com.y9vad9.implier.*
import com.y9vad9.implier.generateFactory
import com.y9vad9.implier.FactoryFunctionImpl
import com.y9vad9.implier.ImmutableImpl
import com.y9vad9.implier.MutableImpl
import com.y9vad9.implier.codegen.FunctionFactoryFileCodeGeneration
import com.y9vad9.implier.codegen.FunctionFactoryFileCodeGeneration.generate
import java.io.OutputStreamWriter

object FactoryFunctionAnnotatedClassProcessor : AnnotatedClassProcessor<FactoryFunctionImpl> {
@OptIn(KspExperimental::class)
override fun process(annotation: FactoryFunctionImpl, codeGenerator: CodeGenerator, classDeclaration: KSClassDeclaration) {
val variantName = if(classDeclaration.isAnnotationPresent(ImmutableImpl::class))
override fun process(
annotation: FactoryFunctionImpl,
codeGenerator: CodeGenerator,
classDeclaration: KSClassDeclaration
) {
val variantName = if (classDeclaration.isAnnotationPresent(ImmutableImpl::class))
"Immutable"
else if(classDeclaration.isAnnotationPresent(MutableImpl::class))
else if (classDeclaration.isAnnotationPresent(MutableImpl::class))
"Mutable"
else throw IllegalStateException(
"Unable to create factory function for interface that does not have Mutable or Immutable realization"
"Unable to create factory function for interface / class that does not have Mutable or Immutable realization"
)
if(classDeclaration.isAnnotationPresent(ImmutableImpl::class)) {
if (classDeclaration.isAnnotationPresent(ImmutableImpl::class)) {
codeGenerator.createNewFile(
Dependencies(false),
classDeclaration.packageName.asString(),
classDeclaration.simpleName.asString().plus("Factory")
).use { output ->
OutputStreamWriter(output).use { writer ->
generateFactory(variantName, classDeclaration).writeTo(writer)
FunctionFactoryFileCodeGeneration.Data(variantName, classDeclaration)
.generate()
.writeTo(writer)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,35 @@ package com.y9vad9.implier.annotations.processor

import com.google.devtools.ksp.processing.CodeGenerator
import com.google.devtools.ksp.processing.Dependencies
import com.google.devtools.ksp.symbol.ClassKind
import com.google.devtools.ksp.symbol.KSClassDeclaration
import com.google.devtools.ksp.symbol.Modifier
import com.y9vad9.implier.ImmutableImpl
import com.y9vad9.implier.generateVariant
import com.y9vad9.implier.codegen.ImplementationFileCodeGeneration
import com.y9vad9.implier.codegen.ImplementationFileCodeGeneration.generate
import java.io.OutputStreamWriter

object ImmutableAnnotatedClassProcessor : AnnotatedClassProcessor<ImmutableImpl> {
override fun process(annotation: ImmutableImpl, codeGenerator: CodeGenerator, classDeclaration: KSClassDeclaration) {
override fun process(
annotation: ImmutableImpl,
codeGenerator: CodeGenerator,
classDeclaration: KSClassDeclaration
) {
if (classDeclaration.classKind == ClassKind.CLASS && Modifier.ABSTRACT !in classDeclaration.modifiers)
throw IllegalStateException("Unable to create realization from non-abstract class")
else if (classDeclaration.classKind != ClassKind.INTERFACE)
throw IllegalStateException("Unable to create realization from ${classDeclaration.classKind}.")

codeGenerator.createNewFile(
Dependencies(false),
classDeclaration.packageName.asString(),
"Immutable${classDeclaration.simpleName.asString()}"
).use { output ->
OutputStreamWriter(output).use { writer ->
generateVariant("Immutable", false, classDeclaration).writeTo(writer)
ImplementationFileCodeGeneration.Data(
mutable = false,
declaration = classDeclaration
).generate().writeTo(writer)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,29 @@ package com.y9vad9.implier.annotations.processor

import com.google.devtools.ksp.processing.CodeGenerator
import com.google.devtools.ksp.processing.Dependencies
import com.google.devtools.ksp.symbol.ClassKind
import com.google.devtools.ksp.symbol.KSClassDeclaration
import com.google.devtools.ksp.symbol.Modifier
import com.y9vad9.implier.MutableImpl
import com.y9vad9.implier.generateVariant
import com.y9vad9.implier.codegen.ImplementationFileCodeGeneration
import com.y9vad9.implier.codegen.ImplementationFileCodeGeneration.generate
import java.io.OutputStreamWriter

object MutableAnnotatedClassProcessor : AnnotatedClassProcessor<MutableImpl> {
override fun process(annotation: MutableImpl, codeGenerator: CodeGenerator, classDeclaration: KSClassDeclaration) {
if (classDeclaration.classKind == ClassKind.CLASS && Modifier.ABSTRACT !in classDeclaration.modifiers)
throw IllegalStateException("Unable to create realization from non-abstract class")
else if (classDeclaration.classKind != ClassKind.INTERFACE)
throw IllegalStateException("Unable to create realization from ${classDeclaration.classKind}.")

codeGenerator.createNewFile(
Dependencies(false),
classDeclaration.packageName.asString(),
"Mutable${classDeclaration.simpleName.asString()}"
).use { output ->
OutputStreamWriter(output).use { writer ->
generateVariant("Mutable", true, classDeclaration).writeTo(writer)
ImplementationFileCodeGeneration.Data(true, classDeclaration).generate()
.writeTo(writer)
}
}
}
Expand Down
Loading

0 comments on commit a288ebb

Please sign in to comment.