mirror of
https://github.com/InsanusMokrassar/MicroUtils.git
synced 2025-09-17 22:39:25 +00:00
Compare commits
70 Commits
Author | SHA1 | Date | |
---|---|---|---|
b0f9e9c30a | |||
7e5c88ddc3 | |||
9824c3e00f | |||
9171d5ed11 | |||
a1830ebb82 | |||
22d2a3d9bf | |||
c9b97fc965 | |||
51f85becd5 | |||
a8a281cfb4 | |||
ddd1304949 | |||
86d70b6c02 | |||
a22bdb39e7 | |||
7ae4d5ef95 | |||
a2038cbefa | |||
992091eade | |||
e3bfead0c5 | |||
0de96141fd | |||
fa18d15c3c | |||
ea9dbf2371 | |||
d34e3ec7a9 | |||
c8833a36af | |||
a067cb0c0f | |||
999c8327bd | |||
c2ec73c70a | |||
702f782fc1 | |||
25dbcaaf83 | |||
b00d454a24 | |||
dbc921d56d | |||
438fefa7a3 | |||
5d74eac814 | |||
9fb62e1e25 | |||
3e366ea73b | |||
0ff895bffa | |||
c5bb120280 | |||
4b26a92b37 | |||
0a8453b4d2 | |||
c9872a61b6 | |||
149a1aa278 | |||
13d0e1b682 | |||
6f9be2a9f8 | |||
93ba98d993 | |||
940ee3df06 | |||
2e7bab10fd | |||
|
3ed70a37ea | ||
fe8f80b9d9 | |||
d81fb32fb9 | |||
2877b5532c | |||
b938b21395 | |||
58836359cc | |||
5edb0e1331 | |||
0f0d0b5d58 | |||
46c1887cbe | |||
5f231c2212 | |||
4e97ce86aa | |||
315a7cb29e | |||
aa7cc503f2 | |||
4bbe7e5a80 | |||
d9c05f38d2 | |||
cd0c4c9650 | |||
fc3407f104 | |||
3a5544206b | |||
e17e2f7fb8 | |||
|
d32c95f143 | ||
6d8a8ab018 | |||
a7dce8fa78 | |||
ca73ff8e19 | |||
d01ad10d7d | |||
81041ee43c | |||
|
6e004c2ae4 | ||
0e2fac5b22 |
77
CHANGELOG.md
77
CHANGELOG.md
@@ -1,5 +1,82 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 0.19.3
|
||||||
|
|
||||||
|
* `Koin`
|
||||||
|
* `Generator`
|
||||||
|
* New getter methods now available with opportunity to use parameters
|
||||||
|
* Old notation `*Single` and `*Factory` is deprecated since this release. With old
|
||||||
|
will be generated new `single*` and `factory*` notations for new generations
|
||||||
|
* Add opportunity to use generic-oriented koin definitions
|
||||||
|
|
||||||
|
## 0.19.2
|
||||||
|
|
||||||
|
* `Versions`:
|
||||||
|
* `Ktor`: `2.3.0` -> `2.3.1`
|
||||||
|
* `Koin`: `3.4.0` -> `3.4.1`
|
||||||
|
* `Uuid`: `0.7.0` -> `0.7.1`
|
||||||
|
|
||||||
|
## 0.19.1
|
||||||
|
|
||||||
|
* `Versions`:
|
||||||
|
* `Korlibs`: `4.0.1` -> `4.0.3`
|
||||||
|
* `Kotlin Poet`: `1.13.2` -> `1.14.0`
|
||||||
|
|
||||||
|
## 0.19.0
|
||||||
|
|
||||||
|
* `Versions`:
|
||||||
|
* `Korlibs`: `3.4.0` -> `4.0.1`
|
||||||
|
|
||||||
|
## 0.18.4
|
||||||
|
|
||||||
|
* `Koin`:
|
||||||
|
* New extension `lazyInject`
|
||||||
|
|
||||||
|
## 0.18.3
|
||||||
|
|
||||||
|
* `Versions`:
|
||||||
|
* `Serialization`: `1.5.0` -> `1.5.1`
|
||||||
|
* `Android Cor Ktx`: `1.10.0` -> `1.10.1`
|
||||||
|
|
||||||
|
## 0.18.2
|
||||||
|
|
||||||
|
* `Startup`:
|
||||||
|
* Now internal `Json` is fully customizable
|
||||||
|
|
||||||
|
## 0.18.1
|
||||||
|
|
||||||
|
* `Common`:
|
||||||
|
* Add `MapDiff`
|
||||||
|
* `Coroutines`:
|
||||||
|
* Add `SmartMutex`
|
||||||
|
|
||||||
|
## 0.18.0
|
||||||
|
|
||||||
|
**ALL PREVIOUSLY DEPRECATED FUNCTIONALITY HAVE BEEN REMOVED**
|
||||||
|
|
||||||
|
* `Versions`:
|
||||||
|
* `Android Fragments`: `1.5.6` -> `1.5.7`
|
||||||
|
* `Ktor`:
|
||||||
|
* `Server`:
|
||||||
|
* Now it is possible to take query parameters as list
|
||||||
|
* `Repos`:
|
||||||
|
* `Common`:
|
||||||
|
* New `WriteKeyValuesRepo.removeWithValue`
|
||||||
|
* `Cache`:
|
||||||
|
* Rename full caching factories functions to `fullyCached`
|
||||||
|
|
||||||
|
## 0.17.8
|
||||||
|
|
||||||
|
* `Versions`:
|
||||||
|
* `Ktor`: `2.2.4` -> `2.3.0`
|
||||||
|
|
||||||
|
## 0.17.7
|
||||||
|
|
||||||
|
* `Versions`:
|
||||||
|
* `Android CoreKtx`: `1.9.0` -> `1.10.0`
|
||||||
|
* `Startup`:
|
||||||
|
* Add support of `linuxX64` and `mingwX64` platforms
|
||||||
|
|
||||||
## 0.17.6
|
## 0.17.6
|
||||||
|
|
||||||
* `Versions`:
|
* `Versions`:
|
||||||
|
@@ -200,20 +200,18 @@ inline fun <T> Iterable<T>.calculateStrictDiff(
|
|||||||
) = calculateDiff(other, strictComparison = true)
|
) = calculateDiff(other, strictComparison = true)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method call [calculateDiff] with strict mode [strictComparison] and then apply differences to [this]
|
* Applies [diff] to [this] [MutableList]
|
||||||
* mutable list
|
|
||||||
*/
|
*/
|
||||||
fun <T> MutableList<T>.applyDiff(
|
fun <T> MutableList<T>.applyDiff(
|
||||||
source: Iterable<T>,
|
diff: Diff<T>
|
||||||
strictComparison: Boolean = false
|
) {
|
||||||
): Diff<T> = calculateDiff(source, strictComparison).also {
|
for (i in diff.removed.indices.sortedDescending()) {
|
||||||
for (i in it.removed.indices.sortedDescending()) {
|
removeAt(diff.removed[i].index)
|
||||||
removeAt(it.removed[i].index)
|
|
||||||
}
|
}
|
||||||
it.added.forEach { (i, t) ->
|
diff.added.forEach { (i, t) ->
|
||||||
add(i, t)
|
add(i, t)
|
||||||
}
|
}
|
||||||
it.replaced.forEach { (_, new) ->
|
diff.replaced.forEach { (_, new) ->
|
||||||
set(new.index, new.value)
|
set(new.index, new.value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -222,17 +220,30 @@ fun <T> MutableList<T>.applyDiff(
|
|||||||
* This method call [calculateDiff] with strict mode [strictComparison] and then apply differences to [this]
|
* This method call [calculateDiff] with strict mode [strictComparison] and then apply differences to [this]
|
||||||
* mutable list
|
* mutable list
|
||||||
*/
|
*/
|
||||||
|
fun <T> MutableList<T>.applyDiff(
|
||||||
|
source: Iterable<T>,
|
||||||
|
strictComparison: Boolean = false
|
||||||
|
): Diff<T> = calculateDiff(source, strictComparison).also {
|
||||||
|
applyDiff(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method call [calculateDiff] and then apply differences to [this]
|
||||||
|
* mutable list
|
||||||
|
*/
|
||||||
fun <T> MutableList<T>.applyDiff(
|
fun <T> MutableList<T>.applyDiff(
|
||||||
source: Iterable<T>,
|
source: Iterable<T>,
|
||||||
comparisonFun: (T?, T?) -> Boolean
|
comparisonFun: (T?, T?) -> Boolean
|
||||||
): Diff<T> = calculateDiff(source, comparisonFun).also {
|
): Diff<T> = calculateDiff(source, comparisonFun).also {
|
||||||
for (i in it.removed.indices.sortedDescending()) {
|
applyDiff(it)
|
||||||
removeAt(it.removed[i].index)
|
|
||||||
}
|
|
||||||
it.added.forEach { (i, t) ->
|
|
||||||
add(i, t)
|
|
||||||
}
|
|
||||||
it.replaced.forEach { (_, new) ->
|
|
||||||
set(new.index, new.value)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse [this] [Diff]. Result will contain [Diff.added] on [Diff.removed] (and vice-verse), all the
|
||||||
|
* [Diff.replaced] values will be reversed too
|
||||||
|
*/
|
||||||
|
fun <T> Diff<T>.reversed() = Diff(
|
||||||
|
removed = added,
|
||||||
|
replaced = replaced.map { it.second to it.first },
|
||||||
|
added = removed
|
||||||
|
)
|
||||||
|
@@ -0,0 +1,135 @@
|
|||||||
|
package dev.inmo.micro_utils.common
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains diff based on the comparison of objects with the same [K].
|
||||||
|
*
|
||||||
|
* @param removed Contains map with keys removed from parent map
|
||||||
|
* @param changed Contains map with keys values changed new map in comparison with old one
|
||||||
|
* @param added Contains map with new keys and values
|
||||||
|
*/
|
||||||
|
data class MapDiff<K, V> @Warning(warning) constructor(
|
||||||
|
val removed: Map<K, V>,
|
||||||
|
val changed: Map<K, Pair<V, V>>,
|
||||||
|
val added: Map<K, V>
|
||||||
|
) {
|
||||||
|
fun isEmpty() = removed.isEmpty() && changed.isEmpty() && added.isEmpty()
|
||||||
|
inline fun isNotEmpty() = !isEmpty()
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val warning = "This feature can be changed without any warranties. Use with caution and only in case you know what you are doing"
|
||||||
|
fun <K, V> empty() = MapDiff<K, V>(emptyMap(), emptyMap(), emptyMap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private inline fun <K, V> createCompareFun(
|
||||||
|
strictComparison: Boolean
|
||||||
|
): (K, V, V) -> Boolean = if (strictComparison) {
|
||||||
|
{ _, first, second -> first === second }
|
||||||
|
} else {
|
||||||
|
{ _, first, second -> first == second }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compare [this] [Map] with the [other] one in principle when [other] is newer than [this]
|
||||||
|
*
|
||||||
|
* @param compareFun Will be used to determine changed values
|
||||||
|
*/
|
||||||
|
fun <K, V> Map<K, V>.diff(
|
||||||
|
other: Map<K, V>,
|
||||||
|
compareFun: (K, V, V) -> Boolean
|
||||||
|
): MapDiff<K, V> {
|
||||||
|
val removed: Map<K, V> = (keys - other.keys).associateWith {
|
||||||
|
getValue(it)
|
||||||
|
}
|
||||||
|
val added: Map<K, V> = (other.keys - keys).associateWith {
|
||||||
|
other.getValue(it)
|
||||||
|
}
|
||||||
|
val changed = keys.intersect(other.keys).mapNotNull {
|
||||||
|
val old = getValue(it)
|
||||||
|
val new = other.getValue(it)
|
||||||
|
if (compareFun(it, old, new)) {
|
||||||
|
return@mapNotNull null
|
||||||
|
} else {
|
||||||
|
it to (old to new)
|
||||||
|
}
|
||||||
|
}.toMap()
|
||||||
|
|
||||||
|
return MapDiff(
|
||||||
|
removed,
|
||||||
|
changed,
|
||||||
|
added
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compare [this] [Map] with the [other] one in principle when [other] is newer than [this]
|
||||||
|
*
|
||||||
|
* @param strictComparison If true, will use strict (===) comparison for the values' comparison. Otherwise, standard
|
||||||
|
* `equals` will be used
|
||||||
|
*/
|
||||||
|
fun <K, V> Map<K, V>.diff(
|
||||||
|
other: Map<K, V>,
|
||||||
|
strictComparison: Boolean = false
|
||||||
|
): MapDiff<K, V> = diff(
|
||||||
|
other,
|
||||||
|
compareFun = createCompareFun(strictComparison)
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will apply [mapDiff] to [this] [MutableMap]
|
||||||
|
*/
|
||||||
|
fun <K, V> MutableMap<K, V>.applyDiff(
|
||||||
|
mapDiff: MapDiff<K, V>
|
||||||
|
) {
|
||||||
|
mapDiff.apply {
|
||||||
|
removed.keys.forEach { remove(it) }
|
||||||
|
changed.forEach { (k, oldNew) ->
|
||||||
|
put(k, oldNew.second)
|
||||||
|
}
|
||||||
|
added.forEach { (k, new) ->
|
||||||
|
put(k, new)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will apply changes with [from] map into [this] one
|
||||||
|
*
|
||||||
|
* @param compareFun Will be used to determine changed values
|
||||||
|
*
|
||||||
|
* @return [MapDiff] applied to [this] [MutableMap]
|
||||||
|
*/
|
||||||
|
fun <K, V> MutableMap<K, V>.applyDiff(
|
||||||
|
from: Map<K, V>,
|
||||||
|
compareFun: (K, V, V) -> Boolean
|
||||||
|
): MapDiff<K, V> {
|
||||||
|
return diff(from, compareFun).also {
|
||||||
|
applyDiff(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will apply changes with [from] map into [this] one
|
||||||
|
*
|
||||||
|
* @param strictComparison If true, will use strict (===) comparison for the values' comparison. Otherwise, standard
|
||||||
|
* `equals` will be used
|
||||||
|
*
|
||||||
|
* @return [MapDiff] applied to [this] [MutableMap]
|
||||||
|
*/
|
||||||
|
fun <K, V> MutableMap<K, V>.applyDiff(
|
||||||
|
from: Map<K, V>,
|
||||||
|
strictComparison: Boolean = false
|
||||||
|
): MapDiff<K, V> = applyDiff(
|
||||||
|
from,
|
||||||
|
compareFun = createCompareFun(strictComparison)
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse [this] [MapDiff]. Result will contain [MapDiff.added] on [MapDiff.removed] (and vice-verse), all the
|
||||||
|
* [MapDiff.changed] values will be reversed too
|
||||||
|
*/
|
||||||
|
fun <K, V> MapDiff<K, V>.reversed(): MapDiff<K, V> = MapDiff(
|
||||||
|
removed = added,
|
||||||
|
changed = changed.mapValues { (_, oldNew) -> oldNew.second to oldNew.first },
|
||||||
|
added = removed
|
||||||
|
)
|
@@ -1,21 +0,0 @@
|
|||||||
package dev.inmo.micro_utils.coroutines.compose
|
|
||||||
|
|
||||||
import androidx.compose.runtime.MutableState
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
|
||||||
|
|
||||||
@Deprecated("Duplicated functionality", ReplaceWith("asMutableComposeState(initial, scope)", "dev.inmo.micro_utils.coroutines.compose.asMutableComposeState"))
|
|
||||||
fun <T> Flow<T>.toMutableState(
|
|
||||||
initial: T,
|
|
||||||
scope: CoroutineScope
|
|
||||||
): MutableState<T> = asMutableComposeState(initial, scope)
|
|
||||||
|
|
||||||
@Deprecated("Duplicated functionality", ReplaceWith("asMutableComposeState(scope)", "dev.inmo.micro_utils.coroutines.compose.asMutableComposeState"))
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
|
||||||
inline fun <T> StateFlow<T>.toMutableState(
|
|
||||||
scope: CoroutineScope
|
|
||||||
): MutableState<T> = asMutableComposeState(scope)
|
|
||||||
|
|
@@ -0,0 +1,136 @@
|
|||||||
|
package dev.inmo.micro_utils.coroutines
|
||||||
|
|
||||||
|
import kotlinx.coroutines.currentCoroutineContext
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
|
import kotlinx.coroutines.isActive
|
||||||
|
import kotlinx.coroutines.sync.Mutex
|
||||||
|
import kotlinx.coroutines.sync.withLock
|
||||||
|
import kotlin.contracts.ExperimentalContracts
|
||||||
|
import kotlin.contracts.InvocationKind
|
||||||
|
import kotlin.contracts.contract
|
||||||
|
|
||||||
|
/**
|
||||||
|
* It is interface which will work like classic [Mutex], but in difference have [lockStateFlow] for listening of the
|
||||||
|
* [SmartMutex] state.
|
||||||
|
*
|
||||||
|
* There is [Mutable] and [Immutable] realizations. In case you are owner and manager current state of lock, you need
|
||||||
|
* [Mutable] [SmartMutex]. Otherwise, [Immutable].
|
||||||
|
*
|
||||||
|
* Any [Mutable] [SmartMutex] may produce its [Immutable] variant which will contains [lockStateFlow] equal to its
|
||||||
|
* [Mutable] creator
|
||||||
|
*/
|
||||||
|
sealed interface SmartMutex {
|
||||||
|
val lockStateFlow: StateFlow<Boolean>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* * True - locked
|
||||||
|
* * False - unlocked
|
||||||
|
*/
|
||||||
|
val isLocked: Boolean
|
||||||
|
get() = lockStateFlow.value
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Immutable variant of [SmartMutex]. In fact will depend on the owner of [lockStateFlow]
|
||||||
|
*/
|
||||||
|
class Immutable(override val lockStateFlow: StateFlow<Boolean>) : SmartMutex
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mutable variant of [SmartMutex]. With that variant you may [lock] and [unlock]. Besides, you may create
|
||||||
|
* [Immutable] variant of [this] instance with [immutable] factory
|
||||||
|
*
|
||||||
|
* @param locked Preset state of [isLocked] and its internal [_lockStateFlow]
|
||||||
|
*/
|
||||||
|
class Mutable(locked: Boolean = false) : SmartMutex {
|
||||||
|
private val _lockStateFlow = MutableStateFlow<Boolean>(locked)
|
||||||
|
override val lockStateFlow: StateFlow<Boolean> = _lockStateFlow.asStateFlow()
|
||||||
|
|
||||||
|
private val internalChangesMutex = Mutex()
|
||||||
|
|
||||||
|
fun immutable() = Immutable(lockStateFlow)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds call until this [SmartMutex] will be re-locked. That means that while [isLocked] == true, [holds] will
|
||||||
|
* wait for [isLocked] == false and then try to lock
|
||||||
|
*/
|
||||||
|
suspend fun lock() {
|
||||||
|
do {
|
||||||
|
waitUnlock()
|
||||||
|
val shouldContinue = internalChangesMutex.withLock {
|
||||||
|
if (_lockStateFlow.value) {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
_lockStateFlow.value = true
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (shouldContinue && currentCoroutineContext().isActive)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will try to lock this [SmartMutex] immediataly
|
||||||
|
*
|
||||||
|
* @return True if lock was successful. False otherwise
|
||||||
|
*/
|
||||||
|
suspend fun tryLock(): Boolean {
|
||||||
|
return if (!_lockStateFlow.value) {
|
||||||
|
internalChangesMutex.withLock {
|
||||||
|
if (!_lockStateFlow.value) {
|
||||||
|
_lockStateFlow.value = true
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If [isLocked] == true - will change it to false and return true. If current call will not unlock this
|
||||||
|
* [SmartMutex] - false
|
||||||
|
*/
|
||||||
|
suspend fun unlock(): Boolean {
|
||||||
|
return if (_lockStateFlow.value) {
|
||||||
|
internalChangesMutex.withLock {
|
||||||
|
if (_lockStateFlow.value) {
|
||||||
|
_lockStateFlow.value = false
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will call [SmartMutex.Mutable.lock], then execute [action] and return the result after [SmartMutex.Mutable.unlock]
|
||||||
|
*/
|
||||||
|
@OptIn(ExperimentalContracts::class)
|
||||||
|
suspend inline fun <T> SmartMutex.Mutable.withLock(action: () -> T): T {
|
||||||
|
contract {
|
||||||
|
callsInPlace(action, InvocationKind.EXACTLY_ONCE)
|
||||||
|
}
|
||||||
|
|
||||||
|
lock()
|
||||||
|
try {
|
||||||
|
return action()
|
||||||
|
} finally {
|
||||||
|
unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will wait until the [SmartMutex.lockStateFlow] of [this] instance will be false.
|
||||||
|
*
|
||||||
|
* Anyway, after the end of this block there are no any guaranties that [SmartMutex.isLocked] == false due to the fact
|
||||||
|
* that some other parties may lock it again
|
||||||
|
*/
|
||||||
|
suspend fun SmartMutex.waitUnlock() = lockStateFlow.first { !it }
|
@@ -1,6 +1,6 @@
|
|||||||
package dev.inmo.micro_utils.crypto
|
package dev.inmo.micro_utils.crypto
|
||||||
|
|
||||||
import com.soywiz.krypto.md5
|
import korlibs.crypto.md5
|
||||||
|
|
||||||
typealias MD5 = String
|
typealias MD5 = String
|
||||||
|
|
||||||
|
@@ -13,10 +13,10 @@ repositories {
|
|||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
jvm()
|
jvm()
|
||||||
// js(IR) {
|
js(IR) {
|
||||||
// browser()
|
browser()
|
||||||
// nodejs()
|
nodejs()
|
||||||
// }
|
}
|
||||||
android {}
|
android {}
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
@@ -29,7 +29,7 @@ kotlin {
|
|||||||
it != project
|
it != project
|
||||||
&& it.hasProperty("kotlin")
|
&& it.hasProperty("kotlin")
|
||||||
&& it.kotlin.sourceSets.any { it.name.contains("commonMain") }
|
&& it.kotlin.sourceSets.any { it.name.contains("commonMain") }
|
||||||
// && it.kotlin.sourceSets.any { it.name.contains("jsMain") }
|
&& it.kotlin.sourceSets.any { it.name.contains("jsMain") }
|
||||||
&& it.kotlin.sourceSets.any { it.name.contains("jvmMain") }
|
&& it.kotlin.sourceSets.any { it.name.contains("jvmMain") }
|
||||||
&& it.kotlin.sourceSets.any { it.name.contains("androidMain") }
|
&& it.kotlin.sourceSets.any { it.name.contains("androidMain") }
|
||||||
) {
|
) {
|
||||||
@@ -38,22 +38,22 @@ kotlin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// jsMain {
|
jsMain {
|
||||||
// dependencies {
|
dependencies {
|
||||||
// implementation kotlin('stdlib')
|
implementation kotlin('stdlib')
|
||||||
|
|
||||||
// project.parent.subprojects.forEach {
|
project.parent.subprojects.forEach {
|
||||||
// if (
|
if (
|
||||||
// it != project
|
it != project
|
||||||
// && it.hasProperty("kotlin")
|
&& it.hasProperty("kotlin")
|
||||||
// && it.kotlin.sourceSets.any { it.name.contains("commonMain") }
|
&& it.kotlin.sourceSets.any { it.name.contains("commonMain") }
|
||||||
// && it.kotlin.sourceSets.any { it.name.contains("jsMain") }
|
&& it.kotlin.sourceSets.any { it.name.contains("jsMain") }
|
||||||
// ) {
|
) {
|
||||||
// api it
|
api it
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
jvmMain {
|
jvmMain {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation kotlin('stdlib')
|
implementation kotlin('stdlib')
|
||||||
@@ -116,9 +116,9 @@ tasks.dokkaHtml {
|
|||||||
sourceRoots.setFrom(findSourcesWithName("commonMain"))
|
sourceRoots.setFrom(findSourcesWithName("commonMain"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// named("jsMain") {
|
named("jsMain") {
|
||||||
// sourceRoots.setFrom(findSourcesWithName("jsMain", "commonMain"))
|
sourceRoots.setFrom(findSourcesWithName("jsMain"))
|
||||||
// }
|
}
|
||||||
|
|
||||||
named("jvmMain") {
|
named("jvmMain") {
|
||||||
sourceRoots.setFrom(findSourcesWithName("jvmMain"))
|
sourceRoots.setFrom(findSourcesWithName("jvmMain"))
|
||||||
|
@@ -23,7 +23,7 @@ allprojects {
|
|||||||
mppProjectWithSerializationPresetPath = "${rootProject.projectDir.absolutePath}/mppProjectWithSerialization.gradle"
|
mppProjectWithSerializationPresetPath = "${rootProject.projectDir.absolutePath}/mppProjectWithSerialization.gradle"
|
||||||
mppProjectWithSerializationAndComposePresetPath = "${rootProject.projectDir.absolutePath}/mppProjectWithSerializationAndCompose.gradle"
|
mppProjectWithSerializationAndComposePresetPath = "${rootProject.projectDir.absolutePath}/mppProjectWithSerializationAndCompose.gradle"
|
||||||
mppJavaProjectPresetPath = "${rootProject.projectDir.absolutePath}/mppJavaProject.gradle"
|
mppJavaProjectPresetPath = "${rootProject.projectDir.absolutePath}/mppJavaProject.gradle"
|
||||||
mppJsAndJavaProjectPresetPath = "${rootProject.projectDir.absolutePath}/mppJsAndJavaProject.gradle"
|
mppJvmJsLinuxMingwProjectPresetPath = "${rootProject.projectDir.absolutePath}/mppJvmJsLinuxMingwProject.gradle"
|
||||||
mppAndroidProjectPresetPath = "${rootProject.projectDir.absolutePath}/mppAndroidProject.gradle"
|
mppAndroidProjectPresetPath = "${rootProject.projectDir.absolutePath}/mppAndroidProject.gradle"
|
||||||
|
|
||||||
defaultAndroidSettingsPresetPath = "${rootProject.projectDir.absolutePath}/defaultAndroidSettings.gradle"
|
defaultAndroidSettingsPresetPath = "${rootProject.projectDir.absolutePath}/defaultAndroidSettings.gradle"
|
||||||
|
@@ -14,5 +14,5 @@ crypto_js_version=4.1.1
|
|||||||
# Project data
|
# Project data
|
||||||
|
|
||||||
group=dev.inmo
|
group=dev.inmo
|
||||||
version=0.17.6
|
version=0.19.3
|
||||||
android_code_version=188
|
android_code_version=199
|
||||||
|
@@ -1,42 +1,42 @@
|
|||||||
[versions]
|
[versions]
|
||||||
|
|
||||||
kt = "1.8.20"
|
kt = "1.8.20"
|
||||||
kt-serialization = "1.5.0"
|
kt-serialization = "1.5.1"
|
||||||
kt-coroutines = "1.6.4"
|
kt-coroutines = "1.6.4"
|
||||||
|
|
||||||
kslog = "1.1.1"
|
kslog = "1.1.1"
|
||||||
|
|
||||||
jb-compose = "1.4.0"
|
jb-compose = "1.4.0"
|
||||||
jb-exposed = "0.41.1"
|
jb-exposed = "0.41.1"
|
||||||
jb-dokka = "1.8.10"
|
jb-dokka = "1.8.20"
|
||||||
|
|
||||||
korlibs = "3.4.0"
|
korlibs = "4.0.3"
|
||||||
uuid = "0.7.0"
|
uuid = "0.7.1"
|
||||||
|
|
||||||
ktor = "2.2.4"
|
ktor = "2.3.1"
|
||||||
|
|
||||||
gh-release = "2.4.1"
|
gh-release = "2.4.1"
|
||||||
|
|
||||||
koin = "3.4.0"
|
koin = "3.4.1"
|
||||||
|
|
||||||
okio = "3.3.0"
|
okio = "3.3.0"
|
||||||
|
|
||||||
ksp = "1.8.20-1.0.10"
|
ksp = "1.8.20-1.0.11"
|
||||||
kotlin-poet = "1.13.0"
|
kotlin-poet = "1.14.2"
|
||||||
|
|
||||||
android-gradle = "7.3.1"
|
android-gradle = "7.4.2"
|
||||||
dexcount = "4.0.0"
|
dexcount = "4.0.0"
|
||||||
|
|
||||||
android-coreKtx = "1.9.0"
|
android-coreKtx = "1.10.1"
|
||||||
android-recyclerView = "1.3.0"
|
android-recyclerView = "1.3.0"
|
||||||
android-appCompat = "1.6.1"
|
android-appCompat = "1.6.1"
|
||||||
android-fragment = "1.5.6"
|
android-fragment = "1.5.7"
|
||||||
android-espresso = "3.5.1"
|
android-espresso = "3.5.1"
|
||||||
android-test = "1.1.5"
|
android-test = "1.1.5"
|
||||||
|
|
||||||
android-props-minSdk = "21"
|
android-props-minSdk = "21"
|
||||||
android-props-compileSdk = "33"
|
android-props-compileSdk = "33"
|
||||||
android-props-buildTools = "33.0.1"
|
android-props-buildTools = "33.0.2"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
|
|
||||||
|
@@ -9,19 +9,28 @@ import com.google.devtools.ksp.processing.Resolver
|
|||||||
import com.google.devtools.ksp.processing.SymbolProcessor
|
import com.google.devtools.ksp.processing.SymbolProcessor
|
||||||
import com.google.devtools.ksp.symbol.KSAnnotated
|
import com.google.devtools.ksp.symbol.KSAnnotated
|
||||||
import com.google.devtools.ksp.symbol.KSFile
|
import com.google.devtools.ksp.symbol.KSFile
|
||||||
|
import com.google.devtools.ksp.symbol.Modifier
|
||||||
|
import com.squareup.kotlinpoet.AnnotationSpec
|
||||||
import com.squareup.kotlinpoet.ClassName
|
import com.squareup.kotlinpoet.ClassName
|
||||||
|
import com.squareup.kotlinpoet.CodeBlock
|
||||||
import com.squareup.kotlinpoet.FileSpec
|
import com.squareup.kotlinpoet.FileSpec
|
||||||
import com.squareup.kotlinpoet.FunSpec
|
import com.squareup.kotlinpoet.FunSpec
|
||||||
|
import com.squareup.kotlinpoet.KModifier
|
||||||
import com.squareup.kotlinpoet.ParameterSpec
|
import com.squareup.kotlinpoet.ParameterSpec
|
||||||
|
import com.squareup.kotlinpoet.ParameterizedTypeName
|
||||||
import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
|
import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
|
||||||
import com.squareup.kotlinpoet.PropertySpec
|
import com.squareup.kotlinpoet.PropertySpec
|
||||||
|
import com.squareup.kotlinpoet.TypeName
|
||||||
|
import com.squareup.kotlinpoet.TypeVariableName
|
||||||
import com.squareup.kotlinpoet.asTypeName
|
import com.squareup.kotlinpoet.asTypeName
|
||||||
import com.squareup.kotlinpoet.ksp.toClassName
|
import com.squareup.kotlinpoet.ksp.toClassName
|
||||||
import com.squareup.kotlinpoet.ksp.toTypeName
|
import com.squareup.kotlinpoet.ksp.toTypeName
|
||||||
import com.squareup.kotlinpoet.ksp.writeTo
|
import com.squareup.kotlinpoet.ksp.writeTo
|
||||||
|
import dev.inmo.micro_utils.koin.annotations.GenerateGenericKoinDefinition
|
||||||
import dev.inmo.micro_utils.koin.annotations.GenerateKoinDefinition
|
import dev.inmo.micro_utils.koin.annotations.GenerateKoinDefinition
|
||||||
import org.koin.core.Koin
|
import org.koin.core.Koin
|
||||||
import org.koin.core.module.Module
|
import org.koin.core.module.Module
|
||||||
|
import org.koin.core.parameter.ParametersDefinition
|
||||||
import org.koin.core.scope.Scope
|
import org.koin.core.scope.Scope
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
@@ -32,11 +41,236 @@ class Processor(
|
|||||||
private val definitionClassName = ClassName("org.koin.core.definition", "Definition")
|
private val definitionClassName = ClassName("org.koin.core.definition", "Definition")
|
||||||
private val koinDefinitionClassName = ClassName("org.koin.core.definition", "KoinDefinition")
|
private val koinDefinitionClassName = ClassName("org.koin.core.definition", "KoinDefinition")
|
||||||
|
|
||||||
|
private fun FileSpec.Builder.addCodeForType(
|
||||||
|
targetType: TypeName,
|
||||||
|
name: String,
|
||||||
|
nullable: Boolean,
|
||||||
|
generateSingle: Boolean,
|
||||||
|
generateFactory: Boolean,
|
||||||
|
) {
|
||||||
|
val targetTypeAsGenericType = (targetType as? TypeVariableName) ?.copy(reified = true)
|
||||||
|
|
||||||
|
fun addGetterProperty(
|
||||||
|
receiver: KClass<*>
|
||||||
|
) {
|
||||||
|
addProperty(
|
||||||
|
PropertySpec.builder(
|
||||||
|
name,
|
||||||
|
targetType,
|
||||||
|
).apply {
|
||||||
|
addKdoc(
|
||||||
|
"""
|
||||||
|
@return Definition by key "${name}"
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
getter(
|
||||||
|
FunSpec.getterBuilder().apply {
|
||||||
|
targetTypeAsGenericType ?.let {
|
||||||
|
addModifiers(KModifier.INLINE)
|
||||||
|
}
|
||||||
|
addCode(
|
||||||
|
"return " + (if (nullable) {
|
||||||
|
"getOrNull"
|
||||||
|
} else {
|
||||||
|
"get"
|
||||||
|
}) + "(named(\"${name}\"))"
|
||||||
|
)
|
||||||
|
}.build()
|
||||||
|
)
|
||||||
|
targetTypeAsGenericType ?.let {
|
||||||
|
addTypeVariable(it)
|
||||||
|
}
|
||||||
|
receiver(receiver)
|
||||||
|
}.build()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetTypeAsGenericType == null) {
|
||||||
|
addGetterProperty(Scope::class)
|
||||||
|
addGetterProperty(Koin::class)
|
||||||
|
}
|
||||||
|
|
||||||
|
val parametersDefinitionClassName = ClassName(
|
||||||
|
"org.koin.core.parameter",
|
||||||
|
"ParametersDefinition"
|
||||||
|
)
|
||||||
|
fun addGetterMethod(
|
||||||
|
receiver: KClass<*>
|
||||||
|
) {
|
||||||
|
addFunction(
|
||||||
|
FunSpec.builder(
|
||||||
|
name
|
||||||
|
).apply {
|
||||||
|
addKdoc(
|
||||||
|
"""
|
||||||
|
@return Definition by key "${name}" with [parameters]
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
receiver(receiver)
|
||||||
|
addParameter(
|
||||||
|
ParameterSpec(
|
||||||
|
"parameters",
|
||||||
|
parametersDefinitionClassName.let {
|
||||||
|
if (targetTypeAsGenericType != null) {
|
||||||
|
it.copy(nullable = true)
|
||||||
|
} else {
|
||||||
|
it
|
||||||
|
}
|
||||||
|
},
|
||||||
|
KModifier.NOINLINE
|
||||||
|
).toBuilder().apply {
|
||||||
|
if (targetTypeAsGenericType != null) {
|
||||||
|
defaultValue("null")
|
||||||
|
}
|
||||||
|
}.build()
|
||||||
|
)
|
||||||
|
addModifiers(KModifier.INLINE)
|
||||||
|
targetTypeAsGenericType ?.let {
|
||||||
|
addTypeVariable(it)
|
||||||
|
returns(it)
|
||||||
|
} ?: returns(targetType)
|
||||||
|
addCode(
|
||||||
|
"return " + (if (nullable) {
|
||||||
|
"getOrNull"
|
||||||
|
} else {
|
||||||
|
"get"
|
||||||
|
}) + "(named(\"${name}\"), parameters)"
|
||||||
|
)
|
||||||
|
}.build()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
addGetterMethod(Scope::class)
|
||||||
|
addGetterMethod(Koin::class)
|
||||||
|
|
||||||
|
fun FunSpec.Builder.addDefinitionParameter() {
|
||||||
|
val definitionModifiers = if (targetTypeAsGenericType == null) {
|
||||||
|
arrayOf()
|
||||||
|
} else {
|
||||||
|
arrayOf(KModifier.NOINLINE)
|
||||||
|
}
|
||||||
|
addParameter(
|
||||||
|
ParameterSpec.builder(
|
||||||
|
"definition",
|
||||||
|
definitionClassName.parameterizedBy(targetType.copy(nullable = false)),
|
||||||
|
*definitionModifiers
|
||||||
|
).build()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (generateSingle) {
|
||||||
|
fun FunSpec.Builder.configure(
|
||||||
|
useInstead: String? = null
|
||||||
|
) {
|
||||||
|
addKdoc(
|
||||||
|
"""
|
||||||
|
Will register [definition] with [org.koin.core.module.Module.single] and key "${name}"
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
receiver(Module::class)
|
||||||
|
addParameter(
|
||||||
|
ParameterSpec.builder(
|
||||||
|
"createdAtStart",
|
||||||
|
Boolean::class
|
||||||
|
).apply {
|
||||||
|
defaultValue("false")
|
||||||
|
}.build()
|
||||||
|
)
|
||||||
|
addDefinitionParameter()
|
||||||
|
returns(koinDefinitionClassName.parameterizedBy(targetType.copy(nullable = false)))
|
||||||
|
addCode(
|
||||||
|
"return single(named(\"${name}\"), createdAtStart = createdAtStart, definition = definition)"
|
||||||
|
)
|
||||||
|
targetTypeAsGenericType ?.let {
|
||||||
|
addTypeVariable(it)
|
||||||
|
addModifiers(KModifier.INLINE)
|
||||||
|
}
|
||||||
|
if (useInstead != null) {
|
||||||
|
addAnnotation(
|
||||||
|
AnnotationSpec.builder(
|
||||||
|
Deprecated::class
|
||||||
|
).apply {
|
||||||
|
addMember(
|
||||||
|
CodeBlock.of(
|
||||||
|
"""
|
||||||
|
"This definition is old style and should not be used anymore. Use $useInstead instead"
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
addMember(CodeBlock.of("ReplaceWith(\"$useInstead\")"))
|
||||||
|
}.build()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val actualSingleName = "single${name.replaceFirstChar { it.uppercase() }}"
|
||||||
|
if (targetTypeAsGenericType == null) { // classic type
|
||||||
|
addFunction(
|
||||||
|
FunSpec.builder("${name}Single").apply { configure(actualSingleName) }.build()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
addFunction(
|
||||||
|
FunSpec.builder(actualSingleName).apply { configure() }.build()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (generateFactory) {
|
||||||
|
fun FunSpec.Builder.configure(
|
||||||
|
useInstead: String? = null
|
||||||
|
) {
|
||||||
|
addKdoc(
|
||||||
|
"""
|
||||||
|
Will register [definition] with [org.koin.core.module.Module.factory] and key "${name}"
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
receiver(Module::class)
|
||||||
|
addDefinitionParameter()
|
||||||
|
returns(koinDefinitionClassName.parameterizedBy(targetType.copy(nullable = false)))
|
||||||
|
addCode(
|
||||||
|
"return factory(named(\"${name}\"), definition = definition)"
|
||||||
|
)
|
||||||
|
targetTypeAsGenericType ?.let {
|
||||||
|
addTypeVariable(it)
|
||||||
|
addModifiers(KModifier.INLINE)
|
||||||
|
}
|
||||||
|
if (useInstead != null) {
|
||||||
|
addAnnotation(
|
||||||
|
AnnotationSpec.builder(
|
||||||
|
Deprecated::class
|
||||||
|
).apply {
|
||||||
|
addMember(
|
||||||
|
CodeBlock.of(
|
||||||
|
"""
|
||||||
|
"This definition is old style and should not be used anymore. Use $useInstead instead"
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
addMember(CodeBlock.of("ReplaceWith(\"$useInstead\")"))
|
||||||
|
}.build()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val actualFactoryName = "factory${name.replaceFirstChar { it.uppercase() }}"
|
||||||
|
if (targetTypeAsGenericType == null) { // classic type
|
||||||
|
addFunction(
|
||||||
|
FunSpec.builder("${name}Factory").apply { configure(useInstead = actualFactoryName) }.build()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
addFunction(
|
||||||
|
FunSpec.builder(actualFactoryName).apply { configure() }.build()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
addImport("org.koin.core.qualifier", "named")
|
||||||
|
}
|
||||||
|
|
||||||
@OptIn(KspExperimental::class)
|
@OptIn(KspExperimental::class)
|
||||||
override fun process(resolver: Resolver): List<KSAnnotated> {
|
override fun process(resolver: Resolver): List<KSAnnotated> {
|
||||||
resolver.getSymbolsWithAnnotation(
|
(resolver.getSymbolsWithAnnotation(
|
||||||
GenerateKoinDefinition::class.qualifiedName!!
|
GenerateKoinDefinition::class.qualifiedName!!
|
||||||
).filterIsInstance<KSFile>().forEach { ksFile ->
|
) + resolver.getSymbolsWithAnnotation(
|
||||||
|
GenerateGenericKoinDefinition::class.qualifiedName!!
|
||||||
|
)).filterIsInstance<KSFile>().forEach { ksFile ->
|
||||||
FileSpec.builder(
|
FileSpec.builder(
|
||||||
ksFile.packageName.asString(),
|
ksFile.packageName.asString(),
|
||||||
"GeneratedDefinitions${ksFile.fileName.removeSuffix(".kt")}"
|
"GeneratedDefinitions${ksFile.fileName.removeSuffix(".kt")}"
|
||||||
@@ -72,92 +306,12 @@ class Processor(
|
|||||||
}.copy(
|
}.copy(
|
||||||
nullable = it.nullable
|
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)
|
addCodeForType(targetType, it.name, it.nullable, it.generateSingle, it.generateFactory)
|
||||||
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()
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
ksFile.getAnnotationsByType(GenerateGenericKoinDefinition::class).forEach {
|
||||||
if (it.generateFactory) {
|
val targetType = TypeVariableName("T", Any::class)
|
||||||
addFunction(
|
addCodeForType(targetType, it.name, it.nullable, it.generateSingle, it.generateFactory)
|
||||||
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 {
|
}.build().let {
|
||||||
File(
|
File(
|
||||||
|
@@ -3,12 +3,15 @@
|
|||||||
// ORIGINAL FILE: Test.kt
|
// ORIGINAL FILE: Test.kt
|
||||||
package dev.inmo.micro_utils.koin.generator.test
|
package dev.inmo.micro_utils.koin.generator.test
|
||||||
|
|
||||||
|
import kotlin.Any
|
||||||
import kotlin.Boolean
|
import kotlin.Boolean
|
||||||
|
import kotlin.Deprecated
|
||||||
import kotlin.String
|
import kotlin.String
|
||||||
import org.koin.core.Koin
|
import org.koin.core.Koin
|
||||||
import org.koin.core.definition.Definition
|
import org.koin.core.definition.Definition
|
||||||
import org.koin.core.definition.KoinDefinition
|
import org.koin.core.definition.KoinDefinition
|
||||||
import org.koin.core.module.Module
|
import org.koin.core.module.Module
|
||||||
|
import org.koin.core.parameter.ParametersDefinition
|
||||||
import org.koin.core.qualifier.named
|
import org.koin.core.qualifier.named
|
||||||
import org.koin.core.scope.Scope
|
import org.koin.core.scope.Scope
|
||||||
|
|
||||||
@@ -24,15 +27,73 @@ public val Scope.sampleInfo: Test<String>
|
|||||||
public val Koin.sampleInfo: Test<String>
|
public val Koin.sampleInfo: Test<String>
|
||||||
get() = get(named("sampleInfo"))
|
get() = get(named("sampleInfo"))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Definition by key "sampleInfo" with [parameters]
|
||||||
|
*/
|
||||||
|
public inline fun Scope.sampleInfo(noinline parameters: ParametersDefinition): Test<String> =
|
||||||
|
get(named("sampleInfo"), parameters)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Definition by key "sampleInfo" with [parameters]
|
||||||
|
*/
|
||||||
|
public inline fun Koin.sampleInfo(noinline parameters: ParametersDefinition): Test<String> =
|
||||||
|
get(named("sampleInfo"), parameters)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Will register [definition] with [org.koin.core.module.Module.single] and key "sampleInfo"
|
* Will register [definition] with [org.koin.core.module.Module.single] and key "sampleInfo"
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(
|
||||||
|
"This definition is old style and should not be used anymore. Use singleSampleInfo instead",
|
||||||
|
ReplaceWith("singleSampleInfo"),
|
||||||
|
)
|
||||||
public fun Module.sampleInfoSingle(createdAtStart: Boolean = false,
|
public fun Module.sampleInfoSingle(createdAtStart: Boolean = false,
|
||||||
definition: Definition<Test<String>>): KoinDefinition<Test<String>> =
|
definition: Definition<Test<String>>): KoinDefinition<Test<String>> =
|
||||||
single(named("sampleInfo"), createdAtStart = createdAtStart, definition = definition)
|
single(named("sampleInfo"), createdAtStart = createdAtStart, definition = definition)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will register [definition] with [org.koin.core.module.Module.single] and key "sampleInfo"
|
||||||
|
*/
|
||||||
|
public fun Module.singleSampleInfo(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"
|
* Will register [definition] with [org.koin.core.module.Module.factory] and key "sampleInfo"
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(
|
||||||
|
"This definition is old style and should not be used anymore. Use factorySampleInfo instead",
|
||||||
|
ReplaceWith("factorySampleInfo"),
|
||||||
|
)
|
||||||
public fun Module.sampleInfoFactory(definition: Definition<Test<String>>):
|
public fun Module.sampleInfoFactory(definition: Definition<Test<String>>):
|
||||||
KoinDefinition<Test<String>> = factory(named("sampleInfo"), definition = definition)
|
KoinDefinition<Test<String>> = factory(named("sampleInfo"), definition = definition)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will register [definition] with [org.koin.core.module.Module.factory] and key "sampleInfo"
|
||||||
|
*/
|
||||||
|
public fun Module.factorySampleInfo(definition: Definition<Test<String>>):
|
||||||
|
KoinDefinition<Test<String>> = factory(named("sampleInfo"), definition = definition)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Definition by key "test" with [parameters]
|
||||||
|
*/
|
||||||
|
public inline fun <reified T : Any> Scope.test(noinline parameters: ParametersDefinition? = null): T
|
||||||
|
= get(named("test"), parameters)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Definition by key "test" with [parameters]
|
||||||
|
*/
|
||||||
|
public inline fun <reified T : Any> Koin.test(noinline parameters: ParametersDefinition? = null): T
|
||||||
|
= get(named("test"), parameters)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will register [definition] with [org.koin.core.module.Module.single] and key "test"
|
||||||
|
*/
|
||||||
|
public inline fun <reified T : Any> Module.singleTest(createdAtStart: Boolean = false, noinline
|
||||||
|
definition: Definition<T>): KoinDefinition<T> = single(named("test"), createdAtStart =
|
||||||
|
createdAtStart, definition = definition)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will register [definition] with [org.koin.core.module.Module.factory] and key "test"
|
||||||
|
*/
|
||||||
|
public inline fun <reified T : Any> Module.factoryTest(noinline definition: Definition<T>):
|
||||||
|
KoinDefinition<T> = factory(named("test"), definition = definition)
|
||||||
|
@@ -1,6 +1,8 @@
|
|||||||
@file:GenerateKoinDefinition("sampleInfo", Test::class, String::class, nullable = false)
|
@file:GenerateKoinDefinition("sampleInfo", Test::class, String::class, nullable = false)
|
||||||
|
@file:GenerateGenericKoinDefinition("test", nullable = false)
|
||||||
package dev.inmo.micro_utils.koin.generator.test
|
package dev.inmo.micro_utils.koin.generator.test
|
||||||
|
|
||||||
|
import dev.inmo.micro_utils.koin.annotations.GenerateGenericKoinDefinition
|
||||||
import dev.inmo.micro_utils.koin.annotations.GenerateKoinDefinition
|
import dev.inmo.micro_utils.koin.annotations.GenerateKoinDefinition
|
||||||
import org.koin.core.Koin
|
import org.koin.core.Koin
|
||||||
|
|
||||||
|
40
koin/src/commonMain/kotlin/GetWithDefinition.kt
Normal file
40
koin/src/commonMain/kotlin/GetWithDefinition.kt
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
package dev.inmo.micro_utils.koin
|
||||||
|
|
||||||
|
import org.koin.core.Koin
|
||||||
|
import org.koin.core.definition.BeanDefinition
|
||||||
|
import org.koin.core.definition.KoinDefinition
|
||||||
|
import org.koin.core.instance.InstanceFactory
|
||||||
|
import org.koin.core.parameter.ParametersDefinition
|
||||||
|
import org.koin.core.scope.Scope
|
||||||
|
|
||||||
|
fun <T> Koin.get(definition: BeanDefinition<T>, parameters: ParametersDefinition? = null): T = get(
|
||||||
|
definition.primaryType,
|
||||||
|
definition.qualifier,
|
||||||
|
parameters
|
||||||
|
)
|
||||||
|
|
||||||
|
fun <T> Koin.get(definition: InstanceFactory<T>, parameters: ParametersDefinition? = null): T = get(
|
||||||
|
definition.beanDefinition,
|
||||||
|
parameters
|
||||||
|
)
|
||||||
|
|
||||||
|
fun <T> Koin.get(definition: KoinDefinition<T>, parameters: ParametersDefinition? = null): T = get(
|
||||||
|
definition.factory,
|
||||||
|
parameters
|
||||||
|
)
|
||||||
|
|
||||||
|
fun <T> Scope.get(definition: BeanDefinition<T>, parameters: ParametersDefinition? = null): T = get(
|
||||||
|
definition.primaryType,
|
||||||
|
definition.qualifier,
|
||||||
|
parameters
|
||||||
|
)
|
||||||
|
|
||||||
|
fun <T> Scope.get(definition: InstanceFactory<T>, parameters: ParametersDefinition? = null): T = get(
|
||||||
|
definition.beanDefinition,
|
||||||
|
parameters
|
||||||
|
)
|
||||||
|
|
||||||
|
fun <T> Scope.get(definition: KoinDefinition<T>, parameters: ParametersDefinition? = null): T = get(
|
||||||
|
definition.factory,
|
||||||
|
parameters
|
||||||
|
)
|
@@ -0,0 +1,26 @@
|
|||||||
|
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 GenerateGenericKoinDefinition(
|
||||||
|
val name: String,
|
||||||
|
val nullable: Boolean = true,
|
||||||
|
val generateSingle: Boolean = true,
|
||||||
|
val generateFactory: Boolean = true
|
||||||
|
)
|
27
koin/src/jvmMain/kotlin/LazyInject.kt
Normal file
27
koin/src/jvmMain/kotlin/LazyInject.kt
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package dev.inmo.micro_utils.koin
|
||||||
|
|
||||||
|
import org.koin.core.parameter.ParametersDefinition
|
||||||
|
import org.koin.core.qualifier.Qualifier
|
||||||
|
import org.koin.java.KoinJavaComponent
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
|
fun <T> lazyInject(
|
||||||
|
kClassFactory: () -> KClass<*>,
|
||||||
|
qualifier: Qualifier? = null,
|
||||||
|
parameters: ParametersDefinition? = null
|
||||||
|
): Lazy<T> {
|
||||||
|
return lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
|
||||||
|
KoinJavaComponent.get(kClassFactory().java, qualifier, parameters)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T> lazyInject(
|
||||||
|
kClass: KClass<*>,
|
||||||
|
qualifier: Qualifier? = null,
|
||||||
|
parameters: ParametersDefinition? = null
|
||||||
|
): Lazy<T> = lazyInject({ kClass }, qualifier, parameters)
|
||||||
|
|
||||||
|
inline fun <reified T> lazyInject(
|
||||||
|
qualifier: Qualifier? = null,
|
||||||
|
noinline parameters: ParametersDefinition? = null
|
||||||
|
): Lazy<T> = lazyInject(T::class, qualifier, parameters)
|
@@ -1,6 +1,6 @@
|
|||||||
package dev.inmo.micro_utils.ktor.common
|
package dev.inmo.micro_utils.ktor.common
|
||||||
|
|
||||||
import com.soywiz.klock.DateTime
|
import korlibs.time.DateTime
|
||||||
|
|
||||||
typealias FromToDateTime = Pair<DateTime?, DateTime?>
|
typealias FromToDateTime = Pair<DateTime?, DateTime?>
|
||||||
|
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
package dev.inmo.micro_utils.ktor.server
|
package dev.inmo.micro_utils.ktor.server
|
||||||
|
|
||||||
import com.soywiz.klock.DateTime
|
import korlibs.time.DateTime
|
||||||
import dev.inmo.micro_utils.ktor.common.FromToDateTime
|
import dev.inmo.micro_utils.ktor.common.FromToDateTime
|
||||||
import io.ktor.http.Parameters
|
import io.ktor.http.Parameters
|
||||||
|
|
||||||
|
@@ -12,10 +12,22 @@ suspend fun ApplicationCall.getParameterOrSendError(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun ApplicationCall.getParametersOrSendError(
|
||||||
|
field: String
|
||||||
|
) = parameters.getAll(field).also {
|
||||||
|
if (it == null) {
|
||||||
|
respond(HttpStatusCode.BadRequest, "Request must contains $field")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun ApplicationCall.getQueryParameter(
|
fun ApplicationCall.getQueryParameter(
|
||||||
field: String
|
field: String
|
||||||
) = request.queryParameters[field]
|
) = request.queryParameters[field]
|
||||||
|
|
||||||
|
fun ApplicationCall.getQueryParameters(
|
||||||
|
field: String
|
||||||
|
) = request.queryParameters.getAll(field)
|
||||||
|
|
||||||
suspend fun ApplicationCall.getQueryParameterOrSendError(
|
suspend fun ApplicationCall.getQueryParameterOrSendError(
|
||||||
field: String
|
field: String
|
||||||
) = getQueryParameter(field).also {
|
) = getQueryParameter(field).also {
|
||||||
@@ -23,3 +35,11 @@ suspend fun ApplicationCall.getQueryParameterOrSendError(
|
|||||||
respond(HttpStatusCode.BadRequest, "Request query parameters must contains $field")
|
respond(HttpStatusCode.BadRequest, "Request query parameters must contains $field")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun ApplicationCall.getQueryParametersOrSendError(
|
||||||
|
field: String
|
||||||
|
) = getQueryParameters(field).also {
|
||||||
|
if (it == null) {
|
||||||
|
respond(HttpStatusCode.BadRequest, "Request query parameters must contains $field")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -15,6 +15,8 @@ kotlin {
|
|||||||
browser()
|
browser()
|
||||||
nodejs()
|
nodejs()
|
||||||
}
|
}
|
||||||
|
linuxX64()
|
||||||
|
mingwX64()
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
commonMain {
|
commonMain {
|
6
renovate.json
Normal file
6
renovate.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||||
|
"extends": [
|
||||||
|
"config:base"
|
||||||
|
]
|
||||||
|
}
|
@@ -21,9 +21,9 @@ open class AutoRecacheWriteCRUDRepo<RegisteredObject, Id, InputObject>(
|
|||||||
protected val idGetter: (RegisteredObject) -> Id
|
protected val idGetter: (RegisteredObject) -> Id
|
||||||
) : WriteCRUDRepo<RegisteredObject, Id, InputObject>, FallbackCacheRepo {
|
) : WriteCRUDRepo<RegisteredObject, Id, InputObject>, FallbackCacheRepo {
|
||||||
override val deletedObjectsIdsFlow: Flow<Id>
|
override val deletedObjectsIdsFlow: Flow<Id>
|
||||||
get() = (originalRepo.deletedObjectsIdsFlow + kvCache.onValueRemoved).distinctUntilChanged()
|
get() = (originalRepo.deletedObjectsIdsFlow).distinctUntilChanged()
|
||||||
override val newObjectsFlow: Flow<RegisteredObject>
|
override val newObjectsFlow: Flow<RegisteredObject>
|
||||||
get() = (originalRepo.newObjectsFlow + kvCache.onNewValue.map { it.second }).distinctUntilChanged()
|
get() = (originalRepo.newObjectsFlow).distinctUntilChanged()
|
||||||
override val updatedObjectsFlow: Flow<RegisteredObject>
|
override val updatedObjectsFlow: Flow<RegisteredObject>
|
||||||
get() = originalRepo.updatedObjectsFlow
|
get() = originalRepo.updatedObjectsFlow
|
||||||
|
|
||||||
|
@@ -17,10 +17,10 @@ open class AutoRecacheWriteKeyValueRepo<Id, RegisteredObject>(
|
|||||||
protected val kvCache: FullKVCache<Id, RegisteredObject> = FullKVCache()
|
protected val kvCache: FullKVCache<Id, RegisteredObject> = FullKVCache()
|
||||||
) : WriteKeyValueRepo<Id, RegisteredObject>, FallbackCacheRepo {
|
) : WriteKeyValueRepo<Id, RegisteredObject>, FallbackCacheRepo {
|
||||||
override val onValueRemoved: Flow<Id>
|
override val onValueRemoved: Flow<Id>
|
||||||
get() = (originalRepo.onValueRemoved + kvCache.onValueRemoved).distinctUntilChanged()
|
get() = (originalRepo.onValueRemoved).distinctUntilChanged()
|
||||||
|
|
||||||
override val onNewValue: Flow<Pair<Id, RegisteredObject>>
|
override val onNewValue: Flow<Pair<Id, RegisteredObject>>
|
||||||
get() = (originalRepo.onNewValue + kvCache.onNewValue).distinctUntilChanged()
|
get() = (originalRepo.onNewValue).distinctUntilChanged()
|
||||||
|
|
||||||
private val onRemovingUpdatesListeningJob = originalRepo.onValueRemoved.subscribeSafelyWithoutExceptions(scope) {
|
private val onRemovingUpdatesListeningJob = originalRepo.onValueRemoved.subscribeSafelyWithoutExceptions(scope) {
|
||||||
kvCache.unset(it)
|
kvCache.unset(it)
|
||||||
|
@@ -7,6 +7,7 @@ import dev.inmo.micro_utils.pagination.utils.doForAllWithNextPaging
|
|||||||
import dev.inmo.micro_utils.repos.WriteKeyValuesRepo
|
import dev.inmo.micro_utils.repos.WriteKeyValuesRepo
|
||||||
import dev.inmo.micro_utils.repos.cache.cache.FullKVCache
|
import dev.inmo.micro_utils.repos.cache.cache.FullKVCache
|
||||||
import dev.inmo.micro_utils.repos.cache.FallbackCacheRepo
|
import dev.inmo.micro_utils.repos.cache.FallbackCacheRepo
|
||||||
|
import dev.inmo.micro_utils.repos.pagination.maxPagePagination
|
||||||
import dev.inmo.micro_utils.repos.set
|
import dev.inmo.micro_utils.repos.set
|
||||||
import dev.inmo.micro_utils.repos.unset
|
import dev.inmo.micro_utils.repos.unset
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
@@ -24,7 +25,7 @@ open class AutoRecacheWriteKeyValuesRepo<Id, RegisteredObject>(
|
|||||||
override val onNewValue: Flow<Pair<Id, RegisteredObject>>
|
override val onNewValue: Flow<Pair<Id, RegisteredObject>>
|
||||||
get() = originalRepo.onNewValue
|
get() = originalRepo.onNewValue
|
||||||
override val onDataCleared: Flow<Id>
|
override val onDataCleared: Flow<Id>
|
||||||
get() = (originalRepo.onDataCleared + kvCache.onValueRemoved).distinctUntilChanged()
|
get() = (originalRepo.onDataCleared).distinctUntilChanged()
|
||||||
|
|
||||||
private val onDataClearedListeningJob = originalRepo.onDataCleared.subscribeSafelyWithoutExceptions(scope) {
|
private val onDataClearedListeningJob = originalRepo.onDataCleared.subscribeSafelyWithoutExceptions(scope) {
|
||||||
kvCache.unset(it)
|
kvCache.unset(it)
|
||||||
@@ -50,7 +51,7 @@ open class AutoRecacheWriteKeyValuesRepo<Id, RegisteredObject>(
|
|||||||
|
|
||||||
override suspend fun clearWithValue(v: RegisteredObject) {
|
override suspend fun clearWithValue(v: RegisteredObject) {
|
||||||
originalRepo.clearWithValue(v)
|
originalRepo.clearWithValue(v)
|
||||||
doForAllWithNextPaging(FirstPagePagination(kvCache.count().takeIf { it < Int.MAX_VALUE } ?.toInt() ?: Int.MAX_VALUE)) {
|
doForAllWithNextPaging(kvCache.maxPagePagination()) {
|
||||||
kvCache.keys(it).also {
|
kvCache.keys(it).also {
|
||||||
it.results.forEach { id ->
|
it.results.forEach { id ->
|
||||||
kvCache.get(id) ?.takeIf { it.contains(v) } ?.let {
|
kvCache.get(id) ?.takeIf { it.contains(v) } ?.let {
|
||||||
@@ -73,6 +74,19 @@ open class AutoRecacheWriteKeyValuesRepo<Id, RegisteredObject>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun removeWithValue(v: RegisteredObject) {
|
||||||
|
originalRepo.removeWithValue(v)
|
||||||
|
doForAllWithNextPaging(kvCache.maxPagePagination()) {
|
||||||
|
kvCache.keys(it).also {
|
||||||
|
it.results.forEach { id ->
|
||||||
|
kvCache.get(id) ?.takeIf { it.contains(v) } ?.let {
|
||||||
|
kvCache.set(id, it - v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun add(toAdd: Map<Id, List<RegisteredObject>>) {
|
override suspend fun add(toAdd: Map<Id, List<RegisteredObject>>) {
|
||||||
originalRepo.add(toAdd)
|
originalRepo.add(toAdd)
|
||||||
toAdd.forEach { (k, v) ->
|
toAdd.forEach { (k, v) ->
|
||||||
|
@@ -102,8 +102,15 @@ open class FullCRUDCacheRepo<ObjectType, IdType, InputValueType>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun <ObjectType, IdType, InputType> CRUDRepo<ObjectType, IdType, InputType>.fullyCached(
|
||||||
|
kvCache: FullKVCache<IdType, ObjectType> = FullKVCache(),
|
||||||
|
scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
|
||||||
|
idGetter: (ObjectType) -> IdType
|
||||||
|
) = FullCRUDCacheRepo(this, kvCache, scope, idGetter)
|
||||||
|
|
||||||
|
@Deprecated("Renamed", ReplaceWith("this.fullyCached(kvCache, scope, idGetter)", "dev.inmo.micro_utils.repos.cache.full.fullyCached"))
|
||||||
fun <ObjectType, IdType, InputType> CRUDRepo<ObjectType, IdType, InputType>.cached(
|
fun <ObjectType, IdType, InputType> CRUDRepo<ObjectType, IdType, InputType>.cached(
|
||||||
kvCache: FullKVCache<IdType, ObjectType>,
|
kvCache: FullKVCache<IdType, ObjectType>,
|
||||||
scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
|
scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
|
||||||
idGetter: (ObjectType) -> IdType
|
idGetter: (ObjectType) -> IdType
|
||||||
) = FullCRUDCacheRepo(this, kvCache, scope, idGetter)
|
) = fullyCached(kvCache, scope, idGetter)
|
||||||
|
@@ -117,7 +117,13 @@ open class FullKeyValueCacheRepo<Key,Value>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun <Key, Value> KeyValueRepo<Key, Value>.fullyCached(
|
||||||
|
kvCache: FullKVCache<Key, Value> = FullKVCache(),
|
||||||
|
scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
|
||||||
|
) = FullKeyValueCacheRepo(this, kvCache, scope)
|
||||||
|
|
||||||
|
@Deprecated("Renamed", ReplaceWith("this.fullyCached(kvCache, scope)", "dev.inmo.micro_utils.repos.cache.full.fullyCached"))
|
||||||
fun <Key, Value> KeyValueRepo<Key, Value>.cached(
|
fun <Key, Value> KeyValueRepo<Key, Value>.cached(
|
||||||
kvCache: FullKVCache<Key, Value>,
|
kvCache: FullKVCache<Key, Value>,
|
||||||
scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
|
scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
|
||||||
) = FullKeyValueCacheRepo(this, kvCache, scope)
|
) = fullyCached(kvCache, scope)
|
||||||
|
@@ -157,8 +157,18 @@ open class FullKeyValuesCacheRepo<Key,Value>(
|
|||||||
override suspend fun invalidate() {
|
override suspend fun invalidate() {
|
||||||
kvCache.actualizeAll(parentRepo)
|
kvCache.actualizeAll(parentRepo)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun removeWithValue(v: Value) {
|
||||||
|
super<FullWriteKeyValuesCacheRepo>.removeWithValue(v)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun <Key, Value> KeyValuesRepo<Key, Value>.fullyCached(
|
||||||
|
kvCache: FullKVCache<Key, List<Value>> = FullKVCache(),
|
||||||
|
scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
|
||||||
|
) = FullKeyValuesCacheRepo(this, kvCache, scope)
|
||||||
|
|
||||||
|
@Deprecated("Renamed", ReplaceWith("this.fullyCached(kvCache, scope)", "dev.inmo.micro_utils.repos.cache.full.fullyCached"))
|
||||||
fun <Key, Value> KeyValuesRepo<Key, Value>.caching(
|
fun <Key, Value> KeyValuesRepo<Key, Value>.caching(
|
||||||
kvCache: FullKVCache<Key, List<Value>>,
|
kvCache: FullKVCache<Key, List<Value>>,
|
||||||
scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
|
scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
package dev.inmo.micro_utils.repos
|
package dev.inmo.micro_utils.repos
|
||||||
|
|
||||||
import dev.inmo.micro_utils.pagination.*
|
import dev.inmo.micro_utils.pagination.*
|
||||||
|
import dev.inmo.micro_utils.pagination.utils.doForAllWithNextPaging
|
||||||
import dev.inmo.micro_utils.pagination.utils.getAllWithNextPaging
|
import dev.inmo.micro_utils.pagination.utils.getAllWithNextPaging
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
@@ -44,9 +45,23 @@ interface WriteKeyValuesRepo<Key, Value> : Repo {
|
|||||||
|
|
||||||
suspend fun add(toAdd: Map<Key, List<Value>>)
|
suspend fun add(toAdd: Map<Key, List<Value>>)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes [Value]s by passed [Key]s without full clear of all data by [Key]
|
||||||
|
*/
|
||||||
suspend fun remove(toRemove: Map<Key, List<Value>>)
|
suspend fun remove(toRemove: Map<Key, List<Value>>)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes [v] without full clear of all data by [Key]s with [v]
|
||||||
|
*/
|
||||||
|
suspend fun removeWithValue(v: Value)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fully clear all data by [k]
|
||||||
|
*/
|
||||||
suspend fun clear(k: Key)
|
suspend fun clear(k: Key)
|
||||||
|
/**
|
||||||
|
* Clear [v] **with** full clear of all data by [Key]s with [v]
|
||||||
|
*/
|
||||||
suspend fun clearWithValue(v: Value)
|
suspend fun clearWithValue(v: Value)
|
||||||
|
|
||||||
suspend fun set(toSet: Map<Key, List<Value>>) {
|
suspend fun set(toSet: Map<Key, List<Value>>) {
|
||||||
@@ -100,6 +115,21 @@ interface KeyValuesRepo<Key, Value> : ReadKeyValuesRepo<Key, Value>, WriteKeyVal
|
|||||||
keysResult.currentPageIfNotEmpty()
|
keysResult.currentPageIfNotEmpty()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
suspend override fun removeWithValue(v: Value) {
|
||||||
|
val toRemove = mutableMapOf<Key, List<Value>>()
|
||||||
|
|
||||||
|
doForAllWithNextPaging {
|
||||||
|
keys(it).also {
|
||||||
|
it.results.forEach {
|
||||||
|
if (contains(it, v)) {
|
||||||
|
toRemove[it] = listOf(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
remove(toRemove)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
typealias OneToManyKeyValueRepo<Key,Value> = KeyValuesRepo<Key, Value>
|
typealias OneToManyKeyValueRepo<Key,Value> = KeyValuesRepo<Key, Value>
|
||||||
|
|
||||||
|
@@ -97,6 +97,8 @@ open class MapperWriteKeyValuesRepo<FromKey, FromValue, ToKey, ToValue>(
|
|||||||
}.toMap()
|
}.toMap()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
override suspend fun removeWithValue(v: FromValue) = to.removeWithValue(v.toOutValue())
|
||||||
|
|
||||||
override suspend fun set(toSet: Map<FromKey, List<FromValue>>) {
|
override suspend fun set(toSet: Map<FromKey, List<FromValue>>) {
|
||||||
to.set(
|
to.set(
|
||||||
toSet.map { (k, vs) -> k.toOutKey() to vs.map { v -> v.toOutValue() } }.toMap()
|
toSet.map { (k, vs) -> k.toOutKey() to vs.map { v -> v.toOutValue() } }.toMap()
|
||||||
|
@@ -4,6 +4,7 @@ import dev.inmo.micro_utils.pagination.FirstPagePagination
|
|||||||
import dev.inmo.micro_utils.pagination.utils.doForAllWithNextPaging
|
import dev.inmo.micro_utils.pagination.utils.doForAllWithNextPaging
|
||||||
import dev.inmo.micro_utils.repos.KeyValueRepo
|
import dev.inmo.micro_utils.repos.KeyValueRepo
|
||||||
import dev.inmo.micro_utils.repos.KeyValuesRepo
|
import dev.inmo.micro_utils.repos.KeyValuesRepo
|
||||||
|
import dev.inmo.micro_utils.repos.set
|
||||||
import dev.inmo.micro_utils.repos.unset
|
import dev.inmo.micro_utils.repos.unset
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
@@ -60,6 +61,24 @@ open class KeyValuesFromKeyValueRepo<Key, Value, ValuesIterable : Iterable<Value
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun removeWithValue(v: Value) {
|
||||||
|
val toRemove = mutableMapOf<Key, List<Value>>()
|
||||||
|
|
||||||
|
doForAllWithNextPaging {
|
||||||
|
original.keys(it).also {
|
||||||
|
it.results.forEach {
|
||||||
|
val data = original.get(it) ?: return@forEach
|
||||||
|
|
||||||
|
if (v in data) {
|
||||||
|
toRemove[it] = listOf(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
remove(toRemove)
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun add(toAdd: Map<Key, List<Value>>) {
|
override suspend fun add(toAdd: Map<Key, List<Value>>) {
|
||||||
original.set(
|
original.set(
|
||||||
toAdd.mapNotNull { (k, adding) ->
|
toAdd.mapNotNull { (k, adding) ->
|
||||||
|
@@ -6,6 +6,7 @@ import dev.inmo.micro_utils.common.mapNotNullA
|
|||||||
import dev.inmo.micro_utils.pagination.*
|
import dev.inmo.micro_utils.pagination.*
|
||||||
import dev.inmo.micro_utils.pagination.utils.reverse
|
import dev.inmo.micro_utils.pagination.utils.reverse
|
||||||
import dev.inmo.micro_utils.repos.*
|
import dev.inmo.micro_utils.repos.*
|
||||||
|
import dev.inmo.micro_utils.repos.crud.asId
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import kotlinx.coroutines.flow.asSharedFlow
|
import kotlinx.coroutines.flow.asSharedFlow
|
||||||
@@ -260,6 +261,19 @@ class OneToManyAndroidRepo<Key, Value>(
|
|||||||
_onValueRemoved.emit(k to v)
|
_onValueRemoved.emit(k to v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun removeWithValue(v: Value) {
|
||||||
|
helper.blockingWritableTransaction {
|
||||||
|
val keys = select(tableName, idColumnArray, "$valueColumnName=?", arrayOf(v.valueAsString())).map {
|
||||||
|
it.asId.keyFromString()
|
||||||
|
}
|
||||||
|
keys.filter {
|
||||||
|
delete(tableName, "$idColumnName=? AND $valueColumnName=?", arrayOf(it.keyAsString(), v.valueAsString())) > 0
|
||||||
|
}
|
||||||
|
}.forEach { k ->
|
||||||
|
_onValueRemoved.emit(k to v)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <Key, Value> OneToManyAndroidRepo(
|
fun <Key, Value> OneToManyAndroidRepo(
|
||||||
|
@@ -5,6 +5,7 @@ import dev.inmo.micro_utils.repos.exposed.ColumnAllocator
|
|||||||
import kotlinx.coroutines.flow.*
|
import kotlinx.coroutines.flow.*
|
||||||
import org.jetbrains.exposed.sql.*
|
import org.jetbrains.exposed.sql.*
|
||||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||||
|
import org.jetbrains.exposed.sql.SqlExpressionBuilder.inList
|
||||||
import org.jetbrains.exposed.sql.transactions.transaction
|
import org.jetbrains.exposed.sql.transactions.transaction
|
||||||
|
|
||||||
typealias ExposedOneToManyKeyValueRepo1<Key, Value> = ExposedKeyValuesRepo<Key, Value>
|
typealias ExposedOneToManyKeyValueRepo1<Key, Value> = ExposedKeyValuesRepo<Key, Value>
|
||||||
@@ -66,9 +67,36 @@ open class ExposedKeyValuesRepo<Key, Value>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun removeWithValue(v: Value) {
|
||||||
|
transaction(database) {
|
||||||
|
val keys = select { selectByValue(v) }.map { it.asKey }
|
||||||
|
deleteWhere { SqlExpressionBuilder.selectByValue(v) }
|
||||||
|
keys
|
||||||
|
}.forEach {
|
||||||
|
_onValueRemoved.emit(it to v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun clear(k: Key) {
|
override suspend fun clear(k: Key) {
|
||||||
transaction(database) {
|
transaction(database) {
|
||||||
deleteWhere { keyColumn.eq(k) }
|
deleteWhere { keyColumn.eq(k) }
|
||||||
}.also { _onDataCleared.emit(k) }
|
}.also { _onDataCleared.emit(k) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun clearWithValue(v: Value) {
|
||||||
|
transaction(database) {
|
||||||
|
val toClear = select { selectByValue(v) }
|
||||||
|
.asSequence()
|
||||||
|
.map { it.asKey to it.asObject }
|
||||||
|
.groupBy { it.first }
|
||||||
|
.mapValues { it.value.map { it.second } }
|
||||||
|
deleteWhere { keyColumn.inList(toClear.keys) }
|
||||||
|
toClear
|
||||||
|
}.forEach {
|
||||||
|
it.value.forEach { v ->
|
||||||
|
_onValueRemoved.emit(it.key to v)
|
||||||
|
}
|
||||||
|
_onDataCleared.emit(it.key)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -87,13 +87,27 @@ class MapWriteKeyValuesRepo<Key, Value>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun removeWithValue(v: Value) {
|
||||||
|
map.forEach { (k, values) ->
|
||||||
|
if (values.remove(v)) {
|
||||||
|
_onValueRemoved.emit(k to v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun clear(k: Key) {
|
override suspend fun clear(k: Key) {
|
||||||
map.remove(k) ?.also { _onDataCleared.emit(k) }
|
map.remove(k) ?.also { _onDataCleared.emit(k) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun clearWithValue(v: Value) {
|
override suspend fun clearWithValue(v: Value) {
|
||||||
map.forEach { (k, values) ->
|
map.filter { (_, values) ->
|
||||||
if (values.remove(v)) _onValueRemoved.emit(k to v)
|
values.contains(v)
|
||||||
|
}.forEach {
|
||||||
|
map.remove(it.key) ?.onEach { v ->
|
||||||
|
_onValueRemoved.emit(it.key to v)
|
||||||
|
} ?.also { _ ->
|
||||||
|
_onDataCleared.emit(it.key)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -47,6 +47,17 @@ class KtorWriteKeyValuesRepoClient<Key : Any, Value : Any>(
|
|||||||
}.throwOnUnsuccess { "Unable to remove $toRemove" }
|
}.throwOnUnsuccess { "Unable to remove $toRemove" }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(InternalAPI::class)
|
||||||
|
override suspend fun removeWithValue(v: Value) {
|
||||||
|
httpClient.post(
|
||||||
|
buildStandardUrl(baseUrl, removeWithValueRoute)
|
||||||
|
) {
|
||||||
|
body = v
|
||||||
|
bodyType = valueTypeInfo
|
||||||
|
contentType(contentType)
|
||||||
|
}.throwOnUnsuccess { "Unable to remove $v" }
|
||||||
|
}
|
||||||
|
|
||||||
@OptIn(InternalAPI::class)
|
@OptIn(InternalAPI::class)
|
||||||
override suspend fun clear(k: Key) {
|
override suspend fun clear(k: Key) {
|
||||||
httpClient.post(
|
httpClient.post(
|
||||||
|
@@ -13,6 +13,7 @@ const val onDataClearedRoute = "onDataCleared"
|
|||||||
|
|
||||||
const val addRoute = "add"
|
const val addRoute = "add"
|
||||||
const val removeRoute = "remove"
|
const val removeRoute = "remove"
|
||||||
|
const val removeWithValueRoute = "removeWithValue"
|
||||||
const val clearRoute = "clear"
|
const val clearRoute = "clear"
|
||||||
const val clearWithValueRoute = "clearWithValue"
|
const val clearWithValueRoute = "clearWithValue"
|
||||||
const val setRoute = "set"
|
const val setRoute = "set"
|
||||||
|
@@ -46,6 +46,11 @@ inline fun <reified Key : Any, reified Value : Any> Route.configureWriteKeyValue
|
|||||||
call.respond(HttpStatusCode.OK)
|
call.respond(HttpStatusCode.OK)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
post(removeWithValueRoute) {
|
||||||
|
originalRepo.removeWithValue(call.receive())
|
||||||
|
call.respond(HttpStatusCode.OK)
|
||||||
|
}
|
||||||
|
|
||||||
post(clearRoute) {
|
post(clearRoute) {
|
||||||
originalRepo.clear(call.receive())
|
originalRepo.clear(call.receive())
|
||||||
call.respond(HttpStatusCode.OK)
|
call.respond(HttpStatusCode.OK)
|
||||||
|
@@ -2,15 +2,17 @@ plugins {
|
|||||||
id "org.jetbrains.kotlin.multiplatform"
|
id "org.jetbrains.kotlin.multiplatform"
|
||||||
id "org.jetbrains.kotlin.plugin.serialization"
|
id "org.jetbrains.kotlin.plugin.serialization"
|
||||||
id "application"
|
id "application"
|
||||||
|
id "com.google.devtools.ksp"
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$mppJsAndJavaProjectPresetPath"
|
apply from: "$mppJvmJsLinuxMingwProjectPresetPath"
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
sourceSets {
|
sourceSets {
|
||||||
commonMain {
|
commonMain {
|
||||||
dependencies {
|
dependencies {
|
||||||
api internalProject("micro_utils.startup.plugin")
|
api internalProject("micro_utils.startup.plugin")
|
||||||
|
api internalProject("micro_utils.koin")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
commonTest {
|
commonTest {
|
||||||
@@ -29,3 +31,10 @@ java {
|
|||||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||||
targetCompatibility = JavaVersion.VERSION_1_8
|
targetCompatibility = JavaVersion.VERSION_1_8
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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: StartLauncherPlugin.kt
|
||||||
|
package dev.inmo.micro_utils.startup.launcher
|
||||||
|
|
||||||
|
import kotlin.Boolean
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
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 "baseJsonProvider"
|
||||||
|
*/
|
||||||
|
public val Scope.baseJsonProvider: Json?
|
||||||
|
get() = getOrNull(named("baseJsonProvider"))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Definition by key "baseJsonProvider"
|
||||||
|
*/
|
||||||
|
public val Koin.baseJsonProvider: Json?
|
||||||
|
get() = getOrNull(named("baseJsonProvider"))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will register [definition] with [org.koin.core.module.Module.single] and key "baseJsonProvider"
|
||||||
|
*/
|
||||||
|
public fun Module.baseJsonProviderSingle(createdAtStart: Boolean = false, definition: Definition<Json>):
|
||||||
|
KoinDefinition<Json> = single(named("baseJsonProvider"), createdAtStart = createdAtStart, definition
|
||||||
|
= definition)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will register [definition] with [org.koin.core.module.Module.factory] and key "baseJsonProvider"
|
||||||
|
*/
|
||||||
|
public fun Module.baseJsonProviderFactory(definition: Definition<Json>): KoinDefinition<Json> =
|
||||||
|
factory(named("baseJsonProvider"), definition = definition)
|
@@ -1,17 +0,0 @@
|
|||||||
package dev.inmo.micro_utils.startup.launcher
|
|
||||||
|
|
||||||
import dev.inmo.micro_utils.startup.launcher.StartLauncherPlugin.setupDI
|
|
||||||
import kotlinx.serialization.json.JsonObject
|
|
||||||
import org.koin.core.KoinApplication
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Will create [KoinApplication], init, load modules using [StartLauncherPlugin] and start plugins using the same base
|
|
||||||
* plugin
|
|
||||||
*
|
|
||||||
* @param rawConfig It is expected that this [JsonObject] will contain serialized [Config] ([StartLauncherPlugin] will
|
|
||||||
* deserialize it in its [StartLauncherPlugin.setupDI]
|
|
||||||
*/
|
|
||||||
@Deprecated("Fully replaced with StartLauncherPlugin#start", ReplaceWith("StartLauncherPlugin.start(rawConfig)", "dev.inmo.micro_utils.startup.launcher.StartLauncherPlugin"))
|
|
||||||
suspend fun start(rawConfig: JsonObject) {
|
|
||||||
StartLauncherPlugin.start(rawConfig)
|
|
||||||
}
|
|
@@ -1,9 +1,11 @@
|
|||||||
|
@file:GenerateKoinDefinition("baseJsonProvider", Json::class)
|
||||||
package dev.inmo.micro_utils.startup.launcher
|
package dev.inmo.micro_utils.startup.launcher
|
||||||
|
|
||||||
import dev.inmo.kslog.common.i
|
import dev.inmo.kslog.common.i
|
||||||
import dev.inmo.kslog.common.taggedLogger
|
import dev.inmo.kslog.common.taggedLogger
|
||||||
import dev.inmo.kslog.common.w
|
import dev.inmo.kslog.common.w
|
||||||
import dev.inmo.micro_utils.coroutines.runCatchingSafely
|
import dev.inmo.micro_utils.coroutines.runCatchingSafely
|
||||||
|
import dev.inmo.micro_utils.koin.annotations.GenerateKoinDefinition
|
||||||
import dev.inmo.micro_utils.startup.launcher.StartLauncherPlugin.setupDI
|
import dev.inmo.micro_utils.startup.launcher.StartLauncherPlugin.setupDI
|
||||||
import dev.inmo.micro_utils.startup.launcher.StartLauncherPlugin.startPlugin
|
import dev.inmo.micro_utils.startup.launcher.StartLauncherPlugin.startPlugin
|
||||||
import dev.inmo.micro_utils.startup.plugin.StartPlugin
|
import dev.inmo.micro_utils.startup.plugin.StartPlugin
|
||||||
@@ -13,9 +15,10 @@ import kotlinx.coroutines.joinAll
|
|||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.serialization.SerialFormat
|
import kotlinx.serialization.SerialFormat
|
||||||
import kotlinx.serialization.StringFormat
|
import kotlinx.serialization.StringFormat
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
import kotlinx.serialization.json.JsonObject
|
import kotlinx.serialization.json.JsonObject
|
||||||
import kotlinx.serialization.json.decodeFromJsonElement
|
|
||||||
import kotlinx.serialization.json.jsonObject
|
import kotlinx.serialization.json.jsonObject
|
||||||
|
import kotlinx.serialization.modules.SerializersModule
|
||||||
import org.koin.core.Koin
|
import org.koin.core.Koin
|
||||||
import org.koin.core.KoinApplication
|
import org.koin.core.KoinApplication
|
||||||
import org.koin.core.context.startKoin
|
import org.koin.core.context.startKoin
|
||||||
@@ -35,7 +38,20 @@ object StartLauncherPlugin : StartPlugin {
|
|||||||
single { rawJsonObject }
|
single { rawJsonObject }
|
||||||
single { config }
|
single { config }
|
||||||
single { CoroutineScope(Dispatchers.Default) }
|
single { CoroutineScope(Dispatchers.Default) }
|
||||||
single { defaultJson } binds arrayOf(StringFormat::class, SerialFormat::class)
|
single {
|
||||||
|
val serializersModules = getAll<SerializersModule>().distinct()
|
||||||
|
val baseJson = baseJsonProvider ?: defaultJson
|
||||||
|
if (serializersModules.isEmpty()) {
|
||||||
|
baseJson
|
||||||
|
} else {
|
||||||
|
Json(baseJson) {
|
||||||
|
serializersModule = SerializersModule {
|
||||||
|
include(baseJson.serializersModule)
|
||||||
|
serializersModules.forEach { include(it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} binds arrayOf(StringFormat::class, SerialFormat::class)
|
||||||
|
|
||||||
includes(
|
includes(
|
||||||
config.plugins.mapNotNull {
|
config.plugins.mapNotNull {
|
||||||
|
@@ -1,25 +0,0 @@
|
|||||||
package dev.inmo.micro_utils.startup.launcher
|
|
||||||
|
|
||||||
import dev.inmo.kslog.common.KSLog
|
|
||||||
import dev.inmo.kslog.common.i
|
|
||||||
import kotlinx.serialization.json.JsonObject
|
|
||||||
import kotlinx.serialization.json.jsonObject
|
|
||||||
|
|
||||||
@Deprecated("Useless due to including of the same functionality in StrtLauncherPlugin")
|
|
||||||
object PluginsStarter {
|
|
||||||
init {
|
|
||||||
KSLog.default = KSLog("Launcher")
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* It is expected that you have registered all the [dev.inmo.micro_utils.startup.plugin.StartPlugin]s of your JS
|
|
||||||
* app inside of [dev.inmo.micro_utils.startup.plugin.StartPluginSerializer] using its
|
|
||||||
* [dev.inmo.micro_utils.startup.plugin.StartPluginSerializer.registerPlugin] method
|
|
||||||
*/
|
|
||||||
suspend fun startPlugins(json: JsonObject) = StartLauncherPlugin.start(json)
|
|
||||||
/**
|
|
||||||
* Will convert [config] to [JsonObject] with auto registration of [dev.inmo.micro_utils.startup.plugin.StartPlugin]s
|
|
||||||
* in [dev.inmo.micro_utils.startup.plugin.StartPluginSerializer]
|
|
||||||
*/
|
|
||||||
suspend fun startPlugins(config: Config) = StartLauncherPlugin.start(config)
|
|
||||||
}
|
|
@@ -3,7 +3,7 @@ plugins {
|
|||||||
id "org.jetbrains.kotlin.plugin.serialization"
|
id "org.jetbrains.kotlin.plugin.serialization"
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$mppJsAndJavaProjectPresetPath"
|
apply from: "$mppJvmJsLinuxMingwProjectPresetPath"
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
sourceSets {
|
sourceSets {
|
||||||
@@ -21,5 +21,15 @@ kotlin {
|
|||||||
api libs.uuid
|
api libs.uuid
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
linuxX64Main {
|
||||||
|
dependencies {
|
||||||
|
api libs.uuid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mingwX64Main {
|
||||||
|
dependencies {
|
||||||
|
api libs.uuid
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,36 @@
|
|||||||
|
package dev.inmo.micro_utils.startup.plugin
|
||||||
|
|
||||||
|
import com.benasher44.uuid.uuid4
|
||||||
|
import kotlinx.serialization.KSerializer
|
||||||
|
import kotlinx.serialization.builtins.serializer
|
||||||
|
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||||
|
import kotlinx.serialization.encoding.Decoder
|
||||||
|
import kotlinx.serialization.encoding.Encoder
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
|
actual object StartPluginSerializer : KSerializer<StartPlugin> {
|
||||||
|
private val registeredPlugins = mutableMapOf<String, StartPlugin>()
|
||||||
|
private val registeredPluginsByPlugin = mutableMapOf<StartPlugin, String>()
|
||||||
|
override val descriptor: SerialDescriptor = String.serializer().descriptor
|
||||||
|
|
||||||
|
override fun deserialize(decoder: Decoder): StartPlugin {
|
||||||
|
val name = decoder.decodeString()
|
||||||
|
return registeredPlugins[name] ?: error("Unable to find startup plugin for $name")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun serialize(encoder: Encoder, value: StartPlugin) {
|
||||||
|
val name = registeredPluginsByPlugin[value] ?: uuid4().toString().also {
|
||||||
|
registeredPlugins[it] = value
|
||||||
|
registeredPluginsByPlugin[value] = it
|
||||||
|
}
|
||||||
|
encoder.encodeString(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register plugin inside of this [KSerializer]. Since plugin has been registered, you may use its [name] in any
|
||||||
|
* serialized [dev.inmo.micro_utils.startup.launcher.Config] to retrieve [plugin] you passed here
|
||||||
|
*/
|
||||||
|
fun registerPlugin(name: String, plugin: StartPlugin) {
|
||||||
|
registeredPlugins[name] = plugin
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,36 @@
|
|||||||
|
package dev.inmo.micro_utils.startup.plugin
|
||||||
|
|
||||||
|
import com.benasher44.uuid.uuid4
|
||||||
|
import kotlinx.serialization.KSerializer
|
||||||
|
import kotlinx.serialization.builtins.serializer
|
||||||
|
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||||
|
import kotlinx.serialization.encoding.Decoder
|
||||||
|
import kotlinx.serialization.encoding.Encoder
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
|
actual object StartPluginSerializer : KSerializer<StartPlugin> {
|
||||||
|
private val registeredPlugins = mutableMapOf<String, StartPlugin>()
|
||||||
|
private val registeredPluginsByPlugin = mutableMapOf<StartPlugin, String>()
|
||||||
|
override val descriptor: SerialDescriptor = String.serializer().descriptor
|
||||||
|
|
||||||
|
override fun deserialize(decoder: Decoder): StartPlugin {
|
||||||
|
val name = decoder.decodeString()
|
||||||
|
return registeredPlugins[name] ?: error("Unable to find startup plugin for $name")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun serialize(encoder: Encoder, value: StartPlugin) {
|
||||||
|
val name = registeredPluginsByPlugin[value] ?: uuid4().toString().also {
|
||||||
|
registeredPlugins[it] = value
|
||||||
|
registeredPluginsByPlugin[value] = it
|
||||||
|
}
|
||||||
|
encoder.encodeString(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register plugin inside of this [KSerializer]. Since plugin has been registered, you may use its [name] in any
|
||||||
|
* serialized [dev.inmo.micro_utils.startup.launcher.Config] to retrieve [plugin] you passed here
|
||||||
|
*/
|
||||||
|
fun registerPlugin(name: String, plugin: StartPlugin) {
|
||||||
|
registeredPlugins[name] = plugin
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user