mirror of
				https://github.com/InsanusMokrassar/MicroUtils.git
				synced 2025-10-30 03:30:19 +00:00 
			
		
		
		
	Compare commits
	
		
			11 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 5a765ea1bc | |||
| 8215f9d2c6 | |||
| d2e6d2ec80 | |||
| 3718181e8f | |||
| 0d825cf424 | |||
| 28a804d988 | |||
| 9e4bb9d678 | |||
| 9c40d7da3d | |||
| 2b76ad0aa9 | |||
| e4b619e050 | |||
| 36c09feaf2 | 
							
								
								
									
										15
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								CHANGELOG.md
									
									
									
									
									
								
							| @@ -1,5 +1,20 @@ | |||||||
| # Changelog | # Changelog | ||||||
|  |  | ||||||
|  | ## 0.16.13 | ||||||
|  |  | ||||||
|  | * `Repos`: | ||||||
|  |     * `Generator`: | ||||||
|  |         * Module has been created | ||||||
|  |  | ||||||
|  | ## 0.16.12 | ||||||
|  |  | ||||||
|  | * `Repos`: | ||||||
|  |     * `Exposed`: | ||||||
|  |         * `CommonExposedRepo.selectByIds` uses `foldRight` by default instead of raw foreach | ||||||
|  | * `Koin`: | ||||||
|  |     * `Generator`: | ||||||
|  |         * Module has been created | ||||||
|  |  | ||||||
| ## 0.16.11 | ## 0.16.11 | ||||||
|  |  | ||||||
| * `LanguageCodes`: | * `LanguageCodes`: | ||||||
|   | |||||||
| @@ -9,6 +9,7 @@ buildscript { | |||||||
|     dependencies { |     dependencies { | ||||||
|         classpath libs.buildscript.kt.gradle |         classpath libs.buildscript.kt.gradle | ||||||
|         classpath libs.buildscript.kt.serialization |         classpath libs.buildscript.kt.serialization | ||||||
|  |         classpath libs.buildscript.kt.ksp | ||||||
|         classpath libs.buildscript.jb.dokka |         classpath libs.buildscript.jb.dokka | ||||||
|         classpath libs.buildscript.gh.release |         classpath libs.buildscript.gh.release | ||||||
|         classpath libs.buildscript.android.gradle |         classpath libs.buildscript.android.gradle | ||||||
|   | |||||||
| @@ -29,6 +29,6 @@ allprojects { | |||||||
|         defaultAndroidSettingsPresetPath = "${rootProject.projectDir.absolutePath}/defaultAndroidSettings.gradle" |         defaultAndroidSettingsPresetPath = "${rootProject.projectDir.absolutePath}/defaultAndroidSettings.gradle" | ||||||
|  |  | ||||||
|         publishGradlePath = "${rootProject.projectDir.absolutePath}/publish.gradle" |         publishGradlePath = "${rootProject.projectDir.absolutePath}/publish.gradle" | ||||||
|         publishMavenPath = "${rootProject.projectDir.absolutePath}/maven.publish.gradle" |         publishJvmOnlyPath = "${rootProject.projectDir.absolutePath}/jvm.publish.gradle" | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -14,5 +14,5 @@ crypto_js_version=4.1.1 | |||||||
| # Project data | # Project data | ||||||
|  |  | ||||||
| group=dev.inmo | group=dev.inmo | ||||||
| version=0.16.11 | version=0.16.13 | ||||||
| android_code_version=179 | android_code_version=181 | ||||||
|   | |||||||
| @@ -19,6 +19,9 @@ gh-release = "2.4.1" | |||||||
|  |  | ||||||
| koin = "3.3.2" | koin = "3.3.2" | ||||||
|  |  | ||||||
|  | ksp = "1.7.20-1.0.8" | ||||||
|  | kotlin-poet = "1.12.0" | ||||||
|  |  | ||||||
| android-gradle = "7.3.0" | android-gradle = "7.3.0" | ||||||
| dexcount = "3.1.0" | dexcount = "3.1.0" | ||||||
|  |  | ||||||
| @@ -83,9 +86,16 @@ android-test-junit = { module = "androidx.test.ext:junit", version.ref = "androi | |||||||
| kt-test-js = { module = "org.jetbrains.kotlin:kotlin-test-js", version.ref = "kt" } | kt-test-js = { module = "org.jetbrains.kotlin:kotlin-test-js", version.ref = "kt" } | ||||||
| kt-test-junit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kt" } | kt-test-junit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kt" } | ||||||
|  |  | ||||||
|  | # ksp dependencies | ||||||
|  |  | ||||||
|  | kotlin-poet = { module = "com.squareup:kotlinpoet-ksp", version.ref = "kotlin-poet" } | ||||||
|  | ksp = { module = "com.google.devtools.ksp:symbol-processing-api", version.ref = "ksp" } | ||||||
|  |  | ||||||
|  | # Buildscript | ||||||
|  |  | ||||||
| buildscript-kt-gradle = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kt" } | buildscript-kt-gradle = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kt" } | ||||||
| buildscript-kt-serialization = { module = "org.jetbrains.kotlin:kotlin-serialization", version.ref = "kt" } | buildscript-kt-serialization = { module = "org.jetbrains.kotlin:kotlin-serialization", version.ref = "kt" } | ||||||
|  | buildscript-kt-ksp = { module = "com.google.devtools.ksp:symbol-processing-gradle-plugin", version.ref = "ksp" } | ||||||
| buildscript-jb-dokka = { module = "org.jetbrains.dokka:dokka-gradle-plugin", version.ref = "jb-dokka" } | buildscript-jb-dokka = { module = "org.jetbrains.dokka:dokka-gradle-plugin", version.ref = "jb-dokka" } | ||||||
| buildscript-gh-release = { module = "com.github.breadmoirai:github-release", version.ref = "gh-release" } | buildscript-gh-release = { module = "com.github.breadmoirai:github-release", version.ref = "gh-release" } | ||||||
| buildscript-android-gradle = { module = "com.android.tools.build:gradle", version.ref = "android-gradle" } | buildscript-android-gradle = { module = "com.android.tools.build:gradle", version.ref = "android-gradle" } | ||||||
|   | |||||||
							
								
								
									
										118
									
								
								jvm.publish.gradle
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								jvm.publish.gradle
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,118 @@ | |||||||
|  | apply plugin: 'maven-publish' | ||||||
|  |  | ||||||
|  | task javadocJar(type: Jar) { | ||||||
|  |     from javadoc | ||||||
|  |     classifier = 'javadoc' | ||||||
|  | } | ||||||
|  | task sourcesJar(type: Jar) { | ||||||
|  |     from sourceSets.main.allSource | ||||||
|  |     classifier = 'sources' | ||||||
|  | } | ||||||
|  |  | ||||||
|  | publishing { | ||||||
|  |     publications { | ||||||
|  |         maven(MavenPublication) { | ||||||
|  |             from components.java | ||||||
|  |  | ||||||
|  |             artifact javadocJar | ||||||
|  |             artifact sourcesJar | ||||||
|  |  | ||||||
|  |             pom { | ||||||
|  |                 resolveStrategy = Closure.DELEGATE_FIRST | ||||||
|  |  | ||||||
|  |                 description = "It is set of projects with micro tools for avoiding of routines coding" | ||||||
|  |                 name = "${project.name}" | ||||||
|  |                 url = "https://github.com/InsanusMokrassar/MicroUtils/" | ||||||
|  |  | ||||||
|  |                 scm { | ||||||
|  |                     developerConnection = "scm:git:[fetch=]https://github.com/InsanusMokrassar/MicroUtils.git[push=]https://github.com/InsanusMokrassar/MicroUtils.git" | ||||||
|  |                     url = "https://github.com/InsanusMokrassar/MicroUtils.git" | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 developers { | ||||||
|  |                      | ||||||
|  |                         developer { | ||||||
|  |                             id = "InsanusMokrassar" | ||||||
|  |                             name = "Aleksei Ovsiannikov" | ||||||
|  |                             email = "ovsyannikov.alexey95@gmail.com" | ||||||
|  |                         } | ||||||
|  |                      | ||||||
|  |  | ||||||
|  |                         developer { | ||||||
|  |                             id = "000Sanya" | ||||||
|  |                             name = "Syrov Aleksandr" | ||||||
|  |                             email = "000sanya.000sanya@gmail.com" | ||||||
|  |                         } | ||||||
|  |                      | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 licenses { | ||||||
|  |                      | ||||||
|  |                         license { | ||||||
|  |                             name = "Apache Software License 2.0" | ||||||
|  |                             url = "https://github.com/InsanusMokrassar/MicroUtils/blob/master/LICENSE" | ||||||
|  |                         } | ||||||
|  |                      | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             repositories { | ||||||
|  |                 if ((project.hasProperty('GITHUBPACKAGES_USER') || System.getenv('GITHUBPACKAGES_USER') != null) && (project.hasProperty('GITHUBPACKAGES_PASSWORD') || System.getenv('GITHUBPACKAGES_PASSWORD') != null)) { | ||||||
|  |                     maven { | ||||||
|  |                         name = "GithubPackages" | ||||||
|  |                         url = uri("https://maven.pkg.github.com/InsanusMokrassar/MicroUtils") | ||||||
|  |                  | ||||||
|  |                         credentials { | ||||||
|  |                             username = project.hasProperty('GITHUBPACKAGES_USER') ? project.property('GITHUBPACKAGES_USER') : System.getenv('GITHUBPACKAGES_USER') | ||||||
|  |                             password = project.hasProperty('GITHUBPACKAGES_PASSWORD') ? project.property('GITHUBPACKAGES_PASSWORD') : System.getenv('GITHUBPACKAGES_PASSWORD') | ||||||
|  |                         } | ||||||
|  |                  | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 if (project.hasProperty('GITEA_TOKEN') || System.getenv('GITEA_TOKEN') != null) { | ||||||
|  |                     maven { | ||||||
|  |                         name = "Gitea" | ||||||
|  |                         url = uri("https://git.inmo.dev/api/packages/InsanusMokrassar/maven") | ||||||
|  |                  | ||||||
|  |                         credentials(HttpHeaderCredentials) { | ||||||
|  |                             name = "Authorization" | ||||||
|  |                             value = project.hasProperty('GITEA_TOKEN') ? project.property('GITEA_TOKEN') : System.getenv('GITEA_TOKEN') | ||||||
|  |                         } | ||||||
|  |                  | ||||||
|  |                         authentication { | ||||||
|  |                             header(HttpHeaderAuthentication) | ||||||
|  |                         } | ||||||
|  |                  | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 if ((project.hasProperty('SONATYPE_USER') || System.getenv('SONATYPE_USER') != null) && (project.hasProperty('SONATYPE_PASSWORD') || System.getenv('SONATYPE_PASSWORD') != null)) { | ||||||
|  |                     maven { | ||||||
|  |                         name = "sonatype" | ||||||
|  |                         url = uri("https://oss.sonatype.org/service/local/staging/deploy/maven2/") | ||||||
|  |                  | ||||||
|  |                         credentials { | ||||||
|  |                             username = project.hasProperty('SONATYPE_USER') ? project.property('SONATYPE_USER') : System.getenv('SONATYPE_USER') | ||||||
|  |                             password = project.hasProperty('SONATYPE_PASSWORD') ? project.property('SONATYPE_PASSWORD') : System.getenv('SONATYPE_PASSWORD') | ||||||
|  |                         } | ||||||
|  |                  | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | if (project.hasProperty("signing.gnupg.keyName")) { | ||||||
|  |     apply plugin: 'signing' | ||||||
|  |      | ||||||
|  |     signing { | ||||||
|  |         useGpgCmd() | ||||||
|  |      | ||||||
|  |         sign publishing.publications | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     task signAll { | ||||||
|  |         tasks.withType(Sign).forEach { | ||||||
|  |             dependsOn(it) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										1
									
								
								jvm.publish.kpsb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								jvm.publish.kpsb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | {"licenses":[{"id":"Apache-2.0","title":"Apache Software License 2.0","url":"https://github.com/InsanusMokrassar/MicroUtils/blob/master/LICENSE"}],"mavenConfig":{"name":"${project.name}","description":"It is set of projects with micro tools for avoiding of routines coding","url":"https://github.com/InsanusMokrassar/MicroUtils/","vcsUrl":"https://github.com/InsanusMokrassar/MicroUtils.git","developers":[{"id":"InsanusMokrassar","name":"Aleksei Ovsiannikov","eMail":"ovsyannikov.alexey95@gmail.com"},{"id":"000Sanya","name":"Syrov Aleksandr","eMail":"000sanya.000sanya@gmail.com"}],"repositories":[{"name":"GithubPackages","url":"https://maven.pkg.github.com/InsanusMokrassar/MicroUtils"},{"name":"Gitea","url":"https://git.inmo.dev/api/packages/InsanusMokrassar/maven","credsType":{"type":"dev.inmo.kmppscriptbuilder.core.models.MavenPublishingRepository.CredentialsType.HttpHeaderCredentials","headerName":"Authorization","headerValueProperty":"GITEA_TOKEN"}},{"name":"sonatype","url":"https://oss.sonatype.org/service/local/staging/deploy/maven2/"}],"gpgSigning":{"type":"dev.inmo.kmppscriptbuilder.core.models.GpgSigning.Optional"}},"type":"JVM"} | ||||||
							
								
								
									
										100
									
								
								koin/generator/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								koin/generator/README.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,100 @@ | |||||||
|  | # Koin generator | ||||||
|  |  | ||||||
|  | It is Kotlin Symbol Processing generator for `Koin` module in `MicroUtils`. | ||||||
|  |  | ||||||
|  | 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 | ||||||
|  |  | ||||||
|  | Let's imagine you want to have shortcuts in koin, to get something easily: | ||||||
|  |  | ||||||
|  | ```kotlin | ||||||
|  | val koin: Koin// some initialization | ||||||
|  |  | ||||||
|  | val someUrl = koin.serverUrl | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | So, in that case you may mark containing file with next annotation (in the beginning of file): | ||||||
|  |  | ||||||
|  | ```kotlin | ||||||
|  | @file:GenerateKoinDefinition("serverUrl", String::class, nullable = false) | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | If file is called like `Sample.kt`, will be generated file `GeneratedDefinitionsSample.kt` with next content: | ||||||
|  |  | ||||||
|  | ```kotlin | ||||||
|  | public val Scope.serverUrl: String | ||||||
|  |   get() = get(named("serverUrl")) | ||||||
|  |  | ||||||
|  | public val Koin.serverUrl: String | ||||||
|  |   get() = get(named("serverUrl")) | ||||||
|  |  | ||||||
|  | public fun Module.serverUrlSingle(createdAtStart: Boolean = false, | ||||||
|  |     definition: Definition<String>): KoinDefinition<String> = | ||||||
|  |     single(named("serverUrl"), createdAtStart = createdAtStart, definition = definition) | ||||||
|  |  | ||||||
|  | public fun Module.serverUrlFactory(definition: Definition<String>): | ||||||
|  |     KoinDefinition<String> = factory(named("serverUrl"), definition = definition) | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | Besides, you may use the generics: | ||||||
|  |  | ||||||
|  | ```kotlin | ||||||
|  | @file:GenerateKoinDefinition("sampleInfo", Sample::class, G1::class, G2::class, nullable = false) | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | Will generate: | ||||||
|  |  | ||||||
|  | ```kotlin | ||||||
|  | public val Scope.sampleInfo: Sample<G1, G2> | ||||||
|  |   get() = get(named("sampleInfo")) | ||||||
|  |  | ||||||
|  | public val Koin.sampleInfo: Sample<G1, G2> | ||||||
|  |   get() = get(named("sampleInfo")) | ||||||
|  |  | ||||||
|  | public fun Module.sampleInfoSingle(createdAtStart: Boolean = false, | ||||||
|  |     definition: Definition<Sample<G1, G2>>): KoinDefinition<Sample<G1, G2>> = | ||||||
|  |     single(named("sampleInfo"), createdAtStart = createdAtStart, definition = definition) | ||||||
|  |  | ||||||
|  | public fun Module.sampleInfoFactory(definition: Definition<Sample<G1, G2>>): | ||||||
|  |     KoinDefinition<Sample<G1, G2>> = factory(named("sampleInfo"), definition = definition) | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | In case you wish not to generate single: | ||||||
|  |  | ||||||
|  | ```kotlin | ||||||
|  | @file:GenerateKoinDefinition("sampleInfo", Sample::class, G1::class, G2::class, nullable = false, generateSingle = false) | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | And you will take next code: | ||||||
|  |  | ||||||
|  | ```kotlin | ||||||
|  | public val Scope.sampleInfo: Sample<G1, G2> | ||||||
|  |   get() = get(named("sampleInfo")) | ||||||
|  |  | ||||||
|  | public val Koin.sampleInfo: Sample<G1, G2> | ||||||
|  |   get() = get(named("sampleInfo")) | ||||||
|  |  | ||||||
|  | public fun Module.sampleInfoFactory(definition: Definition<Sample<G1, G2>>): | ||||||
|  |     KoinDefinition<Sample<G1, G2>> = factory(named("sampleInfo"), definition = definition) | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | ## 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.koin.generator:$microutils_version`: | ||||||
|  |    ```groovy | ||||||
|  |     dependencies { | ||||||
|  |         add("kspCommonMainMetadata", "dev.inmo:micro_utils.koin.generator:$microutils_version") // will work in commonMain of your multiplatform module | ||||||
|  |         add("kspJvm", "dev.inmo:micro_utils.koin.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 | ||||||
|  |     } | ||||||
|  |     ``` | ||||||
							
								
								
									
										15
									
								
								koin/generator/build.gradle
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								koin/generator/build.gradle
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | |||||||
|  | plugins { | ||||||
|  |     id "org.jetbrains.kotlin.jvm" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | apply from: "$publishJvmOnlyPath" | ||||||
|  |  | ||||||
|  | repositories { | ||||||
|  |     mavenCentral() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | dependencies { | ||||||
|  |     api project(":micro_utils.koin") | ||||||
|  |     api libs.kotlin.poet | ||||||
|  |     api libs.ksp | ||||||
|  | } | ||||||
							
								
								
									
										178
									
								
								koin/generator/src/main/kotlin/Processor.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										178
									
								
								koin/generator/src/main/kotlin/Processor.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,178 @@ | |||||||
|  | package dev.inmo.micro_utils.koin.generator | ||||||
|  |  | ||||||
|  | import com.google.devtools.ksp.KSTypeNotPresentException | ||||||
|  | import com.google.devtools.ksp.KSTypesNotPresentException | ||||||
|  | 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.KSAnnotated | ||||||
|  | import com.google.devtools.ksp.symbol.KSFile | ||||||
|  | import com.squareup.kotlinpoet.ClassName | ||||||
|  | import com.squareup.kotlinpoet.FileSpec | ||||||
|  | import com.squareup.kotlinpoet.FunSpec | ||||||
|  | import com.squareup.kotlinpoet.ParameterSpec | ||||||
|  | import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy | ||||||
|  | import com.squareup.kotlinpoet.PropertySpec | ||||||
|  | import com.squareup.kotlinpoet.asTypeName | ||||||
|  | import com.squareup.kotlinpoet.ksp.toClassName | ||||||
|  | import com.squareup.kotlinpoet.ksp.toTypeName | ||||||
|  | import com.squareup.kotlinpoet.ksp.writeTo | ||||||
|  | import dev.inmo.micro_utils.koin.annotations.GenerateKoinDefinition | ||||||
|  | import org.koin.core.Koin | ||||||
|  | import org.koin.core.module.Module | ||||||
|  | import org.koin.core.scope.Scope | ||||||
|  | import java.io.File | ||||||
|  | import kotlin.reflect.KClass | ||||||
|  |  | ||||||
|  | class Processor( | ||||||
|  |     private val codeGenerator: CodeGenerator | ||||||
|  | ) : SymbolProcessor { | ||||||
|  |     private val definitionClassName = ClassName("org.koin.core.definition", "Definition") | ||||||
|  |     private val koinDefinitionClassName = ClassName("org.koin.core.definition", "KoinDefinition") | ||||||
|  |  | ||||||
|  |     @OptIn(KspExperimental::class) | ||||||
|  |     override fun process(resolver: Resolver): List<KSAnnotated> { | ||||||
|  |         resolver.getSymbolsWithAnnotation( | ||||||
|  |             GenerateKoinDefinition::class.qualifiedName!! | ||||||
|  |         ).filterIsInstance<KSFile>().forEach { ksFile -> | ||||||
|  |             FileSpec.builder( | ||||||
|  |                 ksFile.packageName.asString(), | ||||||
|  |                 "GeneratedDefinitions${ksFile.fileName.removeSuffix(".kt")}" | ||||||
|  |             ).apply { | ||||||
|  |                 addFileComment( | ||||||
|  |                     """ | ||||||
|  |                         THIS CODE HAVE BEEN GENERATED AUTOMATICALLY | ||||||
|  |                         TO REGENERATE IT JUST DELETE FILE | ||||||
|  |                         ORIGINAL FILE: ${ksFile.fileName} | ||||||
|  |                     """.trimIndent() | ||||||
|  |                 ) | ||||||
|  |                 ksFile.getAnnotationsByType(GenerateKoinDefinition::class).forEach { | ||||||
|  |                     val type = runCatching { | ||||||
|  |                         it.type.asTypeName() | ||||||
|  |                     }.getOrElse { e -> | ||||||
|  |                         if (e is KSTypeNotPresentException) { | ||||||
|  |                             e.ksType.toClassName() | ||||||
|  |                         } else { | ||||||
|  |                             throw e | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                     val targetType = runCatching { | ||||||
|  |                         type.parameterizedBy(*(it.typeArgs.takeIf { it.isNotEmpty() } ?.map { it.asTypeName() } ?.toTypedArray() ?: return@runCatching type)) | ||||||
|  |                     }.getOrElse { e -> | ||||||
|  |                         when (e) { | ||||||
|  |                             is KSTypeNotPresentException -> e.ksType.toClassName() | ||||||
|  |                         } | ||||||
|  |                         if (e is KSTypesNotPresentException) { | ||||||
|  |                             type.parameterizedBy(*e.ksTypes.map { it.toTypeName() }.toTypedArray()) | ||||||
|  |                         } else { | ||||||
|  |                             throw e | ||||||
|  |                         } | ||||||
|  |                     }.copy( | ||||||
|  |                         nullable = it.nullable | ||||||
|  |                     ) | ||||||
|  |                     fun addGetterProperty( | ||||||
|  |                         receiver: KClass<*> | ||||||
|  |                     ) { | ||||||
|  |                         addProperty( | ||||||
|  |                             PropertySpec.builder( | ||||||
|  |                                 it.name, | ||||||
|  |                                 targetType, | ||||||
|  |                             ).apply { | ||||||
|  |                                 addKdoc( | ||||||
|  |                                     """ | ||||||
|  |                                         @return Definition by key "${it.name}" | ||||||
|  |                                     """.trimIndent() | ||||||
|  |                                 ) | ||||||
|  |                                 getter( | ||||||
|  |                                     FunSpec.getterBuilder().apply { | ||||||
|  |                                         addCode( | ||||||
|  |                                             "return " + (if (it.nullable) { | ||||||
|  |                                                 "getOrNull" | ||||||
|  |                                             } else { | ||||||
|  |                                                 "get" | ||||||
|  |                                             }) + "(named(\"${it.name}\"))" | ||||||
|  |                                         ) | ||||||
|  |                                     }.build() | ||||||
|  |                                 ) | ||||||
|  |                                 receiver(receiver) | ||||||
|  |                             }.build() | ||||||
|  |                         ) | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                     addGetterProperty(Scope::class) | ||||||
|  |                     addGetterProperty(Koin::class) | ||||||
|  |  | ||||||
|  |                     if (it.generateSingle) { | ||||||
|  |                         addFunction( | ||||||
|  |                             FunSpec.builder("${it.name}Single").apply { | ||||||
|  |                                 addKdoc( | ||||||
|  |                                     """ | ||||||
|  |                                         Will register [definition] with [org.koin.core.module.Module.single] and key "${it.name}" | ||||||
|  |                                     """.trimIndent() | ||||||
|  |                                 ) | ||||||
|  |                                 receiver(Module::class) | ||||||
|  |                                 addParameter( | ||||||
|  |                                     ParameterSpec.builder( | ||||||
|  |                                         "createdAtStart", | ||||||
|  |                                         Boolean::class | ||||||
|  |                                     ).apply { | ||||||
|  |                                         defaultValue("false") | ||||||
|  |                                     }.build() | ||||||
|  |                                 ) | ||||||
|  |                                 addParameter( | ||||||
|  |                                     ParameterSpec.builder( | ||||||
|  |                                         "definition", | ||||||
|  |                                         definitionClassName.parameterizedBy(targetType.copy(nullable = false)) | ||||||
|  |                                     ).build() | ||||||
|  |                                 ) | ||||||
|  |                                 returns(koinDefinitionClassName.parameterizedBy(targetType.copy(nullable = false))) | ||||||
|  |                                 addCode( | ||||||
|  |                                     "return single(named(\"${it.name}\"), createdAtStart = createdAtStart, definition = definition)" | ||||||
|  |                                 ) | ||||||
|  |                             }.build() | ||||||
|  |                         ) | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                     if (it.generateFactory) { | ||||||
|  |                         addFunction( | ||||||
|  |                             FunSpec.builder("${it.name}Factory").apply { | ||||||
|  |                                 addKdoc( | ||||||
|  |                                     """ | ||||||
|  |                                         Will register [definition] with [org.koin.core.module.Module.factory] and key "${it.name}" | ||||||
|  |                                     """.trimIndent() | ||||||
|  |                                 ) | ||||||
|  |                                 receiver(Module::class) | ||||||
|  |                                 addParameter( | ||||||
|  |                                     ParameterSpec.builder( | ||||||
|  |                                         "definition", | ||||||
|  |                                         definitionClassName.parameterizedBy(targetType.copy(nullable = false)) | ||||||
|  |                                     ).build() | ||||||
|  |                                 ) | ||||||
|  |                                 returns(koinDefinitionClassName.parameterizedBy(targetType.copy(nullable = false))) | ||||||
|  |                                 addCode( | ||||||
|  |                                     "return factory(named(\"${it.name}\"), definition = definition)" | ||||||
|  |                                 ) | ||||||
|  |                             }.build() | ||||||
|  |                         ) | ||||||
|  |                     } | ||||||
|  |                     addImport("org.koin.core.qualifier", "named") | ||||||
|  |                 } | ||||||
|  |             }.build().let { | ||||||
|  |                 File( | ||||||
|  |                     File(ksFile.filePath).parent, | ||||||
|  |                     "GeneratedDefinitions${ksFile.fileName}" | ||||||
|  |                 ).takeIf { !it.exists() } ?.apply { | ||||||
|  |                     parentFile.mkdirs() | ||||||
|  |  | ||||||
|  |                     writer().use { writer -> | ||||||
|  |                         it.writeTo(writer) | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return emptyList() | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										11
									
								
								koin/generator/src/main/kotlin/Provider.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								koin/generator/src/main/kotlin/Provider.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | |||||||
|  | package dev.inmo.micro_utils.koin.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.koin.generator.Provider | ||||||
							
								
								
									
										27
									
								
								koin/generator/test/build.gradle
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								koin/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: "$mppProjectWithSerializationPresetPath" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | kotlin { | ||||||
|  |     sourceSets { | ||||||
|  |         commonMain { | ||||||
|  |             dependencies { | ||||||
|  |                 api project(":micro_utils.koin") | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | dependencies { | ||||||
|  |     add("kspCommonMainMetadata", project(":micro_utils.koin.generator")) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | ksp { | ||||||
|  | } | ||||||
| @@ -0,0 +1,38 @@ | |||||||
|  | // THIS CODE HAVE BEEN GENERATED AUTOMATICALLY | ||||||
|  | // TO REGENERATE IT JUST DELETE FILE | ||||||
|  | // ORIGINAL FILE: Test.kt | ||||||
|  | package dev.inmo.micro_utils.koin.generator.test | ||||||
|  |  | ||||||
|  | import kotlin.Boolean | ||||||
|  | import kotlin.String | ||||||
|  | import org.koin.core.Koin | ||||||
|  | import org.koin.core.definition.Definition | ||||||
|  | import org.koin.core.definition.KoinDefinition | ||||||
|  | import org.koin.core.module.Module | ||||||
|  | import org.koin.core.qualifier.named | ||||||
|  | import org.koin.core.scope.Scope | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @return Definition by key "sampleInfo" | ||||||
|  |  */ | ||||||
|  | public val Scope.sampleInfo: Test<String> | ||||||
|  |   get() = get(named("sampleInfo")) | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @return Definition by key "sampleInfo" | ||||||
|  |  */ | ||||||
|  | public val Koin.sampleInfo: Test<String> | ||||||
|  |   get() = get(named("sampleInfo")) | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Will register [definition] with [org.koin.core.module.Module.single] and key "sampleInfo" | ||||||
|  |  */ | ||||||
|  | public fun Module.sampleInfoSingle(createdAtStart: Boolean = false, | ||||||
|  |     definition: Definition<Test<String>>): KoinDefinition<Test<String>> = | ||||||
|  |     single(named("sampleInfo"), createdAtStart = createdAtStart, definition = definition) | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Will register [definition] with [org.koin.core.module.Module.factory] and key "sampleInfo" | ||||||
|  |  */ | ||||||
|  | public fun Module.sampleInfoFactory(definition: Definition<Test<String>>): | ||||||
|  |     KoinDefinition<Test<String>> = factory(named("sampleInfo"), definition = definition) | ||||||
							
								
								
									
										13
									
								
								koin/generator/test/src/commonMain/kotlin/Test.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								koin/generator/test/src/commonMain/kotlin/Test.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | |||||||
|  | @file:GenerateKoinDefinition("sampleInfo", Test::class, String::class, nullable = false) | ||||||
|  | package dev.inmo.micro_utils.koin.generator.test | ||||||
|  |  | ||||||
|  | import dev.inmo.micro_utils.koin.annotations.GenerateKoinDefinition | ||||||
|  | import org.koin.core.Koin | ||||||
|  |  | ||||||
|  | class Test<T>( | ||||||
|  |     koin: Koin | ||||||
|  | ) { | ||||||
|  |     init { | ||||||
|  |         koin.sampleInfo | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										1
									
								
								koin/generator/test/src/main/AndroidManifest.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								koin/generator/test/src/main/AndroidManifest.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | <manifest package="dev.inmo.micro_utils.koin.generator.test"/> | ||||||
| @@ -0,0 +1,28 @@ | |||||||
|  | package dev.inmo.micro_utils.koin.annotations | ||||||
|  |  | ||||||
|  | import kotlin.reflect.KClass | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Use this annotation to mark files near to which generator should place generated extensions for koin [org.koin.core.scope.Scope] | ||||||
|  |  * and [org.koin.core.Koin] | ||||||
|  |  * | ||||||
|  |  * @param name Name for definitions. This name will be available as extension for [org.koin.core.scope.Scope] and [org.koin.core.Koin] | ||||||
|  |  * @param type Type of extensions. It is base star-typed class | ||||||
|  |  * @param typeArgs Generic types for [type]. For example, if [type] == `Something::class` and [typeArgs] == `G1::class, | ||||||
|  |  * G2::class`, the result type will be `Something<G1, G2>` | ||||||
|  |  * @param nullable In case when true, extension will not throw error when definition has not been registered in koin | ||||||
|  |  * @param generateSingle Generate definition factory with [org.koin.core.module.Module.single]. You will be able to use | ||||||
|  |  * the extension [org.koin.core.module.Module].[name]Single(createdAtStart/* default false */) { /* your definition */ } | ||||||
|  |  * @param generateFactory Generate definition factory with [org.koin.core.module.Module.factory]. You will be able to use | ||||||
|  |  * the extension [org.koin.core.module.Module].[name]Factory { /* your definition */ } | ||||||
|  |  */ | ||||||
|  | @Target(AnnotationTarget.FILE) | ||||||
|  | @Repeatable | ||||||
|  | annotation class GenerateKoinDefinition( | ||||||
|  |     val name: String, | ||||||
|  |     val type: KClass<*>, | ||||||
|  |     vararg val typeArgs: KClass<*>, | ||||||
|  |     val nullable: Boolean = true, | ||||||
|  |     val generateSingle: Boolean = true, | ||||||
|  |     val generateFactory: Boolean = true | ||||||
|  | ) | ||||||
| @@ -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 | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -7,15 +7,9 @@ interface CommonExposedRepo<IdType, ObjectType> : ExposedRepo { | |||||||
|     val ResultRow.asId: IdType |     val ResultRow.asId: IdType | ||||||
|     val selectById: ISqlExpressionBuilder.(IdType) -> Op<Boolean> |     val selectById: ISqlExpressionBuilder.(IdType) -> Op<Boolean> | ||||||
|     val selectByIds: ISqlExpressionBuilder.(List<IdType>) -> Op<Boolean> |     val selectByIds: ISqlExpressionBuilder.(List<IdType>) -> Op<Boolean> | ||||||
|         get() = { list -> |         get() = { | ||||||
|             if (list.isEmpty()) { |             it.foldRight<IdType, Op<Boolean>?>(null) { id, acc -> | ||||||
|                 Op.FALSE |                 acc ?.or(selectById(id)) ?: selectById(id) | ||||||
|             } else { |             } ?: Op.FALSE | ||||||
|                 var op = selectById(list.first()) |  | ||||||
|                 (1 until list.size).forEach { |  | ||||||
|                     op = op.or(selectById(list[it])) |  | ||||||
|                 } |  | ||||||
|                 op |  | ||||||
|             } |  | ||||||
|         } |         } | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										154
									
								
								repos/generator/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										154
									
								
								repos/generator/README.md
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||||
|  |     } | ||||||
|  |     ``` | ||||||
							
								
								
									
										16
									
								
								repos/generator/build.gradle
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								repos/generator/build.gradle
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||||
|  | } | ||||||
							
								
								
									
										217
									
								
								repos/generator/src/main/kotlin/Processor.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										217
									
								
								repos/generator/src/main/kotlin/Processor.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,217 @@ | |||||||
|  | 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.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 | ||||||
|  | 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.TypeName | ||||||
|  | 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 | ||||||
|  |  | ||||||
|  | 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<KSAnnotated> { | ||||||
|  |         val toRetry = resolver.getSymbolsWithAnnotation( | ||||||
|  |             GenerateCRUDModel::class.qualifiedName!! | ||||||
|  |         ).filterIsInstance<KSClassDeclaration>().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 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.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<KSType>).map { it.declaration as KSClassDeclaration } | ||||||
|  |                     }.toList() | ||||||
|  |  | ||||||
|  |  | ||||||
|  |                     val registeredTypesProperties: List<KSPropertyDeclaration> = registeredSupertypes.flatMap { registeredType -> | ||||||
|  |                         registeredType.getAllProperties() | ||||||
|  |                     }.filter { | ||||||
|  |                         it.simpleName.asString() !in excludedKSClassPropertiesNames && it.getAnnotationsByType(GenerateCRUDModelExcludeOverride::class).none() | ||||||
|  |                     } | ||||||
|  |                     val allProperties: List<KSPropertyDeclaration> = 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() | ||||||
|  |                     ) | ||||||
|  |  | ||||||
|  |                     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().let { | ||||||
|  |                     File( | ||||||
|  |                         File(ksFile.filePath).parent, | ||||||
|  |                         "GeneratedModels${ksFile.fileName}" | ||||||
|  |                     ).takeIf { !it.exists() } ?.apply { | ||||||
|  |                         parentFile.mkdirs() | ||||||
|  |  | ||||||
|  |                         writer().use { writer -> | ||||||
|  |                             it.writeTo(writer) | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             }.isSuccess | ||||||
|  |         }.toList() | ||||||
|  |  | ||||||
|  |         return toRetry | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										11
									
								
								repos/generator/src/main/kotlin/Provider.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								repos/generator/src/main/kotlin/Provider.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||||
|  |     ) | ||||||
|  | } | ||||||
| @@ -0,0 +1 @@ | |||||||
|  | dev.inmo.micro_utils.repos.generator.Provider | ||||||
							
								
								
									
										27
									
								
								repos/generator/test/build.gradle
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								repos/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: "$mppProjectWithSerializationPresetPath" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | kotlin { | ||||||
|  |     sourceSets { | ||||||
|  |         commonMain { | ||||||
|  |             dependencies { | ||||||
|  |                 api project(":micro_utils.repos.common") | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | dependencies { | ||||||
|  |     add("kspCommonMainMetadata", project(":micro_utils.repos.generator")) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | ksp { | ||||||
|  | } | ||||||
| @@ -0,0 +1,31 @@ | |||||||
|  | // 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, | ||||||
|  |   public override val parent: ParentTypeId?, | ||||||
|  | ) : Test | ||||||
|  |  | ||||||
|  | @Serializable | ||||||
|  | @SerialName(value = "RegisteredTest") | ||||||
|  | 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, parent) | ||||||
|  |  | ||||||
|  | public fun Test.asRegistered(id: TestId): RegisteredTest = RegisteredTest(id, property1, property2, | ||||||
|  |     parent) | ||||||
							
								
								
									
										33
									
								
								repos/generator/test/src/commonMain/kotlin/Test.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								repos/generator/test/src/commonMain/kotlin/Test.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | |||||||
|  | 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 | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | typealias ParentTypeId = TestId | ||||||
|  |  | ||||||
|  | @GenerateCRUDModel(IRegisteredTest::class) | ||||||
|  | sealed interface Test { | ||||||
|  |     val property1: String | ||||||
|  |     val property2: Int | ||||||
|  |     val parent: ParentTypeId? | ||||||
|  |  | ||||||
|  |     @GenerateCRUDModelExcludeOverride | ||||||
|  |     val excludedProperty: String | ||||||
|  |         get() = "excluded" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | sealed interface IRegisteredTest : Test { | ||||||
|  |     val id: TestId | ||||||
|  |  | ||||||
|  |     @GenerateCRUDModelExcludeOverride | ||||||
|  |     val excludedProperty2: Boolean | ||||||
|  |         get() = false | ||||||
|  | } | ||||||
							
								
								
									
										1
									
								
								repos/generator/test/src/main/AndroidManifest.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								repos/generator/test/src/main/AndroidManifest.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | <manifest package="dev.inmo.micro_utils.repos.generator.test"/> | ||||||
| @@ -7,6 +7,8 @@ String[] includes = [ | |||||||
|     ":safe_wrapper", |     ":safe_wrapper", | ||||||
|     ":crypto", |     ":crypto", | ||||||
|     ":koin", |     ":koin", | ||||||
|  |     ":koin:generator", | ||||||
|  |     ":koin:generator:test", | ||||||
|     ":selector:common", |     ":selector:common", | ||||||
|     ":pagination:common", |     ":pagination:common", | ||||||
|     ":pagination:exposed", |     ":pagination:exposed", | ||||||
| @@ -16,6 +18,8 @@ String[] includes = [ | |||||||
|     ":language_codes", |     ":language_codes", | ||||||
|     ":language_codes:generator", |     ":language_codes:generator", | ||||||
|     ":repos:common", |     ":repos:common", | ||||||
|  |     ":repos:generator", | ||||||
|  |     ":repos:generator:test", | ||||||
|     ":repos:cache", |     ":repos:cache", | ||||||
|     ":repos:exposed", |     ":repos:exposed", | ||||||
|     ":repos:inmemory", |     ":repos:inmemory", | ||||||
|   | |||||||
| @@ -39,14 +39,18 @@ object StartLauncherPlugin : StartPlugin { | |||||||
|  |  | ||||||
|         includes( |         includes( | ||||||
|             config.plugins.mapNotNull { |             config.plugins.mapNotNull { | ||||||
|  |                 val pluginName = it::class.simpleName ?: it.toString() | ||||||
|                 runCatching { |                 runCatching { | ||||||
|  |                     logger.i { "Start koin module registration for $pluginName" } | ||||||
|                     module { |                     module { | ||||||
|                         with(it) { |                         with(it) { | ||||||
|                             setupDI(rawJsonObject) |                             setupDI(rawJsonObject) | ||||||
|                         } |                         } | ||||||
|                     } |                     } | ||||||
|                 }.onFailure { e -> |                 }.onFailure { e -> | ||||||
|                     logger.w("Unable to load DI part of $it", e) |                     logger.w("Unable to register koin module of $pluginName", e) | ||||||
|  |                 }.onSuccess { | ||||||
|  |                     logger.i("Successfully registered koin module of $pluginName") | ||||||
|                 }.getOrNull() |                 }.getOrNull() | ||||||
|             } |             } | ||||||
|         ) |         ) | ||||||
| @@ -76,16 +80,17 @@ object StartLauncherPlugin : StartPlugin { | |||||||
|         logger.i("Start starting of subplugins") |         logger.i("Start starting of subplugins") | ||||||
|         val scope = koin.get<CoroutineScope>() |         val scope = koin.get<CoroutineScope>() | ||||||
|         koin.get<Config>().plugins.map { plugin -> |         koin.get<Config>().plugins.map { plugin -> | ||||||
|  |             val pluginName = plugin::class.simpleName ?: plugin.toString() | ||||||
|             scope.launch { |             scope.launch { | ||||||
|                 runCatchingSafely { |                 runCatchingSafely { | ||||||
|                     logger.i("Start loading of $plugin") |                     logger.i("Start loading of $pluginName") | ||||||
|                     with(plugin) { |                     with(plugin) { | ||||||
|                         startPlugin(koin) |                         startPlugin(koin) | ||||||
|                     } |                     } | ||||||
|                 }.onFailure { e -> |                 }.onFailure { e -> | ||||||
|                     logger.w("Unable to start plugin $plugin", e) |                     logger.w("Unable to start plugin $pluginName", e) | ||||||
|                 }.onSuccess { |                 }.onSuccess { | ||||||
|                     logger.i("Complete loading of $plugin") |                     logger.i("Complete loading of $pluginName") | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         }.joinAll() |         }.joinAll() | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user