mirror of
				https://github.com/InsanusMokrassar/MicroUtils.git
				synced 2025-10-31 12:10:29 +00:00 
			
		
		
		
	start add variations generator
This commit is contained in:
		| @@ -1,11 +1,13 @@ | |||||||
| package dev.inmo.micro_ksp.generator | package dev.inmo.micro_ksp.generator | ||||||
|  |  | ||||||
| import com.google.devtools.ksp.symbol.KSClassDeclaration | 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.KSFile | ||||||
|  | import com.google.devtools.ksp.symbol.KSFunctionDeclaration | ||||||
| import com.squareup.kotlinpoet.FileSpec | import com.squareup.kotlinpoet.FileSpec | ||||||
| import java.io.File | import java.io.File | ||||||
|  |  | ||||||
| fun KSClassDeclaration.writeFile( | fun KSDeclaration.writeFile( | ||||||
|     prefix: String = "", |     prefix: String = "", | ||||||
|     suffix: String = "", |     suffix: String = "", | ||||||
|     relatedPath: String = "", |     relatedPath: String = "", | ||||||
| @@ -21,8 +23,9 @@ fun KSClassDeclaration.writeFile( | |||||||
|         "$prefix${simpleName.asString()}$suffix.kt" |         "$prefix${simpleName.asString()}$suffix.kt" | ||||||
|     ).takeIf { force || !it.exists() } ?.apply { |     ).takeIf { force || !it.exists() } ?.apply { | ||||||
|         parentFile.mkdirs() |         parentFile.mkdirs() | ||||||
|  |         val fileSpec = fileSpecBuilder() | ||||||
|         writer().use { writer -> |         writer().use { writer -> | ||||||
|             fileSpecBuilder().writeTo(writer) |             fileSpec.writeTo(writer) | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -42,8 +45,9 @@ fun KSFile.writeFile( | |||||||
|         "$prefix${fileName.dropLastWhile { it != '.' }.removeSuffix(".")}$suffix.kt" |         "$prefix${fileName.dropLastWhile { it != '.' }.removeSuffix(".")}$suffix.kt" | ||||||
|     ).takeIf { force || !it.exists() } ?.apply { |     ).takeIf { force || !it.exists() } ?.apply { | ||||||
|         parentFile.mkdirs() |         parentFile.mkdirs() | ||||||
|  |         val fileSpec = fileSpecBuilder() | ||||||
|         writer().use { writer -> |         writer().use { writer -> | ||||||
|             fileSpecBuilder().writeTo(writer) |             fileSpec.writeTo(writer) | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
							
								
								
									
										7
									
								
								ksp/variations/build.gradle
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								ksp/variations/build.gradle
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | |||||||
|  | plugins { | ||||||
|  |     id "org.jetbrains.kotlin.multiplatform" | ||||||
|  |     id "org.jetbrains.kotlin.plugin.serialization" | ||||||
|  |     id "com.android.library" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project" | ||||||
							
								
								
									
										21
									
								
								ksp/variations/generator/build.gradle
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								ksp/variations/generator/build.gradle
									
									
									
									
									
										Normal file
									
								
							| @@ -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
									
								
								ksp/variations/generator/src/main/kotlin/Processor.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										148
									
								
								ksp/variations/generator/src/main/kotlin/Processor.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -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
									
								
								ksp/variations/generator/src/main/kotlin/Provider.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								ksp/variations/generator/src/main/kotlin/Provider.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||||
|  |     ) | ||||||
|  | } | ||||||
| @@ -0,0 +1 @@ | |||||||
|  | dev.inmo.micro_utils.ksp.variations.generator.Provider | ||||||
							
								
								
									
										27
									
								
								ksp/variations/generator/test/build.gradle
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								ksp/variations/generator/test/build.gradle
									
									
									
									
									
										Normal file
									
								
							| @@ -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 { | ||||||
|  | } | ||||||
| @@ -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) | ||||||
| @@ -0,0 +1,7 @@ | |||||||
|  | package dev.inmo.micro_utils.ksp.variations | ||||||
|  |  | ||||||
|  | @Retention(AnnotationRetention.BINARY) | ||||||
|  | @Target(AnnotationTarget.FUNCTION) | ||||||
|  | annotation class GenerateVariations( | ||||||
|  |     val prefix: String = "" | ||||||
|  | ) | ||||||
							
								
								
									
										13
									
								
								ksp/variations/src/commonMain/kotlin/GenerationVariant.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								ksp/variations/src/commonMain/kotlin/GenerationVariant.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -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<*> | ||||||
|  | ) | ||||||
| @@ -62,6 +62,10 @@ String[] includes = [ | |||||||
|     ":ksp:classcasts:generator", |     ":ksp:classcasts:generator", | ||||||
|     ":ksp:classcasts:generator:test", |     ":ksp:classcasts:generator:test", | ||||||
|  |  | ||||||
|  |     ":ksp:variations", | ||||||
|  |     ":ksp:variations:generator", | ||||||
|  |     ":ksp:variations:generator:test", | ||||||
|  |  | ||||||
|     ":dokka" |     ":dokka" | ||||||
| ] | ] | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user