From 3718181e8fe11466606d581bfe7cfb624408f956 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sat, 25 Feb 2023 18:44:07 +0600 Subject: [PATCH 1/4] start 0.16.13 --- CHANGELOG.md | 2 ++ gradle.properties | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8795d05b210..27f16a803d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # Changelog +## 0.16.13 + ## 0.16.12 * `Repos`: diff --git a/gradle.properties b/gradle.properties index 9a4f1279a67..10a4b54dcbf 100644 --- a/gradle.properties +++ b/gradle.properties @@ -14,5 +14,5 @@ crypto_js_version=4.1.1 # Project data group=dev.inmo -version=0.16.12 -android_code_version=180 +version=0.16.13 +android_code_version=181 From d2e6d2ec80719e3f7bf638a3d15b09c41813900e Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sat, 25 Feb 2023 19:56:12 +0600 Subject: [PATCH 2/4] generators for models has been created --- CHANGELOG.md | 4 + .../repos/annotations/GenerateCRUDModel.kt | 35 ++++ repos/generator/README.md | 154 ++++++++++++++ repos/generator/build.gradle | 16 ++ repos/generator/src/main/kotlin/Processor.kt | 193 ++++++++++++++++++ repos/generator/src/main/kotlin/Provider.kt | 11 + ...ols.ksp.processing.SymbolProcessorProvider | 1 + repos/generator/test/build.gradle | 27 +++ .../commonMain/kotlin/GeneratedModelsTest.kt | 28 +++ .../test/src/commonMain/kotlin/Test.kt | 30 +++ .../test/src/main/AndroidManifest.xml | 1 + settings.gradle | 2 + 12 files changed, 502 insertions(+) create mode 100644 repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/annotations/GenerateCRUDModel.kt create mode 100644 repos/generator/README.md create mode 100644 repos/generator/build.gradle create mode 100644 repos/generator/src/main/kotlin/Processor.kt create mode 100644 repos/generator/src/main/kotlin/Provider.kt create mode 100644 repos/generator/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider create mode 100644 repos/generator/test/build.gradle create mode 100644 repos/generator/test/src/commonMain/kotlin/GeneratedModelsTest.kt create mode 100644 repos/generator/test/src/commonMain/kotlin/Test.kt create mode 100644 repos/generator/test/src/main/AndroidManifest.xml diff --git a/CHANGELOG.md b/CHANGELOG.md index 27f16a803d3..58477f114b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## 0.16.13 +* `Repos`: + * `Generator`: + * Module has been created + ## 0.16.12 * `Repos`: diff --git a/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/annotations/GenerateCRUDModel.kt b/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/annotations/GenerateCRUDModel.kt new file mode 100644 index 00000000000..d77e5ee8641 --- /dev/null +++ b/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/annotations/GenerateCRUDModel.kt @@ -0,0 +1,35 @@ +package dev.inmo.micro_utils.repos.annotations + +import kotlin.reflect.KClass + +/** + * Use this annotation and ksp generator (module `micro_utils.repos.generator`) to create the next hierarchy of models: + * + * * New model. For example: data class NewTest + * * Registered model. For example: data class RegisteredTest + * + * @param registeredSupertypes These [KClass]es will be used as supertypes for registered model + * @param serializable If true (default) will generate @[kotlinx.serialization.Serializable] for models. Affects [generateSerialName] + * @param serializable If true (default) will generate @[kotlinx.serialization.SerialName] for models with their names as values + * + * @see GenerateCRUDModelExcludeOverride + */ +@Retention(AnnotationRetention.BINARY) +@Target(AnnotationTarget.CLASS) +annotation class GenerateCRUDModel( + vararg val registeredSupertypes: KClass<*>, + val serializable: Boolean = true, + val generateSerialName: Boolean = true +) + + +/** + * Use this annotation on properties which should be excluded from overriding in models. + * + * @see GenerateCRUDModel + */ +@Retention(AnnotationRetention.BINARY) +@Target(AnnotationTarget.PROPERTY) +annotation class GenerateCRUDModelExcludeOverride + + diff --git a/repos/generator/README.md b/repos/generator/README.md new file mode 100644 index 00000000000..d23a6233625 --- /dev/null +++ b/repos/generator/README.md @@ -0,0 +1,154 @@ +# Koin generator + +It is Kotlin Symbol Processing generator for simple creating of typical models: `New` and `Registered`. + +1. [What may do this generator](#what-may-do-this-generator) +2. [How to add generator](#how-to-add-generator) + +## What may do this generator + +So, you have several known things related to models: + +* Interface with all necessary properties +* Id class or some registered marker + +Minimal sample will be next: + +```kotlin +@GenerateCRUDModel +interface Sample { + val property1: String + val property2: Int +} +``` + +And generator will create: + +```kotlin +@Serializable +@SerialName("NewSample") +data class NewSample( + override val property1: String, + override val property2: Int, +) : Sample + +@Serializable +@SerialName("RegisteredSample") +data class RegisteredSample( + override val property1: String, + override val property2: Int, +) : Sample + +fun Sample.asNew(): NewSample = NewSample(property1, property2) + +fun Sample.asRegistered(): RegisteredSample = RegisteredSample(property1, property2) +``` + +But in most cases you will need to create some id class and registered interface: + +```kotlin +@Serializable +@JvmInline +value class SampleId( + val long: Long +) + +sealed interface IRegisteredSample : Sample { + val id: SampleId + + @GenerateCRUDModelExcludeOverride + val excludedProperty2: Boolean + get() = false +} +``` + +As you may see, we have added `GenerateCRUDModelExcludeOverride` annotation. Properties marked with this annotation +WILL NOT be inclued into overriding in registered class (or your base interface if used there). So, if you will wish to +create model with id, use next form: + +```kotlin +@GenerateCRUDModel(IRegisteredSample::class) +interface Sample { + val property1: String + val property2: Int +} +``` + +And generated registered class will be changed: + +```kotlin +@Serializable +@SerialName(value = "NewSample") +data class NewSample( + override val property1: String, + override val property2: Int, +) : Sample + +@Serializable +@SerialName(value = "RegisteredSample") +data class RegisteredSample( + override val id: SampleId, + override val property1: String, + override val property2: Int, +) : Sample, IRegisteredSample + +fun Sample.asNew(): NewSample = NewSample(property1, property2) + +fun Sample.asRegistered(id: SampleId): RegisteredSample = RegisteredSample(id, property1, property2) +``` + +So, full sample will look like: + +```kotlin +/** + * Your id value class. In fact, but it is not necessary + */ +@Serializable +@JvmInline +value class SampleId( + val long: Long +) + +@GenerateCRUDModel(IRegisteredSample::class) +sealed interface Sample { + val property1: String + val property2: Int + + @GenerateCRUDModelExcludeOverride + val excludedProperty: String + get() = "excluded" +} + +sealed interface IRegisteredSample : Sample { + val id: SampleId + + @GenerateCRUDModelExcludeOverride + val excludedProperty2: Boolean + get() = false +} +``` + +You always may: + +* Use any number of registered classes +* Disable serialization for models +* Disable serial names generation + +## How to add generator + +**Note: $ksp_version in the samples above is equal to supported `ksp` version presented in `/gradle/libs.versions.toml` of project** + +**Note: $microutils_version in the version of MicroUtils library in your project** + +1. Add `classpath` in `build.gradle` (`classpath "com.google.devtools.ksp:symbol-processing-gradle-plugin:$ksp_version"`) +2. Add plugin to the plugins list of your module: `id "com.google.devtools.ksp"` +3. In `dependencies` block add to the required target/compile the dependency `dev.inmo:micro_utils.repos.generator:$microutils_version`: + ```groovy + dependencies { + add("kspCommonMainMetadata", "dev.inmo:micro_utils.repos.generator:$microutils_version") // will work in commonMain of your multiplatform module + add("kspJvm", "dev.inmo:micro_utils.repos.generator:$microutils_version") // will work in main of your JVM module + } + + ksp { // this generator do not require any arguments and we should left `ksp` empty + } + ``` diff --git a/repos/generator/build.gradle b/repos/generator/build.gradle new file mode 100644 index 00000000000..112b1af1eac --- /dev/null +++ b/repos/generator/build.gradle @@ -0,0 +1,16 @@ +plugins { + id "org.jetbrains.kotlin.jvm" +} + +apply from: "$publishJvmOnlyPath" + +repositories { + mavenCentral() +} + +dependencies { + api libs.kt.reflect + api project(":micro_utils.repos.common") + api libs.kotlin.poet + api libs.ksp +} diff --git a/repos/generator/src/main/kotlin/Processor.kt b/repos/generator/src/main/kotlin/Processor.kt new file mode 100644 index 00000000000..dfa26799239 --- /dev/null +++ b/repos/generator/src/main/kotlin/Processor.kt @@ -0,0 +1,193 @@ +package dev.inmo.micro_utils.repos.generator + +import com.google.devtools.ksp.KspExperimental +import com.google.devtools.ksp.getAnnotationsByType +import com.google.devtools.ksp.isAnnotationPresent +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.KSAnnotated +import com.google.devtools.ksp.symbol.KSClassDeclaration +import com.google.devtools.ksp.symbol.KSPropertyDeclaration +import com.google.devtools.ksp.symbol.KSType +import com.squareup.kotlinpoet.AnnotationSpec +import com.squareup.kotlinpoet.ClassName +import com.squareup.kotlinpoet.CodeBlock +import com.squareup.kotlinpoet.FileSpec +import com.squareup.kotlinpoet.FunSpec +import com.squareup.kotlinpoet.KModifier +import com.squareup.kotlinpoet.ParameterSpec +import com.squareup.kotlinpoet.PropertySpec +import com.squareup.kotlinpoet.TypeSpec +import com.squareup.kotlinpoet.asTypeName +import com.squareup.kotlinpoet.ksp.toAnnotationSpec +import com.squareup.kotlinpoet.ksp.toClassName +import com.squareup.kotlinpoet.ksp.toTypeName +import dev.inmo.micro_utils.repos.annotations.GenerateCRUDModel +import dev.inmo.micro_utils.repos.annotations.GenerateCRUDModelExcludeOverride +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import java.io.File +import kotlin.reflect.KProperty1 +import kotlin.reflect.full.memberProperties + +class Processor( + private val codeGenerator: CodeGenerator +) : SymbolProcessor { + @OptIn(KspExperimental::class) + override fun process(resolver: Resolver): List { + resolver.getSymbolsWithAnnotation( + GenerateCRUDModel::class.qualifiedName!! + ).filterIsInstance().forEach { ksClassDeclaration -> + val ksFile = ksClassDeclaration.containingFile ?: return@forEach + FileSpec.builder( + ksClassDeclaration.packageName.asString(), + "GeneratedModels${ksFile.fileName.removeSuffix(".kt")}" + ).apply { + val annotation = ksClassDeclaration.getAnnotationsByType(GenerateCRUDModel::class).first() + addFileComment( + """ + THIS CODE HAVE BEEN GENERATED AUTOMATICALLY + TO REGENERATE IT JUST DELETE FILE + ORIGINAL FILE: ${ksFile.fileName} + """.trimIndent() + ) + val newName = "New${ksClassDeclaration.simpleName.getShortName()}" + val registeredName = "Registered${ksClassDeclaration.simpleName.getShortName()}" + + val allKSClassProperties = ksClassDeclaration.getAllProperties() + val excludedKSClassProperties = allKSClassProperties.filter { + it.isAnnotationPresent(GenerateCRUDModelExcludeOverride::class) + } + val excludedKSClassPropertiesNames = excludedKSClassProperties.map { it.simpleName.asString() } + val ksClassProperties = allKSClassProperties.filter { + it !in excludedKSClassProperties + } + val ksClassPropertiesNames = ksClassProperties.map { it.simpleName.asString() } + val newNewType = TypeSpec.classBuilder(newName).apply { + val typeBuilder = this + addSuperinterface(ksClassDeclaration.toClassName()) + addModifiers(KModifier.DATA) + if (annotation.serializable) { + addAnnotation(Serializable::class) + if (annotation.generateSerialName) { + addAnnotation(AnnotationSpec.get(SerialName(newName))) + } + } + primaryConstructor( + FunSpec.constructorBuilder().apply { + ksClassProperties.forEach { + addParameter(it.simpleName.getShortName(), it.type.toTypeName()) + typeBuilder.addProperty( + PropertySpec.builder(it.simpleName.getShortName(), it.type.toTypeName(), KModifier.OVERRIDE).apply { + initializer(it.simpleName.getShortName()) + }.build() + ) + } + }.build() + ) + }.build() + addType( + newNewType + ) + + val registeredSupertypes = ksClassDeclaration.annotations.filter { + it.shortName.asString() == GenerateCRUDModel::class.simpleName && + it.annotationType.resolve().declaration.qualifiedName ?.asString() == GenerateCRUDModel::class.qualifiedName + }.flatMap { + (it.arguments.first().value as List).map { it.declaration as KSClassDeclaration } + }.toList() + + + val registeredTypesProperties: List = registeredSupertypes.flatMap { registeredType -> + registeredType.getAllProperties() + }.filter { + it.simpleName.asString() !in excludedKSClassPropertiesNames && it.getAnnotationsByType(GenerateCRUDModelExcludeOverride::class).none() + } + val allProperties: List = ksClassProperties.toList() + registeredTypesProperties + val propertiesToOverrideInRegistered = allProperties.distinctBy { it.simpleName.asString() }.sortedBy { property -> + val name = property.simpleName.asString() + + ksClassPropertiesNames.indexOf(name).takeIf { it > -1 } ?.let { + it + allProperties.size + } ?: allProperties.indexOfFirst { it.simpleName.asString() == name } + } + + val newRegisteredType = TypeSpec.classBuilder(registeredName).apply { + val typeBuilder = this + addSuperinterface(ksClassDeclaration.toClassName()) + if (annotation.serializable) { + addAnnotation(Serializable::class) + + if (annotation.generateSerialName) { + addAnnotation( + AnnotationSpec.get(SerialName(registeredName)) + ) + } + } + addSuperinterfaces(registeredSupertypes.map { it.toClassName() }) + addModifiers(KModifier.DATA) + primaryConstructor( + FunSpec.constructorBuilder().apply { + propertiesToOverrideInRegistered.forEach { + addParameter( + ParameterSpec.builder(it.simpleName.getShortName(), it.type.toTypeName()).apply { + annotations += it.annotations.map { it.toAnnotationSpec() } + }.build() + ) + typeBuilder.addProperty( + PropertySpec.builder(it.simpleName.getShortName(), it.type.toTypeName(), KModifier.OVERRIDE).apply { + initializer(it.simpleName.getShortName()) + }.build() + ) + } + }.build() + ) + }.build() + addType( + newRegisteredType + ) + + addFunction( + FunSpec.builder("asNew").apply { + receiver(ksClassDeclaration.toClassName()) + addCode( + CodeBlock.of( + "return ${newNewType.name}(${newNewType.propertySpecs.joinToString { it.name }})" + ) + ) + returns(ClassName(packageName, newNewType.name!!)) + }.build() + ) + + addFunction( + FunSpec.builder("asRegistered").apply { + receiver(ksClassDeclaration.toClassName()) + (registeredTypesProperties.filter { it.simpleName.asString() !in ksClassPropertiesNames }).forEach { + addParameter(it.simpleName.asString(), it.type.toTypeName()) + } + addCode( + CodeBlock.of( + "return ${newRegisteredType.name}(${newRegisteredType.propertySpecs.joinToString { it.name }})" + ) + ) + returns(ClassName(packageName, newRegisteredType.name!!)) + }.build() + ) + }.build().let { + File( + File(ksFile.filePath).parent, + "GeneratedModels${ksFile.fileName}" + ).takeIf { !it.exists() } ?.apply { + parentFile.mkdirs() + + writer().use { writer -> + it.writeTo(writer) + } + } + } + } + + return emptyList() + } +} diff --git a/repos/generator/src/main/kotlin/Provider.kt b/repos/generator/src/main/kotlin/Provider.kt new file mode 100644 index 00000000000..9610778e062 --- /dev/null +++ b/repos/generator/src/main/kotlin/Provider.kt @@ -0,0 +1,11 @@ +package dev.inmo.micro_utils.repos.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 + ) +} diff --git a/repos/generator/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider b/repos/generator/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider new file mode 100644 index 00000000000..a40559b08b3 --- /dev/null +++ b/repos/generator/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider @@ -0,0 +1 @@ +dev.inmo.micro_utils.repos.generator.Provider diff --git a/repos/generator/test/build.gradle b/repos/generator/test/build.gradle new file mode 100644 index 00000000000..84a7b71ace9 --- /dev/null +++ b/repos/generator/test/build.gradle @@ -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: "$mppProjectWithSerializationPresetPath" + + +kotlin { + sourceSets { + commonMain { + dependencies { + api project(":micro_utils.repos.common") + } + } + } +} + + +dependencies { + add("kspCommonMainMetadata", project(":micro_utils.repos.generator")) +} + +ksp { +} diff --git a/repos/generator/test/src/commonMain/kotlin/GeneratedModelsTest.kt b/repos/generator/test/src/commonMain/kotlin/GeneratedModelsTest.kt new file mode 100644 index 00000000000..bbad6b596c2 --- /dev/null +++ b/repos/generator/test/src/commonMain/kotlin/GeneratedModelsTest.kt @@ -0,0 +1,28 @@ +// THIS CODE HAVE BEEN GENERATED AUTOMATICALLY +// TO REGENERATE IT JUST DELETE FILE +// ORIGINAL FILE: Test.kt +package dev.inmo.micro_utils.repos.generator.test + +import kotlin.Int +import kotlin.String +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +@SerialName(value = "NewTest") +public data class NewTest( + public override val property1: String, + public override val property2: Int, +) : Test + +@Serializable +@SerialName(value = "RegisteredTest") +public data class RegisteredTest( + public override val id: TestId, + public override val property1: String, + public override val property2: Int, +) : Test, IRegisteredTest + +public fun Test.asNew(): NewTest = NewTest(property1, property2) + +public fun Test.asRegistered(id: TestId): RegisteredTest = RegisteredTest(id, property1, property2) diff --git a/repos/generator/test/src/commonMain/kotlin/Test.kt b/repos/generator/test/src/commonMain/kotlin/Test.kt new file mode 100644 index 00000000000..4a8ff7754b1 --- /dev/null +++ b/repos/generator/test/src/commonMain/kotlin/Test.kt @@ -0,0 +1,30 @@ +package dev.inmo.micro_utils.repos.generator.test + +import dev.inmo.micro_utils.repos.annotations.GenerateCRUDModel +import dev.inmo.micro_utils.repos.annotations.GenerateCRUDModelExcludeOverride +import kotlinx.serialization.Serializable +import kotlin.jvm.JvmInline + +@Serializable +@JvmInline +value class TestId( + val long: Long +) + +@GenerateCRUDModel(IRegisteredTest::class) +sealed interface Test { + val property1: String + val property2: Int + + @GenerateCRUDModelExcludeOverride + val excludedProperty: String + get() = "excluded" +} + +sealed interface IRegisteredTest : Test { + val id: TestId + + @GenerateCRUDModelExcludeOverride + val excludedProperty2: Boolean + get() = false +} diff --git a/repos/generator/test/src/main/AndroidManifest.xml b/repos/generator/test/src/main/AndroidManifest.xml new file mode 100644 index 00000000000..22910dac229 --- /dev/null +++ b/repos/generator/test/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + diff --git a/settings.gradle b/settings.gradle index 3ffca7736c8..6d422fe4e63 100644 --- a/settings.gradle +++ b/settings.gradle @@ -18,6 +18,8 @@ String[] includes = [ ":language_codes", ":language_codes:generator", ":repos:common", + ":repos:generator", + ":repos:generator:test", ":repos:cache", ":repos:exposed", ":repos:inmemory", From 8215f9d2c6f46203af974f85b1ba80b8100ca9ff Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sun, 26 Feb 2023 14:37:41 +0600 Subject: [PATCH 3/4] temporal solution of generating problem --- publish.gradle | 26 +- repos/generator/src/main/kotlin/Processor.kt | 290 ++++++++++-------- .../commonMain/kotlin/GeneratedModelsTest.kt | 7 +- .../test/src/commonMain/kotlin/Test.kt | 3 + 4 files changed, 178 insertions(+), 148 deletions(-) diff --git a/publish.gradle b/publish.gradle index d3300addb04..bd184636fe2 100644 --- a/publish.gradle +++ b/publish.gradle @@ -89,17 +89,17 @@ publishing { } if (project.hasProperty("signing.gnupg.keyName")) { - apply plugin: 'signing' - - signing { - useGpgCmd() - - sign publishing.publications - } - - task signAll { - tasks.withType(Sign).forEach { - dependsOn(it) - } - } +// apply plugin: 'signing' +// +// signing { +// useGpgCmd() +// +// sign publishing.publications +// } +// +// task signAll { +// tasks.withType(Sign).forEach { +// dependsOn(it) +// } +// } } diff --git a/repos/generator/src/main/kotlin/Processor.kt b/repos/generator/src/main/kotlin/Processor.kt index dfa26799239..9e8780b826c 100644 --- a/repos/generator/src/main/kotlin/Processor.kt +++ b/repos/generator/src/main/kotlin/Processor.kt @@ -8,8 +8,13 @@ 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.google.devtools.ksp.symbol.KSClassifierReference import com.google.devtools.ksp.symbol.KSPropertyDeclaration +import com.google.devtools.ksp.symbol.KSReferenceElement import com.google.devtools.ksp.symbol.KSType +import com.google.devtools.ksp.symbol.KSTypeAlias +import com.google.devtools.ksp.symbol.KSValueArgument +import com.google.devtools.ksp.symbol.Nullability import com.squareup.kotlinpoet.AnnotationSpec import com.squareup.kotlinpoet.ClassName import com.squareup.kotlinpoet.CodeBlock @@ -18,6 +23,7 @@ import com.squareup.kotlinpoet.FunSpec import com.squareup.kotlinpoet.KModifier import com.squareup.kotlinpoet.ParameterSpec import com.squareup.kotlinpoet.PropertySpec +import com.squareup.kotlinpoet.TypeName import com.squareup.kotlinpoet.TypeSpec import com.squareup.kotlinpoet.asTypeName import com.squareup.kotlinpoet.ksp.toAnnotationSpec @@ -31,163 +37,181 @@ import java.io.File import kotlin.reflect.KProperty1 import kotlin.reflect.full.memberProperties +private fun KSClassifierReference.quilifiedName(): String = "${qualifier ?.let { "${it.quilifiedName()}." } ?: ""}${referencedName()}" + class Processor( private val codeGenerator: CodeGenerator ) : SymbolProcessor { + private val KSPropertyDeclaration.typeName: TypeName + get() { + return runCatching { + type.toTypeName() + }.getOrElse { + val element = type.element as KSClassifierReference + (type.element as KSClassifierReference).let { + ClassName( + element.qualifier ?.quilifiedName() ?: "", + element.referencedName() + ) + } + } + } @OptIn(KspExperimental::class) override fun process(resolver: Resolver): List { - resolver.getSymbolsWithAnnotation( + val toRetry = resolver.getSymbolsWithAnnotation( GenerateCRUDModel::class.qualifiedName!! - ).filterIsInstance().forEach { ksClassDeclaration -> - val ksFile = ksClassDeclaration.containingFile ?: return@forEach - FileSpec.builder( - ksClassDeclaration.packageName.asString(), - "GeneratedModels${ksFile.fileName.removeSuffix(".kt")}" - ).apply { - val annotation = ksClassDeclaration.getAnnotationsByType(GenerateCRUDModel::class).first() - addFileComment( - """ + ).filterIsInstance().filterNot { ksClassDeclaration -> + val ksFile = ksClassDeclaration.containingFile ?: return@filterNot false + runCatching { + FileSpec.builder( + ksClassDeclaration.packageName.asString(), + "GeneratedModels${ksFile.fileName.removeSuffix(".kt")}" + ).apply { + val annotation = ksClassDeclaration.getAnnotationsByType(GenerateCRUDModel::class).first() + addFileComment( + """ THIS CODE HAVE BEEN GENERATED AUTOMATICALLY TO REGENERATE IT JUST DELETE FILE ORIGINAL FILE: ${ksFile.fileName} """.trimIndent() - ) - val newName = "New${ksClassDeclaration.simpleName.getShortName()}" - val registeredName = "Registered${ksClassDeclaration.simpleName.getShortName()}" + ) + val newName = "New${ksClassDeclaration.simpleName.getShortName()}" + val registeredName = "Registered${ksClassDeclaration.simpleName.getShortName()}" - val allKSClassProperties = ksClassDeclaration.getAllProperties() - val excludedKSClassProperties = allKSClassProperties.filter { - it.isAnnotationPresent(GenerateCRUDModelExcludeOverride::class) - } - val excludedKSClassPropertiesNames = excludedKSClassProperties.map { it.simpleName.asString() } - val ksClassProperties = allKSClassProperties.filter { - it !in excludedKSClassProperties - } - val ksClassPropertiesNames = ksClassProperties.map { it.simpleName.asString() } - val newNewType = TypeSpec.classBuilder(newName).apply { - val typeBuilder = this - addSuperinterface(ksClassDeclaration.toClassName()) - addModifiers(KModifier.DATA) - if (annotation.serializable) { - addAnnotation(Serializable::class) - if (annotation.generateSerialName) { - addAnnotation(AnnotationSpec.get(SerialName(newName))) - } + val allKSClassProperties = ksClassDeclaration.getAllProperties() + val excludedKSClassProperties = allKSClassProperties.filter { + it.isAnnotationPresent(GenerateCRUDModelExcludeOverride::class) } - primaryConstructor( - FunSpec.constructorBuilder().apply { - ksClassProperties.forEach { - addParameter(it.simpleName.getShortName(), it.type.toTypeName()) - typeBuilder.addProperty( - PropertySpec.builder(it.simpleName.getShortName(), it.type.toTypeName(), KModifier.OVERRIDE).apply { - initializer(it.simpleName.getShortName()) - }.build() + val excludedKSClassPropertiesNames = excludedKSClassProperties.map { it.simpleName.asString() } + val ksClassProperties = allKSClassProperties.filter { + it !in excludedKSClassProperties + } + val ksClassPropertiesNames = ksClassProperties.map { it.simpleName.asString() } + val newNewType = TypeSpec.classBuilder(newName).apply { + val typeBuilder = this + addSuperinterface(ksClassDeclaration.toClassName()) + addModifiers(KModifier.DATA) + if (annotation.serializable) { + addAnnotation(Serializable::class) + if (annotation.generateSerialName) { + addAnnotation(AnnotationSpec.get(SerialName(newName))) + } + } + primaryConstructor( + FunSpec.constructorBuilder().apply { + ksClassProperties.forEach { + addParameter(it.simpleName.getShortName(), it.typeName) + typeBuilder.addProperty( + PropertySpec.builder(it.simpleName.getShortName(), it.typeName, KModifier.OVERRIDE).apply { + initializer(it.simpleName.getShortName()) + }.build() + ) + } + }.build() + ) + }.build() + addType( + newNewType + ) + + val registeredSupertypes = ksClassDeclaration.annotations.filter { + it.shortName.asString() == GenerateCRUDModel::class.simpleName && + it.annotationType.resolve().declaration.qualifiedName ?.asString() == GenerateCRUDModel::class.qualifiedName + }.flatMap { + (it.arguments.first().value as List).map { it.declaration as KSClassDeclaration } + }.toList() + + + val registeredTypesProperties: List = registeredSupertypes.flatMap { registeredType -> + registeredType.getAllProperties() + }.filter { + it.simpleName.asString() !in excludedKSClassPropertiesNames && it.getAnnotationsByType(GenerateCRUDModelExcludeOverride::class).none() + } + val allProperties: List = ksClassProperties.toList() + registeredTypesProperties + val propertiesToOverrideInRegistered = allProperties.distinctBy { it.simpleName.asString() }.sortedBy { property -> + val name = property.simpleName.asString() + + ksClassPropertiesNames.indexOf(name).takeIf { it > -1 } ?.let { + it + allProperties.size + } ?: allProperties.indexOfFirst { it.simpleName.asString() == name } + } + + val newRegisteredType = TypeSpec.classBuilder(registeredName).apply { + val typeBuilder = this + addSuperinterface(ksClassDeclaration.toClassName()) + if (annotation.serializable) { + addAnnotation(Serializable::class) + + if (annotation.generateSerialName) { + addAnnotation( + AnnotationSpec.get(SerialName(registeredName)) ) } + } + addSuperinterfaces(registeredSupertypes.map { it.toClassName() }) + addModifiers(KModifier.DATA) + primaryConstructor( + FunSpec.constructorBuilder().apply { + propertiesToOverrideInRegistered.forEach { + addParameter( + ParameterSpec.builder(it.simpleName.getShortName(), it.typeName).apply { + annotations += it.annotations.map { it.toAnnotationSpec() } + }.build() + ) + typeBuilder.addProperty( + PropertySpec.builder(it.simpleName.getShortName(), it.typeName, KModifier.OVERRIDE).apply { + initializer(it.simpleName.getShortName()) + }.build() + ) + } + }.build() + ) + }.build() + addType( + newRegisteredType + ) + + addFunction( + FunSpec.builder("asNew").apply { + receiver(ksClassDeclaration.toClassName()) + addCode( + CodeBlock.of( + "return ${newNewType.name}(${newNewType.propertySpecs.joinToString { it.name }})" + ) + ) + returns(ClassName(packageName, newNewType.name!!)) }.build() ) - }.build() - addType( - newNewType - ) - val registeredSupertypes = ksClassDeclaration.annotations.filter { - it.shortName.asString() == GenerateCRUDModel::class.simpleName && - it.annotationType.resolve().declaration.qualifiedName ?.asString() == GenerateCRUDModel::class.qualifiedName - }.flatMap { - (it.arguments.first().value as List).map { it.declaration as KSClassDeclaration } - }.toList() - - - val registeredTypesProperties: List = registeredSupertypes.flatMap { registeredType -> - registeredType.getAllProperties() - }.filter { - it.simpleName.asString() !in excludedKSClassPropertiesNames && it.getAnnotationsByType(GenerateCRUDModelExcludeOverride::class).none() - } - val allProperties: List = ksClassProperties.toList() + registeredTypesProperties - val propertiesToOverrideInRegistered = allProperties.distinctBy { it.simpleName.asString() }.sortedBy { property -> - val name = property.simpleName.asString() - - ksClassPropertiesNames.indexOf(name).takeIf { it > -1 } ?.let { - it + allProperties.size - } ?: allProperties.indexOfFirst { it.simpleName.asString() == name } - } - - val newRegisteredType = TypeSpec.classBuilder(registeredName).apply { - val typeBuilder = this - addSuperinterface(ksClassDeclaration.toClassName()) - if (annotation.serializable) { - addAnnotation(Serializable::class) - - if (annotation.generateSerialName) { - addAnnotation( - AnnotationSpec.get(SerialName(registeredName)) - ) - } - } - addSuperinterfaces(registeredSupertypes.map { it.toClassName() }) - addModifiers(KModifier.DATA) - primaryConstructor( - FunSpec.constructorBuilder().apply { - propertiesToOverrideInRegistered.forEach { - addParameter( - ParameterSpec.builder(it.simpleName.getShortName(), it.type.toTypeName()).apply { - annotations += it.annotations.map { it.toAnnotationSpec() } - }.build() - ) - typeBuilder.addProperty( - PropertySpec.builder(it.simpleName.getShortName(), it.type.toTypeName(), KModifier.OVERRIDE).apply { - initializer(it.simpleName.getShortName()) - }.build() - ) + addFunction( + FunSpec.builder("asRegistered").apply { + receiver(ksClassDeclaration.toClassName()) + (registeredTypesProperties.filter { it.simpleName.asString() !in ksClassPropertiesNames }).forEach { + addParameter(it.simpleName.asString(), it.typeName) } + addCode( + CodeBlock.of( + "return ${newRegisteredType.name}(${newRegisteredType.propertySpecs.joinToString { it.name }})" + ) + ) + returns(ClassName(packageName, newRegisteredType.name!!)) }.build() ) - }.build() - addType( - newRegisteredType - ) + }.build().let { + File( + File(ksFile.filePath).parent, + "GeneratedModels${ksFile.fileName}" + ).takeIf { !it.exists() } ?.apply { + parentFile.mkdirs() - addFunction( - FunSpec.builder("asNew").apply { - receiver(ksClassDeclaration.toClassName()) - addCode( - CodeBlock.of( - "return ${newNewType.name}(${newNewType.propertySpecs.joinToString { it.name }})" - ) - ) - returns(ClassName(packageName, newNewType.name!!)) - }.build() - ) - - addFunction( - FunSpec.builder("asRegistered").apply { - receiver(ksClassDeclaration.toClassName()) - (registeredTypesProperties.filter { it.simpleName.asString() !in ksClassPropertiesNames }).forEach { - addParameter(it.simpleName.asString(), it.type.toTypeName()) + writer().use { writer -> + it.writeTo(writer) } - addCode( - CodeBlock.of( - "return ${newRegisteredType.name}(${newRegisteredType.propertySpecs.joinToString { it.name }})" - ) - ) - returns(ClassName(packageName, newRegisteredType.name!!)) - }.build() - ) - }.build().let { - File( - File(ksFile.filePath).parent, - "GeneratedModels${ksFile.fileName}" - ).takeIf { !it.exists() } ?.apply { - parentFile.mkdirs() - - writer().use { writer -> - it.writeTo(writer) } } - } - } + }.isSuccess + }.toList() - return emptyList() + return toRetry } } diff --git a/repos/generator/test/src/commonMain/kotlin/GeneratedModelsTest.kt b/repos/generator/test/src/commonMain/kotlin/GeneratedModelsTest.kt index bbad6b596c2..27111d44cc8 100644 --- a/repos/generator/test/src/commonMain/kotlin/GeneratedModelsTest.kt +++ b/repos/generator/test/src/commonMain/kotlin/GeneratedModelsTest.kt @@ -13,6 +13,7 @@ import kotlinx.serialization.Serializable public data class NewTest( public override val property1: String, public override val property2: Int, + public override val parent: ParentTypeId?, ) : Test @Serializable @@ -21,8 +22,10 @@ public data class RegisteredTest( public override val id: TestId, public override val property1: String, public override val property2: Int, + public override val parent: ParentTypeId?, ) : Test, IRegisteredTest -public fun Test.asNew(): NewTest = NewTest(property1, property2) +public fun Test.asNew(): NewTest = NewTest(property1, property2, parent) -public fun Test.asRegistered(id: TestId): RegisteredTest = RegisteredTest(id, property1, property2) +public fun Test.asRegistered(id: TestId): RegisteredTest = RegisteredTest(id, property1, property2, + parent) diff --git a/repos/generator/test/src/commonMain/kotlin/Test.kt b/repos/generator/test/src/commonMain/kotlin/Test.kt index 4a8ff7754b1..24bc9a92f4b 100644 --- a/repos/generator/test/src/commonMain/kotlin/Test.kt +++ b/repos/generator/test/src/commonMain/kotlin/Test.kt @@ -11,10 +11,13 @@ value class TestId( val long: Long ) +typealias ParentTypeId = TestId + @GenerateCRUDModel(IRegisteredTest::class) sealed interface Test { val property1: String val property2: Int + val parent: ParentTypeId? @GenerateCRUDModelExcludeOverride val excludedProperty: String From 5a765ea1bc56540e5052838919f49a19d883f209 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Mon, 27 Feb 2023 15:43:52 +0600 Subject: [PATCH 4/4] get back publishing signing --- publish.gradle | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/publish.gradle b/publish.gradle index bd184636fe2..d3300addb04 100644 --- a/publish.gradle +++ b/publish.gradle @@ -89,17 +89,17 @@ publishing { } if (project.hasProperty("signing.gnupg.keyName")) { -// apply plugin: 'signing' -// -// signing { -// useGpgCmd() -// -// sign publishing.publications -// } -// -// task signAll { -// tasks.withType(Sign).forEach { -// dependsOn(it) -// } -// } + apply plugin: 'signing' + + signing { + useGpgCmd() + + sign publishing.publications + } + + task signAll { + tasks.withType(Sign).forEach { + dependsOn(it) + } + } }