mirror of
				https://github.com/InsanusMokrassar/MicroUtils.git
				synced 2025-10-29 19:20:26 +00:00 
			
		
		
		
	Compare commits
	
		
			37 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 2c7fd320eb | |||
| 88ee82e1c6 | |||
| d6402c624e | |||
| 8b9c93bc10 | |||
| 4f5e261d01 | |||
| cf455aebe6 | |||
| 1380d5f8e1 | |||
| 5f65260bfe | |||
| 11f0dcfc01 | |||
| 555b7886a4 | |||
| 3707a6c0ce | |||
| 4c8d92b4c3 | |||
| 8bbd33c896 | |||
| ac33a3580f | |||
| a64a32fbe6 | |||
| 9493e97a38 | |||
| 88bd770260 | |||
| a7bd33b7bf | |||
| 73c724a2e5 | |||
| d8cf3c6726 | |||
| 15dee238b5 | |||
| c70626734e | |||
| 5a765ea1bc | |||
| 8215f9d2c6 | |||
| d2e6d2ec80 | |||
| 3718181e8f | |||
| 0d825cf424 | |||
| 28a804d988 | |||
| 9e4bb9d678 | |||
| 9c40d7da3d | |||
| 2b76ad0aa9 | |||
| e4b619e050 | |||
| 36c09feaf2 | |||
| 2d68321503 | |||
| 85455ab21c | |||
| 18d63eb980 | |||
| 2e429e9704 | 
							
								
								
									
										47
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										47
									
								
								CHANGELOG.md
									
									
									
									
									
								
							| @@ -1,5 +1,52 @@ | |||||||
| # Changelog | # Changelog | ||||||
|  |  | ||||||
|  | ## 0.17.3 | ||||||
|  |  | ||||||
|  | * `Common`: | ||||||
|  |     * Add `fixed` extensions for `Float` and `Double` | ||||||
|  |     * New function `emptyDiff` | ||||||
|  |     * Now you may pass custom `comparisonFun` to all `diff` functions | ||||||
|  |  | ||||||
|  | ## 0.17.2 | ||||||
|  |  | ||||||
|  | * `FSM`: | ||||||
|  |     * `DefaultStatesManager.onUpdateContextsConflictResolver` and `DefaultStatesManager.onStartContextsConflictResolver` now return `false` by default | ||||||
|  |  | ||||||
|  | ## 0.17.1 | ||||||
|  |  | ||||||
|  | * **Hotfix** for absence of jvm dependencies in android modules | ||||||
|  | * `Versions`: | ||||||
|  |     * `Ktor`: `2.2.3` -> `2.2.4` | ||||||
|  |  | ||||||
|  | ## 0.17.0 | ||||||
|  |  | ||||||
|  | * `Versions`: | ||||||
|  |     * `Kotlin`: `1.7.20` -> `1.8.10` | ||||||
|  |     * `Serialization`: `1.4.1` -> `1.5.0` | ||||||
|  |     * `KSLog`: `0.5.4` -> `1.0.0` | ||||||
|  |     * `AppCompat`: `1.6.0` -> `1.6.1` | ||||||
|  |  | ||||||
|  | ## 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 | ||||||
|  |  | ||||||
|  | * `LanguageCodes`: | ||||||
|  |     * In android and JVM targets now available `toJavaLocale` and from Java `Locale` conversations from/to | ||||||
|  |       `IetfLanguageCode` | ||||||
|  |  | ||||||
| ## 0.16.10 | ## 0.16.10 | ||||||
|  |  | ||||||
| * `Repos`: | * `Repos`: | ||||||
|   | |||||||
| @@ -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 | ||||||
|   | |||||||
| @@ -18,6 +18,7 @@ kotlin { | |||||||
|                 api project(":micro_utils.coroutines") |                 api project(":micro_utils.coroutines") | ||||||
|                 api libs.android.fragment |                 api libs.android.fragment | ||||||
|             } |             } | ||||||
|  |             dependsOn jvmMain | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -34,7 +34,11 @@ data class Diff<T> internal constructor( | |||||||
|      */ |      */ | ||||||
|     val replaced: List<Pair<@Serializable(IndexedValueSerializer::class) IndexedValue<T>, @Serializable(IndexedValueSerializer::class) IndexedValue<T>>>, |     val replaced: List<Pair<@Serializable(IndexedValueSerializer::class) IndexedValue<T>, @Serializable(IndexedValueSerializer::class) IndexedValue<T>>>, | ||||||
|     val added: List<@Serializable(IndexedValueSerializer::class) IndexedValue<T>> |     val added: List<@Serializable(IndexedValueSerializer::class) IndexedValue<T>> | ||||||
| ) | ) { | ||||||
|  |     fun isEmpty(): Boolean = removed.isEmpty() && replaced.isEmpty() && added.isEmpty() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | fun <T> emptyDiff(): Diff<T> = Diff(emptyList(), emptyList(), emptyList()) | ||||||
|  |  | ||||||
| private inline fun <T> performChanges( | private inline fun <T> performChanges( | ||||||
|     potentialChanges: MutableList<Pair<IndexedValue<T>?, IndexedValue<T>?>>, |     potentialChanges: MutableList<Pair<IndexedValue<T>?, IndexedValue<T>?>>, | ||||||
| @@ -43,14 +47,14 @@ private inline fun <T> performChanges( | |||||||
|     changedList: MutableList<Pair<IndexedValue<T>, IndexedValue<T>>>, |     changedList: MutableList<Pair<IndexedValue<T>, IndexedValue<T>>>, | ||||||
|     removedList: MutableList<IndexedValue<T>>, |     removedList: MutableList<IndexedValue<T>>, | ||||||
|     addedList: MutableList<IndexedValue<T>>, |     addedList: MutableList<IndexedValue<T>>, | ||||||
|     strictComparison: Boolean |     comparisonFun: (T?, T?) -> Boolean | ||||||
| ) { | ) { | ||||||
|     var i = -1 |     var i = -1 | ||||||
|     val (oldObject, newObject) = potentialChanges.lastOrNull() ?: return |     val (oldObject, newObject) = potentialChanges.lastOrNull() ?: return | ||||||
|     for ((old, new) in potentialChanges.take(potentialChanges.size - 1)) { |     for ((old, new) in potentialChanges.take(potentialChanges.size - 1)) { | ||||||
|         i++ |         i++ | ||||||
|         val oldOneEqualToNewObject = old ?.value === newObject ?.value || (old ?.value == newObject ?.value && !strictComparison) |         val oldOneEqualToNewObject = comparisonFun(old ?.value, newObject ?.value) | ||||||
|         val newOneEqualToOldObject = new ?.value === oldObject ?.value || (new ?.value == oldObject ?.value && !strictComparison) |         val newOneEqualToOldObject = comparisonFun(new ?.value, oldObject ?.value) | ||||||
|         if (oldOneEqualToNewObject || newOneEqualToOldObject) { |         if (oldOneEqualToNewObject || newOneEqualToOldObject) { | ||||||
|             changedList.addAll( |             changedList.addAll( | ||||||
|                 potentialChanges.take(i).mapNotNull { |                 potentialChanges.take(i).mapNotNull { | ||||||
| @@ -104,7 +108,7 @@ private inline fun <T> performChanges( | |||||||
|  */ |  */ | ||||||
| fun <T> Iterable<T>.calculateDiff( | fun <T> Iterable<T>.calculateDiff( | ||||||
|     other: Iterable<T>, |     other: Iterable<T>, | ||||||
|     strictComparison: Boolean = false |     comparisonFun: (T?, T?) -> Boolean | ||||||
| ): Diff<T> { | ): Diff<T> { | ||||||
|     var i = -1 |     var i = -1 | ||||||
|     var j = -1 |     var j = -1 | ||||||
| @@ -132,7 +136,7 @@ fun <T> Iterable<T>.calculateDiff( | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         when { |         when { | ||||||
|             oldObject === newObject || (oldObject == newObject && !strictComparison) -> { |             comparisonFun(oldObject, newObject) -> { | ||||||
|                 changedObjects.addAll(potentiallyChangedObjects.map { |                 changedObjects.addAll(potentiallyChangedObjects.map { | ||||||
|                     @Suppress("UNCHECKED_CAST") |                     @Suppress("UNCHECKED_CAST") | ||||||
|                     it as Pair<IndexedValue<T>, IndexedValue<T>> |                     it as Pair<IndexedValue<T>, IndexedValue<T>> | ||||||
| @@ -143,23 +147,49 @@ fun <T> Iterable<T>.calculateDiff( | |||||||
|                 potentiallyChangedObjects.add(oldObject ?.let { IndexedValue(i, oldObject) } to newObject ?.let { IndexedValue(j, newObject) }) |                 potentiallyChangedObjects.add(oldObject ?.let { IndexedValue(i, oldObject) } to newObject ?.let { IndexedValue(j, newObject) }) | ||||||
|                 val previousOldsAdditionsSize = additionalInOld.size |                 val previousOldsAdditionsSize = additionalInOld.size | ||||||
|                 val previousNewsAdditionsSize = additionalInNew.size |                 val previousNewsAdditionsSize = additionalInNew.size | ||||||
|                 performChanges(potentiallyChangedObjects, additionalInOld, additionalInNew, changedObjects, removedObjects, addedObjects, strictComparison) |                 performChanges(potentiallyChangedObjects, additionalInOld, additionalInNew, changedObjects, removedObjects, addedObjects, comparisonFun) | ||||||
|                 i -= (additionalInOld.size - previousOldsAdditionsSize) |                 i -= (additionalInOld.size - previousOldsAdditionsSize) | ||||||
|                 j -= (additionalInNew.size - previousNewsAdditionsSize) |                 j -= (additionalInNew.size - previousNewsAdditionsSize) | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     potentiallyChangedObjects.add(null to null) |     potentiallyChangedObjects.add(null to null) | ||||||
|     performChanges(potentiallyChangedObjects, additionalInOld, additionalInNew, changedObjects, removedObjects, addedObjects, strictComparison) |     performChanges(potentiallyChangedObjects, additionalInOld, additionalInNew, changedObjects, removedObjects, addedObjects, comparisonFun) | ||||||
|  |  | ||||||
|     return Diff(removedObjects.toList(), changedObjects.toList(), addedObjects.toList()) |     return Diff(removedObjects.toList(), changedObjects.toList(), addedObjects.toList()) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Calculating [Diff] object | ||||||
|  |  * | ||||||
|  |  * @param strictComparison If this parameter set to true, objects which are not equal by links will be used as different | ||||||
|  |  * objects. For example, in case of two "Example" string they will be equal by value, but CAN be different by links | ||||||
|  |  */ | ||||||
|  | fun <T> Iterable<T>.calculateDiff( | ||||||
|  |     other: Iterable<T>, | ||||||
|  |     strictComparison: Boolean = false | ||||||
|  | ): Diff<T> = calculateDiff( | ||||||
|  |     other, | ||||||
|  |     comparisonFun = if (strictComparison) { | ||||||
|  |         { t1, t2 -> | ||||||
|  |             t1 === t2 | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         { t1, t2 -> | ||||||
|  |             t1 === t2 || t1 == t2 // small optimization for cases when t1 and t2 are the same - comparison will be faster potentially | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | ) | ||||||
| inline fun <T> Iterable<T>.diff( | inline fun <T> Iterable<T>.diff( | ||||||
|     other: Iterable<T>, |     other: Iterable<T>, | ||||||
|     strictComparison: Boolean = false |     strictComparison: Boolean = false | ||||||
| ): Diff<T> = calculateDiff(other, strictComparison) | ): Diff<T> = calculateDiff(other, strictComparison) | ||||||
|  | inline fun <T> Iterable<T>.diff( | ||||||
|  |     other: Iterable<T>, | ||||||
|  |     noinline comparisonFun: (T?, T?) -> Boolean | ||||||
|  | ): Diff<T> = calculateDiff(other, comparisonFun) | ||||||
|  |  | ||||||
| inline fun <T> Diff(old: Iterable<T>, new: Iterable<T>) = old.calculateDiff(new) | inline fun <T> Diff(old: Iterable<T>, new: Iterable<T>) = old.calculateDiff(new, strictComparison = false) | ||||||
| inline fun <T> StrictDiff(old: Iterable<T>, new: Iterable<T>) = old.calculateDiff(new, true) | inline fun <T> StrictDiff(old: Iterable<T>, new: Iterable<T>) = old.calculateDiff(new, true) | ||||||
|  |  | ||||||
| /** | /** | ||||||
| @@ -187,3 +217,22 @@ fun <T> MutableList<T>.applyDiff( | |||||||
|         set(new.index, new.value) |         set(new.index, new.value) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * This method call [calculateDiff] with strict mode [strictComparison] and then apply differences to [this] | ||||||
|  |  * mutable list | ||||||
|  |  */ | ||||||
|  | fun <T> MutableList<T>.applyDiff( | ||||||
|  |     source: Iterable<T>, | ||||||
|  |     comparisonFun: (T?, T?) -> Boolean | ||||||
|  | ): Diff<T> = calculateDiff(source, comparisonFun).also { | ||||||
|  |     for (i in it.removed.indices.sortedDescending()) { | ||||||
|  |         removeAt(it.removed[i].index) | ||||||
|  |     } | ||||||
|  |     it.added.forEach { (i, t) -> | ||||||
|  |         add(i, t) | ||||||
|  |     } | ||||||
|  |     it.replaced.forEach { (_, new) -> | ||||||
|  |         set(new.index, new.value) | ||||||
|  |     } | ||||||
|  | } | ||||||
|   | |||||||
| @@ -0,0 +1,6 @@ | |||||||
|  | package dev.inmo.micro_utils.common | ||||||
|  |  | ||||||
|  | val FixedSignsRange = 0 .. 100 | ||||||
|  |  | ||||||
|  | expect fun Float.fixed(signs: Int): Float | ||||||
|  | expect fun Double.fixed(signs: Int): Double | ||||||
| @@ -0,0 +1,4 @@ | |||||||
|  | package dev.inmo.micro_utils.common | ||||||
|  |  | ||||||
|  | actual fun Float.fixed(signs: Int): Float = this.asDynamic().toFixed(signs.coerceIn(FixedSignsRange)).unsafeCast<String>().toFloat() | ||||||
|  | actual fun Double.fixed(signs: Int): Double = this.asDynamic().toFixed(signs.coerceIn(FixedSignsRange)).unsafeCast<String>().toDouble() | ||||||
| @@ -0,0 +1,12 @@ | |||||||
|  | package dev.inmo.micro_utils.common | ||||||
|  |  | ||||||
|  | import java.math.BigDecimal | ||||||
|  | import java.math.RoundingMode | ||||||
|  |  | ||||||
|  | actual fun Float.fixed(signs: Int): Float = BigDecimal.valueOf(this.toDouble()) | ||||||
|  |     .setScale(signs.coerceIn(FixedSignsRange), RoundingMode.HALF_UP) | ||||||
|  |     .toFloat(); | ||||||
|  |  | ||||||
|  | actual fun Double.fixed(signs: Int): Double = BigDecimal.valueOf(this) | ||||||
|  |     .setScale(signs.coerceIn(FixedSignsRange), RoundingMode.HALF_UP) | ||||||
|  |     .toDouble(); | ||||||
| @@ -1,30 +1,5 @@ | |||||||
| apply plugin: 'com.getkeepsafe.dexcount' | apply plugin: 'com.getkeepsafe.dexcount' | ||||||
|  |  | ||||||
| ext { |  | ||||||
|     jvmKotlinFolderFile = { |  | ||||||
|         String sep = File.separator |  | ||||||
|         return new File("${project.projectDir}${sep}src${sep}jvmMain${sep}kotlin") |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     enableIncludingJvmCodeInAndroidPart = { |  | ||||||
|         File jvmKotlinFolder = jvmKotlinFolderFile() |  | ||||||
|         if (jvmKotlinFolder.exists()) { |  | ||||||
|             android.sourceSets.main.java.srcDirs += jvmKotlinFolder.path |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     disableIncludingJvmCodeInAndroidPart = { |  | ||||||
|         File jvmKotlinFolder = jvmKotlinFolderFile() |  | ||||||
|         String[] oldDirs = android.sourceSets.main.java.srcDirs |  | ||||||
|         android.sourceSets.main.java.srcDirs = [] |  | ||||||
|         for (oldDir in oldDirs) { |  | ||||||
|             if (oldDir != jvmKotlinFolder.path) { |  | ||||||
|                 android.sourceSets.main.java.srcDirs += oldDir |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| android { | android { | ||||||
|     compileSdkVersion libs.versions.android.props.compileSdk.get().toInteger() |     compileSdkVersion libs.versions.android.props.compileSdk.get().toInteger() | ||||||
|     buildToolsVersion libs.versions.android.props.buildTools.get() |     buildToolsVersion libs.versions.android.props.buildTools.get() | ||||||
| @@ -58,10 +33,4 @@ android { | |||||||
|     kotlinOptions { |     kotlinOptions { | ||||||
|         jvmTarget = JavaVersion.VERSION_1_8.toString() |         jvmTarget = JavaVersion.VERSION_1_8.toString() | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     sourceSets { |  | ||||||
|         String sep = File.separator |  | ||||||
|         main.java.srcDirs += "src${sep}main${sep}kotlin" |  | ||||||
|         enableIncludingJvmCodeInAndroidPart() |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -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" | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -48,8 +48,8 @@ interface DefaultStatesManagerRepo<T : State> { | |||||||
|  */ |  */ | ||||||
| open class DefaultStatesManager<T : State>( | open class DefaultStatesManager<T : State>( | ||||||
|     protected val repo: DefaultStatesManagerRepo<T> = InMemoryDefaultStatesManagerRepo(), |     protected val repo: DefaultStatesManagerRepo<T> = InMemoryDefaultStatesManagerRepo(), | ||||||
|     protected val onStartContextsConflictResolver: suspend (current: T, new: T) -> Boolean = { _, _ -> true }, |     protected val onStartContextsConflictResolver: suspend (current: T, new: T) -> Boolean = { _, _ -> false }, | ||||||
|     protected val onUpdateContextsConflictResolver: suspend (old: T, new: T, currentNew: T) -> Boolean = { _, _, _ -> true } |     protected val onUpdateContextsConflictResolver: suspend (old: T, new: T, currentNew: T) -> Boolean = { _, _, _ -> false } | ||||||
| ) : StatesManager<T> { | ) : StatesManager<T> { | ||||||
|     protected val _onChainStateUpdated = MutableSharedFlow<Pair<T, T>>(0) |     protected val _onChainStateUpdated = MutableSharedFlow<Pair<T, T>>(0) | ||||||
|     override val onChainStateUpdated: Flow<Pair<T, T>> = _onChainStateUpdated.asSharedFlow() |     override val onChainStateUpdated: Flow<Pair<T, T>> = _onChainStateUpdated.asSharedFlow() | ||||||
|   | |||||||
| @@ -14,5 +14,5 @@ crypto_js_version=4.1.1 | |||||||
| # Project data | # Project data | ||||||
|  |  | ||||||
| group=dev.inmo | group=dev.inmo | ||||||
| version=0.16.10 | version=0.17.3 | ||||||
| android_code_version=178 | android_code_version=185 | ||||||
|   | |||||||
| @@ -1,33 +1,36 @@ | |||||||
| [versions] | [versions] | ||||||
|  |  | ||||||
| kt = "1.7.20" | kt = "1.8.10" | ||||||
| kt-serialization = "1.4.1" | kt-serialization = "1.5.0" | ||||||
| kt-coroutines = "1.6.4" | kt-coroutines = "1.6.4" | ||||||
|  |  | ||||||
| kslog = "0.5.4" | kslog = "1.0.0" | ||||||
|  |  | ||||||
| jb-compose = "1.2.2" | jb-compose = "1.3.1-rc01" | ||||||
| jb-exposed = "0.41.1" | jb-exposed = "0.41.1" | ||||||
| jb-dokka = "1.7.20" | jb-dokka = "1.7.20" | ||||||
|  |  | ||||||
| klock = "3.4.0" | klock = "3.4.0" | ||||||
| uuid = "0.6.0" | uuid = "0.7.0" | ||||||
|  |  | ||||||
| ktor = "2.2.3" | ktor = "2.2.4" | ||||||
|  |  | ||||||
| gh-release = "2.4.1" | gh-release = "2.4.1" | ||||||
|  |  | ||||||
| koin = "3.3.2" | koin = "3.3.2" | ||||||
|  |  | ||||||
| android-gradle = "7.3.0" | ksp = "1.8.10-1.0.9" | ||||||
| dexcount = "3.1.0" | kotlin-poet = "1.12.0" | ||||||
|  |  | ||||||
|  | android-gradle = "7.3.1" | ||||||
|  | dexcount = "4.0.0" | ||||||
|  |  | ||||||
| android-coreKtx = "1.9.0" | android-coreKtx = "1.9.0" | ||||||
| android-recyclerView = "1.2.1" | android-recyclerView = "1.2.1" | ||||||
| android-appCompat = "1.6.0" | android-appCompat = "1.6.1" | ||||||
| android-fragment = "1.5.5" | android-fragment = "1.5.5" | ||||||
| android-espresso = "3.4.0" | android-espresso = "3.5.1" | ||||||
| android-test = "1.1.3" | android-test = "1.1.5" | ||||||
|  |  | ||||||
| android-props-minSdk = "21" | android-props-minSdk = "21" | ||||||
| android-props-compileSdk = "33" | android-props-compileSdk = "33" | ||||||
| @@ -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" } | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
								
							| @@ -1,5 +1,5 @@ | |||||||
| distributionBase=GRADLE_USER_HOME | distributionBase=GRADLE_USER_HOME | ||||||
| distributionPath=wrapper/dists | distributionPath=wrapper/dists | ||||||
| distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip | distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-bin.zip | ||||||
| zipStoreBase=GRADLE_USER_HOME | zipStoreBase=GRADLE_USER_HOME | ||||||
| zipStorePath=wrapper/dists | zipStorePath=wrapper/dists | ||||||
|   | |||||||
							
								
								
									
										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 | ||||||
|  | ) | ||||||
| @@ -15,7 +15,6 @@ kotlin { | |||||||
|                 api libs.ktor.client |                 api libs.ktor.client | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         androidMain { |         androidMain { | ||||||
|             dependsOn jvmMain |             dependsOn jvmMain | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -44,7 +44,8 @@ actual suspend fun <T> HttpClient.uniUpload( | |||||||
|     val withBinary = data.values.any { it is File || it is UniUploadFileInfo } |     val withBinary = data.values.any { it is File || it is UniUploadFileInfo } | ||||||
|  |  | ||||||
|     val formData = formData { |     val formData = formData { | ||||||
|         data.forEach { (k, v) -> |         for (k in data.keys) { | ||||||
|  |             val v = data[k] ?: continue | ||||||
|             when (v) { |             when (v) { | ||||||
|                 is File -> append( |                 is File -> append( | ||||||
|                     k, |                     k, | ||||||
| @@ -89,7 +90,7 @@ actual suspend fun <T> HttpClient.uniUpload( | |||||||
|         submitForm( |         submitForm( | ||||||
|             url, |             url, | ||||||
|             Parameters.build { |             Parameters.build { | ||||||
|                 formData.forEach { |                 for (it in formData) { | ||||||
|                     val formItem = (it as PartData.FormItem) |                     val formItem = (it as PartData.FormItem) | ||||||
|                     append(it.name!!, it.value) |                     append(it.name!!, it.value) | ||||||
|                 } |                 } | ||||||
|   | |||||||
| @@ -17,5 +17,8 @@ kotlin { | |||||||
|                 api libs.ktor.io |                 api libs.ktor.io | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |         androidMain { | ||||||
|  |             dependsOn jvmMain | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -5,3 +5,11 @@ plugins { | |||||||
| } | } | ||||||
|  |  | ||||||
| apply from: "$mppProjectWithSerializationPresetPath" | apply from: "$mppProjectWithSerializationPresetPath" | ||||||
|  |  | ||||||
|  | kotlin { | ||||||
|  |     sourceSets { | ||||||
|  |         androidMain { | ||||||
|  |             dependsOn jvmMain | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|   | |||||||
							
								
								
									
										8
									
								
								language_codes/src/jvmMain/kotlin/Locale.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								language_codes/src/jvmMain/kotlin/Locale.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | |||||||
|  | package dev.inmo.micro_utils.language_codes | ||||||
|  |  | ||||||
|  | import java.util.Locale | ||||||
|  |  | ||||||
|  | fun IetfLanguageCode.toJavaLocale(): Locale = Locale.forLanguageTag(code) | ||||||
|  | fun IetfLanguageCode?.toJavaLocaleOrDefault(): Locale = this ?.toJavaLocale() ?: Locale.getDefault() | ||||||
|  |  | ||||||
|  | fun Locale.toIetfLanguageCode(): IetfLanguageCode = IetfLanguageCode(toLanguageTag()) | ||||||
| @@ -50,6 +50,8 @@ kotlin { | |||||||
|                 implementation libs.android.espresso |                 implementation libs.android.espresso | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         androidMain.dependsOn jvmMain | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -61,6 +61,8 @@ kotlin { | |||||||
|                 implementation libs.android.espresso |                 implementation libs.android.espresso | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         androidMain.dependsOn jvmMain | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -32,5 +32,3 @@ kotlin { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| disableIncludingJvmCodeInAndroidPart() |  | ||||||
|   | |||||||
| @@ -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