Compare commits

..

42 Commits
0.2.1 ... 0.2.7

Author SHA1 Message Date
64cc42a23b upfill of changelog 2020-11-07 02:27:54 +06:00
325f178763 repos update 2020-11-07 02:25:50 +06:00
fc8d0e52ef start 0.2.7 2020-11-04 18:47:42 +06:00
e58348907e Merge pull request #10 from InsanusMokrassar/0.2.6
0.2.6
2020-11-03 20:08:06 +06:00
eaba9173ae more fixes:) 2020-11-03 20:07:33 +06:00
2f42b30f87 fix for paginate (again) and lastIndexExclusive 2020-11-03 19:43:45 +06:00
35913b95be fixes in pagintion 2020-11-03 18:37:50 +06:00
8023fa1b76 start 0.2.6 2020-11-03 18:35:22 +06:00
4cbe2d1d61 Merge pull request #9 from InsanusMokrassar/0.2.5
0.2.5
2020-11-02 21:44:53 +06:00
740036e8d9 link fix in ktor server 2020-11-02 21:44:12 +06:00
273a7aa9a4 optimize imports 2020-11-02 21:34:49 +06:00
dccb479c3c CoroutineScope#safeActor 2020-11-02 21:32:04 +06:00
6f5c6e5ebe safelyWithoutExceptions 2020-11-02 21:25:33 +06:00
8495cc6263 start 0.2.5 2020-11-02 21:24:20 +06:00
d242d8dcf4 Merge pull request #8 from InsanusMokrassar/0.2.4
0.2.4
2020-11-02 00:19:44 +06:00
864d576e70 fix add test for common diff utils 2020-11-02 00:19:23 +06:00
f0127b018e hotfix 2020-11-02 00:14:25 +06:00
5eb48a58bf diff utils rewriting 2020-11-02 00:12:47 +06:00
b690f68c7f add Flow#subscribe 2020-11-01 00:54:07 +06:00
7f19b83828 deprecate broadcast channel flow extensions 2020-11-01 00:43:47 +06:00
98a1ec82db update serialization version 2020-11-01 00:41:57 +06:00
63313ff964 start 0.2.4 2020-11-01 00:41:02 +06:00
210e32bed4 Merge pull request #7 from InsanusMokrassar/0.2.3
0.2.3
2020-10-28 14:47:30 +06:00
5b325a8ff9 add dokka 2020-10-28 14:47:12 +06:00
4582e0c817 small refactoring of BroadcastStateFlow 2020-10-28 14:19:57 +06:00
e2d1c5d6a1 corotoutines actualization 2020-10-28 14:17:39 +06:00
4019ad7d31 update exposed and coroutines versions 2020-10-28 13:58:23 +06:00
e7df21e91a common js visibility and on screen extensions 2020-10-28 13:57:24 +06:00
b2e30c9f6d start 0.2.3 2020-10-28 13:55:49 +06:00
0b27b5cc06 Revert "start add recycler view"
This reverts commit 347e9c32fe.
2020-10-28 13:51:24 +06:00
347e9c32fe start add recycler view 2020-10-28 13:51:16 +06:00
cc18f58e4c Merge pull request #6 from InsanusMokrassar/0.2.2
0.2.2
2020-10-25 22:26:18 +06:00
dbd2e963a1 change order of functions in read one to many interface 2020-10-25 22:23:24 +06:00
6928ca5329 optimize imports 2020-10-25 22:21:54 +06:00
9ece160aa8 add extensions for onetomany functions 2020-10-25 22:21:23 +06:00
6115c1bcac add extensions for OneToManyKeyValueRepo 2020-10-25 22:08:42 +06:00
e026e94cbf start 0.2.2 2020-10-25 21:52:36 +06:00
56cdd8d6af remove fixing of last index due to solving that pages size must be saved in reversing of pagination 2020-10-22 18:50:17 +06:00
899e6760e1 fix for lastIndex in reversing of pagination 2020-10-22 18:48:05 +06:00
864d0ffcc6 hope last fix 2020-10-22 18:37:56 +06:00
c6f417f8c8 fixes in pagination 2020-10-22 17:57:27 +06:00
9091fa5bd8 Merge pull request #4 from InsanusMokrassar/0.2.1
0.2.1
2020-10-22 15:50:30 +06:00
48 changed files with 881 additions and 168 deletions

View File

@@ -1,5 +1,69 @@
# Changelog
## 0.2.7
* `Versions`:
* `Coroutines`: `1.4.0` -> `1.4.1`
* `Repos`:
* `WriteStandardKeyValueRepo` got new methods `set` and `unset` with collections
* All standard realizations of repos got collections methods realizations
* All old usages of `BroadcastFlow` and `BroadcastChannel` has been replaced with `MutableSharedFlow`
* `Ktor`:
* `Server`:
* Fixed incorrect answer for `keyvalue`
## 0.2.6
* `Pagination`
* Fixes in function `List#paginate`
* Extension property `Pagination#lastIndexExclusive`
## 0.2.5
* `Coroutines`
* Function `safelyWithoutExceptions`
* Extension `CoroutineScope#safeActor`
## 0.2.4
* `Versions`
* `Serialization`: `1.0.0` -> `1.0.1`
* `Common`
* Full rework of `DiffUtils`
* Data class `Diff` has been added
* Extension `Iterable#calculateDiff` has been added
* Extension `Iterable#calculateStrictDiff` as replacement for `Iterable#calculateDiff` with
`strictComparison` mode enabled
* Functions `Diff` (as analog of `Iterable#calculateDiff`) and `StrictDiff` (as analog of
`Iterable#calculateStrictDiff`)
* `Coroutines`
* `BroadcastFlow` now is deprecated
* `BroadcastStateFlow` now is deprecated
* New extensions for `Flow`s:
* `Flow#subscribe`
* `Flow#subscribeSafely`
* `Flow#subscribeSafelyWithoutExceptions`
## 0.2.3
* `Versions`
* `Coroutines`: `1.3.9` -> `1.4.0`
* `Exposed`: `0.27.1` -> `0.28.1`
* `Common`
* `K/JS`
* Add several extensions for `Element` objects to detect that object is on screen viewport
* Add several extensions for `Element` objects to detect object visibility
* `Coroutines`
* `BroadcastStateFlow` now use different strategy for getting of state and implements `replayCache`
## 0.2.2
* `Repos`
* `Common`
* Several new methods `ReadOneToManyKeyValueRepo#getAll`
* Several new method `WriteOneToManyKeyValueRepo#add` and several extensions
* Several new method `WriteOneToManyKeyValueRepo#remove` and several extensions
## 0.2.1
* `Pagination`
@@ -7,6 +71,7 @@
* Extension `Pagination#reverse` has been added
* Factory `PaginationByIndexes`
* Shortcut `calculatePagesNumber` with reversed parameters
* Value `emptyPagination` for empty `SimplePagination` cases
## 0.2.0

View File

@@ -1,10 +1,166 @@
@file:Suppress("NOTHING_TO_INLINE")
package dev.inmo.micro_utils.common
fun <T> Iterable<T>.syncWith(
other: Iterable<T>,
removed: (List<T>) -> Unit = {},
added: (List<T>) -> Unit = {}
) {
removed(filter { it !in other })
added(other.filter { it !in this })
private inline fun <T> getObject(
additional: MutableList<T>,
iterator: Iterator<T>
): T? = when {
additional.isNotEmpty() -> additional.removeFirst()
iterator.hasNext() -> iterator.next()
else -> null
}
/**
* Diff object which contains information about differences between two [Iterable]s
*
* @see calculateDiff
*/
data class Diff<T> internal constructor(
val removed: List<IndexedValue<T>>,
/**
* Old-New values pairs
*/
val replaced: List<Pair<IndexedValue<T>, IndexedValue<T>>>,
val added: List<IndexedValue<T>>
)
private inline fun <T> performChanges(
potentialChanges: MutableList<Pair<IndexedValue<T>?, IndexedValue<T>?>>,
additionalsInOld: MutableList<T>,
additionalsInNew: MutableList<T>,
changedList: MutableList<Pair<IndexedValue<T>, IndexedValue<T>>>,
removedList: MutableList<IndexedValue<T>>,
addedList: MutableList<IndexedValue<T>>,
strictComparison: Boolean
) {
var i = -1
val (oldObject, newObject) = potentialChanges.lastOrNull() ?: return
for ((old, new) in potentialChanges.take(potentialChanges.size - 1)) {
i++
val oldOneEqualToNewObject = old ?.value === newObject ?.value || (old ?.value == newObject ?.value && !strictComparison)
val newOneEqualToOldObject = new ?.value === oldObject ?.value || (new ?.value == oldObject ?.value && !strictComparison)
if (oldOneEqualToNewObject || newOneEqualToOldObject) {
changedList.addAll(
potentialChanges.take(i).mapNotNull {
if (it.first != null && it.second != null) it as Pair<IndexedValue<T>, IndexedValue<T>> else null
}
)
val newPotentials = potentialChanges.drop(i).take(potentialChanges.size - i)
when {
oldOneEqualToNewObject -> {
newPotentials.first().second ?.let { addedList.add(it) }
newPotentials.drop(1).take(newPotentials.size - 2).forEach { (oldOne, newOne) ->
addedList.add(newOne!!)
oldOne ?.let { additionalsInOld.add(oldOne.value) }
}
if (newPotentials.size > 1) {
newPotentials.last().first ?.value ?.let { additionalsInOld.add(it) }
}
}
newOneEqualToOldObject -> {
newPotentials.first().first ?.let { removedList.add(it) }
newPotentials.drop(1).take(newPotentials.size - 2).forEach { (oldOne, newOne) ->
removedList.add(oldOne!!)
newOne ?.let { additionalsInNew.add(newOne.value) }
}
if (newPotentials.size > 1) {
newPotentials.last().second ?.value ?.let { additionalsInNew.add(it) }
}
}
}
potentialChanges.clear()
return
}
}
if (potentialChanges.isNotEmpty() && potentialChanges.last().let { it.first == null && it.second == null }) {
potentialChanges.dropLast(1).forEach { (old, new) ->
when {
old != null && new != null -> changedList.add(old to new)
old != null -> removedList.add(old)
new != null -> addedList.add(new)
}
}
}
}
/**
* 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> {
var i = -1
var j = -1
val additionalInOld = mutableListOf<T>()
val additionalInNew = mutableListOf<T>()
val oldIterator = iterator()
val newIterator = other.iterator()
val potentiallyChangedObjects = mutableListOf<Pair<IndexedValue<T>?, IndexedValue<T>?>>()
val changedObjects = mutableListOf<Pair<IndexedValue<T>, IndexedValue<T>>>()
val addedObjects = mutableListOf<IndexedValue<T>>()
val removedObjects = mutableListOf<IndexedValue<T>>()
while (true) {
i++
j++
val oldObject = getObject(additionalInOld, oldIterator)
val newObject = getObject(additionalInNew, newIterator)
if (oldObject == null && newObject == null) {
break
}
when {
oldObject === newObject || (oldObject == newObject && !strictComparison) -> {
changedObjects.addAll(potentiallyChangedObjects.map { it as Pair<IndexedValue<T>, IndexedValue<T>> })
potentiallyChangedObjects.clear()
}
else -> {
potentiallyChangedObjects.add(oldObject ?.let { IndexedValue(i, oldObject) } to newObject ?.let { IndexedValue(j, newObject) })
val previousOldsAdditionsSize = additionalInOld.size
val previousNewsAdditionsSize = additionalInNew.size
performChanges(potentiallyChangedObjects, additionalInOld, additionalInNew, changedObjects, removedObjects, addedObjects, strictComparison)
i -= (additionalInOld.size - previousOldsAdditionsSize)
j -= (additionalInNew.size - previousNewsAdditionsSize)
}
}
}
potentiallyChangedObjects.add(null to null)
performChanges(potentiallyChangedObjects, additionalInOld, additionalInNew, changedObjects, removedObjects, addedObjects, strictComparison)
return Diff(removedObjects.toList(), changedObjects.toList(), addedObjects.toList())
}
inline fun <T> Diff(old: Iterable<T>, new: Iterable<T>) = old.calculateDiff(new)
inline fun <T> StrictDiff(old: Iterable<T>, new: Iterable<T>) = old.calculateDiff(new, true)
/**
* This method call [calculateDiff] with strict mode enabled
*/
inline fun <T> Iterable<T>.calculateStrictDiff(
other: Iterable<T>
) = calculateDiff(other, strictComparison = true)
/**
* Compare one-to-one
*/
@Deprecated("Will be removed or replaced with some new function. Use calculateDiff instead")
inline fun <T> Iterable<T>.syncWith(
other: Iterable<T>,
noinline removed: (List<T>) -> Unit = {},
noinline added: (List<T>) -> Unit = {}
) {
calculateDiff(other).also {
removed(it.removed.map { it.value })
added(it.added.map { it.value })
}
}

View File

@@ -0,0 +1,76 @@
package dev.inmo.micro_utils.common
import kotlin.math.floor
import kotlin.test.Test
import kotlin.test.assertEquals
class DiffUtilsTests {
@Test
fun testThatSimpleRemoveWorks() {
val oldList = (0 until 10).toList()
val withIndex = oldList.withIndex()
for (count in 1 .. (floor(oldList.size.toFloat() / 2).toInt())) {
for ((i, v) in withIndex) {
if (i + count > oldList.lastIndex) {
continue
}
val removedSublist = oldList.subList(i, i + count)
oldList.calculateDiff(oldList - removedSublist).apply {
assertEquals(
removedSublist.mapIndexed { j, o -> IndexedValue(i + j, o) },
removed
)
}
}
}
}
@Test
fun testThatSimpleAddWorks() {
val oldList = (0 until 10).map { it.toString() }
val withIndex = oldList.withIndex()
for (count in 1 .. (floor(oldList.size.toFloat() / 2).toInt())) {
for ((i, v) in withIndex) {
if (i + count > oldList.lastIndex) {
continue
}
val addedSublist = oldList.subList(i, i + count).map { "added$it" }
val mutable = oldList.toMutableList()
mutable.addAll(i, addedSublist)
oldList.calculateDiff(mutable).apply {
assertEquals(
addedSublist.mapIndexed { j, o -> IndexedValue(i + j, o) },
added
)
}
}
}
}
@Test
fun testThatSimpleChangesWorks() {
val oldList = (0 until 10).map { it.toString() }
val withIndex = oldList.withIndex()
for (step in 0 until oldList.size) {
for ((i, v) in withIndex) {
val mutable = oldList.toMutableList()
val changes = (
if (step == 0) i until oldList.size else (i until oldList.size step step)
).map { index ->
IndexedValue(index, mutable[index]) to IndexedValue(index, "changed$index").also {
mutable[index] = it.value
}
}
oldList.calculateDiff(mutable).apply {
assertEquals(
changes,
replaced
)
}
}
}
}
}

View File

@@ -0,0 +1,43 @@
package dev.inmo.micro_utils.common
import kotlinx.browser.window
import org.w3c.dom.DOMRect
import org.w3c.dom.Element
val DOMRect.isOnScreenByLeftEdge: Boolean
get() = left >= 0 && left <= window.innerWidth
inline val Element.isOnScreenByLeftEdge
get() = getBoundingClientRect().isOnScreenByLeftEdge
val DOMRect.isOnScreenByRightEdge: Boolean
get() = right >= 0 && right <= window.innerWidth
inline val Element.isOnScreenByRightEdge
get() = getBoundingClientRect().isOnScreenByRightEdge
internal val DOMRect.isOnScreenHorizontally: Boolean
get() = isOnScreenByLeftEdge || isOnScreenByRightEdge
val DOMRect.isOnScreenByTopEdge: Boolean
get() = top >= 0 && top <= window.innerHeight
inline val Element.isOnScreenByTopEdge
get() = getBoundingClientRect().isOnScreenByTopEdge
val DOMRect.isOnScreenByBottomEdge: Boolean
get() = bottom >= 0 && bottom <= window.innerHeight
inline val Element.isOnScreenByBottomEdge
get() = getBoundingClientRect().isOnScreenByBottomEdge
internal val DOMRect.isOnScreenVertically: Boolean
get() = isOnScreenByLeftEdge || isOnScreenByRightEdge
val DOMRect.isOnScreenFully: Boolean
get() = isOnScreenByLeftEdge && isOnScreenByTopEdge && isOnScreenByRightEdge && isOnScreenByBottomEdge
val Element.isOnScreenFully: Boolean
get() = getBoundingClientRect().isOnScreenFully
val DOMRect.isOnScreen: Boolean
get() = isOnScreenFully || (isOnScreenHorizontally && isOnScreenVertically)
inline val Element.isOnScreen: Boolean
get() = getBoundingClientRect().isOnScreen

View File

@@ -0,0 +1,48 @@
package dev.inmo.micro_utils.common
import kotlinx.browser.window
import org.w3c.dom.Element
import org.w3c.dom.css.CSSStyleDeclaration
sealed class Visibility
object Visible : Visibility()
object Invisible : Visibility()
object Gone : Visibility()
var CSSStyleDeclaration.visibilityState: Visibility
get() = when {
display == "none" -> Gone
visibility == "hidden" -> Invisible
else -> Visible
}
set(value) {
when (value) {
Visible -> {
if (display == "none") {
display = "initial"
}
visibility = "visible"
}
Invisible -> {
if (display == "none") {
display = "initial"
}
visibility = "hidden"
}
Gone -> {
display = "none"
}
}
}
inline var Element.visibilityState: Visibility
get() = window.getComputedStyle(this).visibilityState
set(value) {
window.getComputedStyle(this).visibilityState = value
}
inline val Element.isVisible: Boolean
get() = visibilityState == Visible
inline val Element.isInvisible: Boolean
get() = visibilityState == Invisible
inline val Element.isGone: Boolean
get() = visibilityState == Gone

View File

@@ -17,3 +17,15 @@ fun <T> CoroutineScope.actor(
return channel
}
inline fun <T> CoroutineScope.safeActor(
channelCapacity: Int = Channel.UNLIMITED,
noinline onException: ExceptionHandler<Unit> = {},
crossinline block: suspend (T) -> Unit
): Channel<T> = actor(
channelCapacity
) {
safely(onException) {
block(it)
}
}

View File

@@ -1,9 +1,11 @@
package dev.inmo.micro_utils.coroutines
import kotlinx.coroutines.channels.*
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.asFlow
@Suppress("FunctionName")
@Deprecated("Deprecated due to stabilization of SharedFlow and StateFlow")
fun <T> BroadcastFlow(
internalChannelSize: Int = Channel.BUFFERED
): BroadcastFlow<T> {
@@ -15,6 +17,7 @@ fun <T> BroadcastFlow(
)
}
@Deprecated("Deprecated due to stabilization of SharedFlow and StateFlow")
class BroadcastFlow<T> internal constructor(
private val channel: BroadcastChannel<T>,
private val flow: Flow<T>

View File

@@ -5,29 +5,64 @@ import kotlinx.coroutines.channels.BroadcastChannel
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.*
const val defaultBroadcastStateFlowReplayCacheSize = 1
@Deprecated("Deprecated due to stabilization of SharedFlow and StateFlow")
class BroadcastStateFlow<T> internal constructor(
parentFlow: Flow<T>,
private val stateGetter: () -> T
initial: T,
replayCacheSize: Int = defaultBroadcastStateFlowReplayCacheSize,
replayScope: CoroutineScope
) : StateFlow<T>, Flow<T> by parentFlow {
private val deque = ArrayDeque<T>(1).also {
it.add(initial)
}
override val replayCache: List<T>
get() = deque.toList()
override val value: T
get() = stateGetter()
}
get() = deque.last()
fun <T> BroadcastChannel<T>.asStateFlow(value: T, scope: CoroutineScope): StateFlow<T> = asFlow().let {
var state: T = value
it.onEach { state = it }.launchIn(scope)
BroadcastStateFlow(it) {
state
init {
if (replayCacheSize < 1) {
error("Replay cache size can't be less than 1, but was $replayCacheSize")
}
parentFlow.onEach {
deque.addLast(it)
if (deque.size > replayCacheSize) {
deque.removeFirst()
}
}.launchIn(replayScope)
}
}
fun <T> BroadcastChannel<T?>.asStateFlow(scope: CoroutineScope): StateFlow<T?> = asStateFlow(null, scope)
@Deprecated("Deprecated due to stabilization of SharedFlow and StateFlow")
fun <T> BroadcastChannel<T>.asStateFlow(
value: T,
scope: CoroutineScope,
replayCacheSize: Int = defaultBroadcastStateFlowReplayCacheSize
): StateFlow<T> = BroadcastStateFlow(asFlow(), value, replayCacheSize, scope)
fun <T> broadcastStateFlow(initial: T, scope: CoroutineScope, channelSize: Int = Channel.BUFFERED) = BroadcastChannel<T>(
@Deprecated("Deprecated due to stabilization of SharedFlow and StateFlow")
fun <T> BroadcastChannel<T?>.asStateFlow(
scope: CoroutineScope,
replayCacheSize: Int = defaultBroadcastStateFlowReplayCacheSize
): StateFlow<T?> = asStateFlow(null, scope, replayCacheSize)
@Deprecated("Deprecated due to stabilization of SharedFlow and StateFlow")
fun <T> broadcastStateFlow(
initial: T, scope: CoroutineScope,
channelSize: Int = Channel.BUFFERED,
replayCacheSize: Int = defaultBroadcastStateFlowReplayCacheSize
) = BroadcastChannel<T>(
channelSize
).let {
it to it.asStateFlow(initial, scope)
it to it.asStateFlow(initial, scope, replayCacheSize)
}
fun <T> broadcastStateFlow(scope: CoroutineScope, channelSize: Int = Channel.BUFFERED) = broadcastStateFlow<T?>(null, scope, channelSize)
@Deprecated("Deprecated due to stabilization of SharedFlow and StateFlow")
fun <T> broadcastStateFlow(
scope: CoroutineScope,
channelSize: Int = Channel.BUFFERED,
replayCacheSize: Int = defaultBroadcastStateFlowReplayCacheSize
) = broadcastStateFlow<T?>(null, scope, channelSize, replayCacheSize)

View File

@@ -0,0 +1,37 @@
@file:Suppress("NOTHING_TO_INLINE")
package dev.inmo.micro_utils.coroutines
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.*
/**
* Shortcut for chain if [Flow.onEach] and [Flow.launchIn]
*/
inline fun <T> Flow<T>.subscribe(scope: CoroutineScope, noinline block: suspend (T) -> Unit) = onEach(block).launchIn(scope)
/**
* Use [subscribe], but all [block]s will be called inside of [safely] function.
* Use [onException] to set up your reaction for [Throwable]s
*/
inline fun <T> Flow<T>.subscribeSafely(
scope: CoroutineScope,
noinline onException: ExceptionHandler<Unit> = { throw it },
noinline block: suspend (T) -> Unit
) = subscribe(scope) {
safely(onException) {
block(it)
}
}
/**
* Use [subscribeSafelyWithoutExceptions], but all exceptions inside of [safely] will be skipped
*/
inline fun <T> Flow<T>.subscribeSafelyWithoutExceptions(
scope: CoroutineScope,
noinline block: suspend (T) -> Unit
) = subscribeSafely(
scope,
{},
block
)

View File

@@ -21,3 +21,10 @@ suspend inline fun <T> safely(
onException(e)
}
}
/**
* Shortcut for [safely] without exception handler (instead of this you will receive null as a result)
*/
suspend inline fun <T> safelyWithoutExceptions(
noinline block: suspend CoroutineScope.() -> T
): T? = safely({ null }, block)

88
dokka/build.gradle Normal file
View File

@@ -0,0 +1,88 @@
buildscript {
repositories {
mavenLocal()
jcenter()
mavenCentral()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
classpath "org.jetbrains.dokka:dokka-gradle-plugin:$dokka_version"
}
}
plugins {
id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.kotlin.plugin.serialization"
id "org.jetbrains.dokka" version "$dokka_version"
}
repositories {
mavenLocal()
jcenter()
mavenCentral()
}
kotlin {
jvm()
js(BOTH) {
browser()
nodejs()
}
sourceSets {
commonMain {
dependencies {
implementation kotlin('stdlib')
project.parent.subprojects.forEach {
if (
it != project
&& it.hasProperty("kotlin")
&& it.kotlin.sourceSets.any { it.name.contains("commonMain") }
) {
api it
}
}
}
}
}
}
private List<SourceDirectorySet> findSourcesWithName(String... approximateNames) {
return parent.subprojects
.findAll { it != project && it.hasProperty("kotlin") }
.collectMany { it.kotlin.sourceSets }
.findAll { sourceSet ->
approximateNames.any { nameToFilter ->
sourceSet.name.contains(nameToFilter)
}
}.collect { it.kotlin }
}
tasks.dokkaHtml {
dokkaSourceSets {
configureEach {
skipDeprecated.set(true)
sourceLink {
localDirectory.set(file("./"))
remoteUrl.set(new URL("https://github.com/InsanusMokrassar/MicroUtils/blob/master/"))
remoteLineSuffix.set("#L")
}
}
named("commonMain") {
sourceRoots.setFrom(findSourcesWithName("commonMain"))
}
named("jsMain") {
sourceRoots.setFrom(findSourcesWithName("jsMain", "commonMain"))
}
named("jvmMain") {
sourceRoots.setFrom(findSourcesWithName("jvmMain", "commonMain"))
}
}
}

3
dokka/gradle.properties Normal file
View File

@@ -0,0 +1,3 @@
dokka_version=1.4.0
org.gradle.jvmargs=-Xmx1024m

View File

@@ -5,9 +5,9 @@ kotlin.incremental=true
kotlin.incremental.js=true
kotlin_version=1.4.10
kotlin_coroutines_version=1.3.9
kotlin_serialisation_core_version=1.0.0
kotlin_exposed_version=0.27.1
kotlin_coroutines_version=1.4.1
kotlin_serialisation_core_version=1.0.1
kotlin_exposed_version=0.28.1
ktor_version=1.4.1
@@ -19,4 +19,4 @@ github_release_plugin_version=2.2.12
uuidVersion=0.2.2
group=dev.inmo
version=0.2.1
version=0.2.7

View File

@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@@ -4,7 +4,8 @@ import dev.inmo.micro_utils.coroutines.safely
import dev.inmo.micro_utils.ktor.common.*
import io.ktor.client.HttpClient
import io.ktor.client.features.websocket.ws
import io.ktor.http.cio.websocket.*
import io.ktor.http.cio.websocket.Frame
import io.ktor.http.cio.websocket.readBytes
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.channelFlow
import kotlinx.serialization.DeserializationStrategy

View File

@@ -4,7 +4,8 @@ import dev.inmo.micro_utils.ktor.common.*
import io.ktor.client.HttpClient
import io.ktor.client.request.get
import io.ktor.client.request.post
import kotlinx.serialization.*
import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.SerializationStrategy
typealias BodyPair<T> = Pair<SerializationStrategy<T>, T>

View File

@@ -22,13 +22,11 @@ fun <T> Route.includeWebsocketHandling(
converter: (T) -> StandardKtorSerialInputData
) {
webSocket(suburl) {
// println("connected")
safely {
flow.collect {
send(converter(it))
}
}
// println("disconnected")
}
}

View File

@@ -7,8 +7,8 @@ import io.ktor.http.HttpStatusCode
import io.ktor.request.receive
import io.ktor.response.respond
import io.ktor.response.respondBytes
import io.ktor.util.toByteArray
import kotlinx.serialization.*
import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.SerializationStrategy
suspend fun <T> ApplicationCall.unianswer(
answerSerializer: SerializationStrategy<T>,

View File

@@ -28,6 +28,15 @@ interface Pagination {
val Pagination.firstIndex: Int
get() = page * size
/**
* Last number in index of objects. In fact, one [Pagination] object represent data in next range:
*
* [[firstIndex], [lastIndex]]; That means, that for [Pagination] with [Pagination.size] == 10 and [Pagination.page] == 1
* you will retrieve [Pagination.firstIndex] == 10 and [Pagination.lastIndex] == 19. Here [Pagination.lastIndexExclusive] == 20
*/
val Pagination.lastIndexExclusive: Int
get() = firstIndex + size
/**
* Last number in index of objects. In fact, one [Pagination] object represent data in next range:
*
@@ -35,7 +44,7 @@ val Pagination.firstIndex: Int
* you will retrieve [Pagination.firstIndex] == 10 and [Pagination.lastIndex] == 19.
*/
val Pagination.lastIndex: Int
get() = firstIndex + size - 1
get() = lastIndexExclusive - 1
/**
* Calculates pages count for given [datasetSize]

View File

@@ -14,6 +14,8 @@ inline fun FirstPagePagination(size: Int = defaultMediumPageSize) =
size = size
)
val emptyPagination = Pagination(0, 0)
@Suppress("NOTHING_TO_INLINE")
inline fun Pagination.nextPage() =
SimplePagination(

View File

@@ -21,7 +21,12 @@ fun <T> Iterable<T>.paginate(with: Pagination): PaginationResult<T> {
}
fun <T> List<T>.paginate(with: Pagination): PaginationResult<T> {
return subList(with.firstIndex, with.lastIndex + 1).createPaginationResult(
val firstIndex = maxOf(with.firstIndex, 0)
val lastIndex = minOf(with.lastIndexExclusive, size)
if (firstIndex > lastIndex) {
return emptyPaginationResult()
}
return subList(firstIndex, lastIndex).createPaginationResult(
with,
size.toLong()
)

View File

@@ -10,22 +10,16 @@ import dev.inmo.micro_utils.pagination.*
*
* @return Reversed version of this [Pagination]
*/
fun Pagination.reverse(objectsCount: Long): SimplePagination {
val firstIndex = (objectsCount - (this.lastIndex + 1)).let {
when {
it < 0 -> it
it > objectsCount -> objectsCount
else -> it
}
}.toInt()
val lastIndex = (objectsCount - (this.firstIndex + 1)).let {
when {
it < 0 -> it
it > objectsCount -> objectsCount
else -> it
}
}.toInt()
return PaginationByIndexes(firstIndex, lastIndex)
fun Pagination.reverse(datasetSize: Long): SimplePagination {
val pagesNumber = calculatePagesNumber(size, datasetSize)
val newPage = pagesNumber - page - 1
return when {
page < 0 || page >= pagesNumber -> emptyPagination
else -> Pagination(
newPage,
size
)
}
}
/**

View File

@@ -7,18 +7,18 @@ import kotlin.test.assertEquals
class PaginationReversingTests {
@Test
fun testThatCommonCaseWorksOk() {
val pageSize = 2
val collectionSize = 8
val pages = calculatePage(collectionSize, pageSize)
val pageSize = 3
val collectionSize = 9
doWithPagination(FirstPagePagination(pageSize)) {
val reversed = it.reverse(collectionSize.toLong())
assertEquals(Pagination(calculatePage(collectionSize - it.firstIndex - it.size, it.size), it.size), reversed)
if (it.page < pages) {
it.nextPage()
} else {
null
}
}
assertEquals(Pagination(-1, pageSize).reverse(collectionSize), Pagination(0, 0))
val middleFirstIndex = collectionSize / 2 - pageSize / 2
val middleLastIndex = middleFirstIndex + pageSize - 1
assertEquals(
PaginationByIndexes(middleFirstIndex, middleLastIndex).reverse(collectionSize),
PaginationByIndexes(middleFirstIndex, middleLastIndex)
)
assertEquals(Pagination(calculatePagesNumber(collectionSize, pageSize), pageSize).reverse(collectionSize), Pagination(0, 0))
}
}

View File

@@ -1,7 +1,6 @@
package dev.inmo.micro_utils.repos
import dev.inmo.micro_utils.pagination.Pagination
import dev.inmo.micro_utils.pagination.PaginationResult
import dev.inmo.micro_utils.pagination.*
import kotlinx.coroutines.flow.Flow
interface ReadOneToManyKeyValueRepo<Key, Value> : Repo {
@@ -11,6 +10,30 @@ interface ReadOneToManyKeyValueRepo<Key, Value> : Repo {
suspend fun contains(k: Key, v: Value): Boolean
suspend fun count(k: Key): Long
suspend fun count(): Long
suspend fun getAll(k: Key, reversed: Boolean = false): List<Value> = mutableListOf<Value>().also { list ->
doWithPagination {
get(k, it).also {
list.addAll(it.results)
}.nextPageIfNotEmpty()
}
if (reversed) {
list.reverse()
}
}
/**
* WARNING!!! THIS METHOD PROBABLY IS NOT EFFICIENT, USE WITH CAUTION
*/
suspend fun getAll(reverseLists: Boolean = false): Map<Key, List<Value>> = mutableMapOf<Key, List<Value>>().also { map ->
doWithPagination {
keys(it).also { paginationResult ->
paginationResult.results.forEach { k ->
map[k] = getAll(k, reverseLists)
}
}.nextPageIfNotEmpty()
}
}
}
@Deprecated("Renamed", ReplaceWith("ReadOneToManyKeyValueRepo", "dev.inmo.micro_utils.repos.ReadOneToManyKeyValueRepo"))
typealias OneToManyReadKeyValueRepo<Key, Value> = ReadOneToManyKeyValueRepo<Key, Value>
@@ -20,11 +43,43 @@ interface WriteOneToManyKeyValueRepo<Key, Value> : Repo {
val onValueRemoved: Flow<Pair<Key, Value>>
val onDataCleared: Flow<Key>
suspend fun add(k: Key, v: Value)
suspend fun remove(k: Key, v: Value)
suspend fun add(toAdd: Map<Key, List<Value>>)
@Deprecated("Will be extracted as extension for other add method")
suspend fun add(k: Key, v: Value) = add(mapOf(k to listOf(v)))
suspend fun remove(toRemove: Map<Key, List<Value>>)
@Deprecated("Will be extracted as extension for other remove method")
suspend fun remove(k: Key, v: Value) = remove(mapOf(k to listOf(v)))
suspend fun clear(k: Key)
}
@Deprecated("Renamed", ReplaceWith("WriteOneToManyKeyValueRepo", "dev.inmo.micro_utils.repos.WriteOneToManyKeyValueRepo"))
typealias OneToManyWriteKeyValueRepo<Key, Value> = WriteOneToManyKeyValueRepo<Key, Value>
interface OneToManyKeyValueRepo<Key, Value> : ReadOneToManyKeyValueRepo<Key, Value>, WriteOneToManyKeyValueRepo<Key, Value>
interface OneToManyKeyValueRepo<Key, Value> : ReadOneToManyKeyValueRepo<Key, Value>, WriteOneToManyKeyValueRepo<Key, Value>
suspend inline fun <Key, Value, REPO : WriteOneToManyKeyValueRepo<Key, Value>> REPO.add(
k: Key,
vararg v: Value
) = add(mapOf(k to v.toList()))
suspend inline fun <Key, Value, REPO : WriteOneToManyKeyValueRepo<Key, Value>> REPO.add(
keysAndValues: List<Pair<Key, List<Value>>>
) = add(keysAndValues.toMap())
suspend inline fun <Key, Value, REPO : WriteOneToManyKeyValueRepo<Key, Value>> REPO.add(
vararg keysAndValues: Pair<Key, List<Value>>
) = add(keysAndValues.toMap())
suspend inline fun <Key, Value, REPO : WriteOneToManyKeyValueRepo<Key, Value>> REPO.remove(
k: Key,
vararg v: Value
) = remove(mapOf(k to v.toList()))
suspend inline fun <Key, Value, REPO : WriteOneToManyKeyValueRepo<Key, Value>> REPO.remove(
keysAndValues: List<Pair<Key, List<Value>>>
) = remove(keysAndValues.toMap())
suspend inline fun <Key, Value, REPO : WriteOneToManyKeyValueRepo<Key, Value>> REPO.remove(
vararg keysAndValues: Pair<Key, List<Value>>
) = remove(keysAndValues.toMap())

View File

@@ -16,8 +16,16 @@ interface WriteStandardKeyValueRepo<Key, Value> : Repo {
val onNewValue: Flow<Pair<Key, Value>>
val onValueRemoved: Flow<Key>
@Deprecated("Realize set with map instead")
suspend fun set(k: Key, v: Value)
suspend fun set(toSet: Map<Key, Value>) = toSet.forEach { (k, v) ->
set(k, v)
}
@Deprecated("Realize unset with list instead")
suspend fun unset(k: Key)
suspend fun unset(toUnset: List<Key>) = toUnset.forEach {
unset(it)
}
}
interface StandardKeyValueRepo<Key, Value> : ReadStandardKeyValueRepo<Key, Value>, WriteStandardKeyValueRepo<Key, Value>

View File

@@ -1,7 +1,7 @@
package dev.inmo.micro_utils.repos.pagination
import dev.inmo.micro_utils.pagination.*
import dev.inmo.micro_utils.repos.*
import dev.inmo.micro_utils.repos.ReadStandardKeyValueRepo
suspend inline fun <Key, Value, REPO : ReadStandardKeyValueRepo<Key, Value>> REPO.doForAll(
@Suppress("REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE")

View File

@@ -1,7 +1,7 @@
package dev.inmo.micro_utils.repos.pagination
import dev.inmo.micro_utils.pagination.*
import dev.inmo.micro_utils.repos.*
import dev.inmo.micro_utils.repos.ReadOneToManyKeyValueRepo
suspend inline fun <Key, Value, REPO : ReadOneToManyKeyValueRepo<Key, Value>> REPO.doForAll(
@Suppress("REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE")

View File

@@ -1,6 +1,5 @@
package dev.inmo.micro_utils.repos.exposed
import dev.inmo.micro_utils.repos.Repo
import org.jetbrains.exposed.sql.*
interface ExposedCRUDRepo<ObjectType, IdType> : ExposedRepo {

View File

@@ -1,11 +1,9 @@
package dev.inmo.micro_utils.repos.exposed.keyvalue
import dev.inmo.micro_utils.repos.StandardKeyValueRepo
import dev.inmo.micro_utils.repos.exposed.*
import kotlinx.coroutines.channels.BroadcastChannel
import dev.inmo.micro_utils.repos.exposed.ColumnAllocator
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.*
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.transactions.transaction
@@ -20,11 +18,11 @@ open class ExposedKeyValueRepo<Key, Value>(
valueColumnAllocator,
tableName
) {
private val onNewValueChannel = BroadcastChannel<Pair<Key, Value>>(Channel.BUFFERED)
private val onValueRemovedChannel = BroadcastChannel<Key>(Channel.BUFFERED)
private val _onNewValue = MutableSharedFlow<Pair<Key, Value>>(Channel.BUFFERED)
private val _onValueRemoved = MutableSharedFlow<Key>(Channel.BUFFERED)
override val onNewValue: Flow<Pair<Key, Value>> = onNewValueChannel.asFlow()
override val onValueRemoved: Flow<Key> = onValueRemovedChannel.asFlow()
override val onNewValue: Flow<Pair<Key, Value>> = _onNewValue.asSharedFlow()
override val onValueRemoved: Flow<Key> = _onValueRemoved.asSharedFlow()
override suspend fun set(k: Key, v: Value) {
transaction(database) {
@@ -39,14 +37,50 @@ open class ExposedKeyValueRepo<Key, Value>(
}
}
}
onNewValueChannel.send(k to v)
_onNewValue.emit(k to v)
}
override suspend fun set(toSet: Map<Key, Value>) {
transaction(database) {
toSet.mapNotNull { (k, v) ->
if (update({ keyColumn.eq(k) }) { it[valueColumn] = v } > 0) {
k to v
} else {
val inserted = insert {
it[keyColumn] = k
it[valueColumn] = v
}.getOrNull(keyColumn) != null
if (inserted) {
k to v
} else {
null
}
}
}
}.forEach {
_onNewValue.emit(it)
}
}
override suspend fun unset(k: Key) {
transaction(database) {
deleteWhere { keyColumn.eq(k) }
}
onValueRemovedChannel.send(k)
_onValueRemoved.emit(k)
}
override suspend fun unset(toUnset: List<Key>) {
transaction(database) {
toUnset.mapNotNull {
if (deleteWhere { keyColumn.eq(it) } > 0) {
it
} else {
null
}
}
}.forEach {
_onValueRemoved.emit(it)
}
}
}

View File

@@ -2,7 +2,8 @@ package dev.inmo.micro_utils.repos.exposed.keyvalue
import dev.inmo.micro_utils.pagination.*
import dev.inmo.micro_utils.repos.ReadStandardKeyValueRepo
import dev.inmo.micro_utils.repos.exposed.*
import dev.inmo.micro_utils.repos.exposed.ColumnAllocator
import dev.inmo.micro_utils.repos.exposed.ExposedRepo
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.transactions.transaction

View File

@@ -3,8 +3,7 @@ package dev.inmo.micro_utils.repos.exposed.onetomany
import dev.inmo.micro_utils.coroutines.BroadcastFlow
import dev.inmo.micro_utils.repos.OneToManyKeyValueRepo
import dev.inmo.micro_utils.repos.exposed.ColumnAllocator
import dev.inmo.micro_utils.repos.exposed.initTable
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.*
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.transactions.transaction
@@ -19,13 +18,13 @@ open class ExposedOneToManyKeyValueRepo<Key, Value>(
valueColumnAllocator,
tableName
) {
protected val _onNewValue: BroadcastFlow<Pair<Key, Value>> = BroadcastFlow()
protected val _onNewValue: MutableSharedFlow<Pair<Key, Value>> = MutableSharedFlow()
override val onNewValue: Flow<Pair<Key, Value>>
get() = _onNewValue
protected val _onValueRemoved: BroadcastFlow<Pair<Key, Value>> = BroadcastFlow()
protected val _onValueRemoved: MutableSharedFlow<Pair<Key, Value>> = MutableSharedFlow()
override val onValueRemoved: Flow<Pair<Key, Value>>
get() = _onValueRemoved
protected val _onDataCleared: BroadcastFlow<Key> = BroadcastFlow()
protected val _onDataCleared: MutableSharedFlow<Key> = MutableSharedFlow()
override val onDataCleared: Flow<Key>
get() = _onDataCleared
@@ -35,19 +34,42 @@ open class ExposedOneToManyKeyValueRepo<Key, Value>(
it[keyColumn] = k
it[valueColumn] = v
}
}.also { _onNewValue.send(k to v) }
}.also { _onNewValue.emit(k to v) }
}
override suspend fun remove(k: Key, v: Value) {
override suspend fun add(toAdd: Map<Key, List<Value>>) {
transaction(database) {
deleteWhere { keyColumn.eq(k).and(valueColumn.eq(v)) }
}.also { _onValueRemoved.send(k to v) }
toAdd.keys.flatMap { k ->
toAdd[k] ?.mapNotNull { v ->
insertIgnore {
it[keyColumn] = k
it[valueColumn] = v
}.getOrNull(keyColumn) ?.let { k to v }
} ?: emptyList()
}
}.forEach { _onNewValue.emit(it) }
}
override suspend fun remove(toRemove: Map<Key, List<Value>>) {
transaction(database) {
toRemove.keys.flatMap { k ->
toRemove[k] ?.mapNotNull { v ->
if (deleteIgnoreWhere { keyColumn.eq(k).and(valueColumn.eq(v)) } > 0 ) {
k to v
} else {
null
}
} ?: emptyList()
}
}.forEach {
_onValueRemoved.emit(it)
}
}
override suspend fun clear(k: Key) {
transaction(database) {
deleteWhere { keyColumn.eq(k) }
}.also { _onDataCleared.send(k) }
}.also { _onDataCleared.emit(k) }
}
}

View File

@@ -1,10 +1,12 @@
package dev.inmo.micro_utils.repos
import dev.inmo.micro_utils.coroutines.BroadcastFlow
import dev.inmo.micro_utils.pagination.*
import dev.inmo.micro_utils.pagination.Pagination
import dev.inmo.micro_utils.pagination.PaginationResult
import dev.inmo.micro_utils.pagination.utils.paginate
import dev.inmo.micro_utils.pagination.utils.reverse
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
class ReadMapKeyValueRepo<Key, Value>(
private val map: Map<Key, Value> = emptyMap()
@@ -46,20 +48,31 @@ class ReadMapKeyValueRepo<Key, Value>(
class WriteMapKeyValueRepo<Key, Value>(
private val map: MutableMap<Key, Value> = mutableMapOf()
) : WriteStandardKeyValueRepo<Key, Value> {
private val _onNewValue: BroadcastFlow<Pair<Key, Value>> = BroadcastFlow()
private val _onNewValue: MutableSharedFlow<Pair<Key, Value>> = MutableSharedFlow()
override val onNewValue: Flow<Pair<Key, Value>>
get() = _onNewValue
private val _onValueRemoved: BroadcastFlow<Key> = BroadcastFlow()
private val _onValueRemoved: MutableSharedFlow<Key> = MutableSharedFlow()
override val onValueRemoved: Flow<Key>
get() = _onValueRemoved
override suspend fun set(k: Key, v: Value) {
map[k] = v
_onNewValue.send(k to v)
_onNewValue.emit(k to v)
}
override suspend fun set(toSet: Map<Key, Value>) {
map.putAll(toSet)
toSet.forEach { (k, v) -> _onNewValue.emit(k to v) }
}
override suspend fun unset(k: Key) {
map.remove(k) ?.also { _onValueRemoved.send(k) }
map.remove(k) ?.also { _onValueRemoved.emit(k) }
}
override suspend fun unset(toUnset: List<Key>) {
toUnset.forEach { k ->
map.remove(k) ?.also { _ -> _onValueRemoved.emit(k) }
}
}
}

View File

@@ -55,13 +55,18 @@ class MapWriteOneToManyKeyValueRepo<Key, Value>(
override val onDataCleared: Flow<Key>
get() = _onDataCleared
override suspend fun add(k: Key, v: Value) {
map.getOrPut(k) { mutableListOf() }.add(v)
_onNewValue.send(k to v)
override suspend fun add(toAdd: Map<Key, List<Value>>) {
toAdd.keys.forEach {
map.getOrPut(it) {
mutableListOf()
}.addAll(toAdd[it] ?: return@forEach)
}
}
override suspend fun remove(k: Key, v: Value) {
map[k] ?.remove(v) ?.also { _onValueRemoved.send(k to v) }
override suspend fun remove(toRemove: Map<Key, List<Value>>) {
toRemove.keys.forEach {
map[it] ?.removeAll(toRemove[it] ?: return@forEach)
}
}
override suspend fun clear(k: Key) {

View File

@@ -3,9 +3,7 @@ package dev.inmo.micro_utils.repos.ktor.client.crud
import dev.inmo.micro_utils.ktor.client.encodeUrlQueryValue
import dev.inmo.micro_utils.ktor.client.uniget
import dev.inmo.micro_utils.ktor.common.buildStandardUrl
import dev.inmo.micro_utils.pagination.Pagination
import dev.inmo.micro_utils.pagination.PaginationResult
import dev.inmo.micro_utils.pagination.asUrlQueryParts
import dev.inmo.micro_utils.pagination.*
import dev.inmo.micro_utils.repos.ReadStandardCRUDRepo
import dev.inmo.micro_utils.repos.ktor.common.crud.*
import io.ktor.client.HttpClient

View File

@@ -1,8 +1,6 @@
package dev.inmo.micro_utils.repos.ktor.client.crud
import dev.inmo.micro_utils.ktor.client.BodyPair
import dev.inmo.micro_utils.ktor.client.createStandardWebsocketFlow
import dev.inmo.micro_utils.ktor.client.unipost
import dev.inmo.micro_utils.ktor.client.*
import dev.inmo.micro_utils.ktor.common.buildStandardUrl
import dev.inmo.micro_utils.repos.UpdatedValuePair
import dev.inmo.micro_utils.repos.WriteStandardCRUDRepo

View File

@@ -3,12 +3,10 @@ package dev.inmo.micro_utils.repos.ktor.client.key_value
import dev.inmo.micro_utils.ktor.client.encodeUrlQueryValue
import dev.inmo.micro_utils.ktor.client.uniget
import dev.inmo.micro_utils.ktor.common.buildStandardUrl
import dev.inmo.micro_utils.pagination.Pagination
import dev.inmo.micro_utils.pagination.PaginationResult
import dev.inmo.micro_utils.pagination.asUrlQueryParts
import dev.inmo.micro_utils.pagination.*
import dev.inmo.micro_utils.repos.ReadStandardKeyValueRepo
import dev.inmo.micro_utils.repos.ktor.common.key_value.*
import io.ktor.client.*
import io.ktor.client.HttpClient
import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.serializer

View File

@@ -1,9 +1,7 @@
package dev.inmo.micro_utils.repos.ktor.client.key_value
import dev.inmo.micro_utils.repos.StandardKeyValueRepo
import dev.inmo.micro_utils.repos.ReadStandardKeyValueRepo
import dev.inmo.micro_utils.repos.WriteStandardKeyValueRepo
import io.ktor.client.*
import dev.inmo.micro_utils.repos.*
import io.ktor.client.HttpClient
import kotlinx.serialization.KSerializer
class KtorStandartKeyValueRepo<K, V> (

View File

@@ -1,16 +1,13 @@
package dev.inmo.micro_utils.repos.ktor.client.key_value
import dev.inmo.micro_utils.ktor.client.BodyPair
import dev.inmo.micro_utils.ktor.client.createStandardWebsocketFlow
import dev.inmo.micro_utils.ktor.client.unipost
import dev.inmo.micro_utils.ktor.client.*
import dev.inmo.micro_utils.ktor.common.buildStandardUrl
import dev.inmo.micro_utils.repos.WriteStandardKeyValueRepo
import dev.inmo.micro_utils.repos.ktor.common.key_value.*
import io.ktor.client.*
import io.ktor.client.HttpClient
import kotlinx.coroutines.flow.Flow
import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.PairSerializer
import kotlinx.serialization.builtins.serializer
import kotlinx.serialization.builtins.*
class KtorWriteStandardKeyValueRepo<K, V> (
private var baseUrl: String,
@@ -18,6 +15,8 @@ class KtorWriteStandardKeyValueRepo<K, V> (
private var keySerializer: KSerializer<K>,
private var valueSerializer: KSerializer<V>,
) : WriteStandardKeyValueRepo<K, V> {
private val keyValueMapSerializer = MapSerializer(keySerializer, valueSerializer)
private val keysListSerializer = ListSerializer(keySerializer)
override val onNewValue: Flow<Pair<K, V>> = client.createStandardWebsocketFlow(
buildStandardUrl(baseUrl, onNewValueRoute),
deserializer = PairSerializer(keySerializer, valueSerializer)
@@ -28,23 +27,25 @@ class KtorWriteStandardKeyValueRepo<K, V> (
deserializer = keySerializer
)
override suspend fun set(k: K, v: V) = client.unipost(
override suspend fun set(toSet: Map<K, V>) = client.unipost(
buildStandardUrl(
baseUrl,
setRoute
),
BodyPair(KeyValuePostObject.serializer(keySerializer, valueSerializer), KeyValuePostObject(k, v)),
BodyPair(keyValueMapSerializer, toSet),
Unit.serializer()
)
override suspend fun set(k: K, v: V) = set(mapOf(k to v))
override suspend fun unset(k: K) = client.unipost(
override suspend fun unset(toUnset: List<K>) = client.unipost(
buildStandardUrl(
baseUrl,
unsetRoute,
),
BodyPair(keySerializer, k),
BodyPair(keysListSerializer, toUnset),
Unit.serializer()
)
override suspend fun unset(k: K) = unset(listOf(k))
}
@Deprecated("Renamed", ReplaceWith("KtorWriteStandardKeyValueRepo", "dev.inmo.micro_utils.repos.ktor.client.key_value.KtorWriteStandardKeyValueRepo"))

View File

@@ -1,8 +1,6 @@
package dev.inmo.micro_utils.repos.ktor.client.one_to_many
import dev.inmo.micro_utils.repos.OneToManyKeyValueRepo
import dev.inmo.micro_utils.repos.ReadOneToManyKeyValueRepo
import dev.inmo.micro_utils.repos.WriteOneToManyKeyValueRepo
import dev.inmo.micro_utils.repos.*
import io.ktor.client.HttpClient
import kotlinx.serialization.KSerializer

View File

@@ -3,9 +3,7 @@ package dev.inmo.micro_utils.repos.ktor.client.one_to_many
import dev.inmo.micro_utils.ktor.client.encodeUrlQueryValue
import dev.inmo.micro_utils.ktor.client.uniget
import dev.inmo.micro_utils.ktor.common.buildStandardUrl
import dev.inmo.micro_utils.pagination.Pagination
import dev.inmo.micro_utils.pagination.PaginationResult
import dev.inmo.micro_utils.pagination.asUrlQueryParts
import dev.inmo.micro_utils.pagination.*
import dev.inmo.micro_utils.repos.ReadOneToManyKeyValueRepo
import dev.inmo.micro_utils.repos.ktor.common.one_to_many.*
import io.ktor.client.HttpClient

View File

@@ -1,16 +1,13 @@
package dev.inmo.micro_utils.repos.ktor.client.one_to_many
import dev.inmo.micro_utils.coroutines.broadcastStateFlow
import dev.inmo.micro_utils.ktor.client.*
import dev.inmo.micro_utils.ktor.common.buildStandardUrl
import dev.inmo.micro_utils.repos.WriteOneToManyKeyValueRepo
import dev.inmo.micro_utils.repos.ktor.common.one_to_many.*
import io.ktor.client.HttpClient
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.PairSerializer
import kotlinx.serialization.builtins.serializer
import kotlinx.serialization.builtins.*
class KtorWriteOneToManyKeyValueRepo<Key, Value> (
private val baseUrl: String,
@@ -19,6 +16,7 @@ class KtorWriteOneToManyKeyValueRepo<Key, Value> (
private val valueSerializer: KSerializer<Value>
) : WriteOneToManyKeyValueRepo<Key, Value> {
private val keyValueSerializer = PairSerializer(keySerializer, valueSerializer)
private val keyValueMapSerializer = MapSerializer(keySerializer, ListSerializer(valueSerializer))
override val onNewValue: Flow<Pair<Key, Value>> = client.createStandardWebsocketFlow(
buildStandardUrl(baseUrl, onNewValueRoute),
deserializer = keyValueSerializer
@@ -41,15 +39,23 @@ class KtorWriteOneToManyKeyValueRepo<Key, Value> (
Unit.serializer(),
)
override suspend fun remove(k: Key, v: Value) = client.unipost(
override suspend fun remove(toRemove: Map<Key, List<Value>>) = client.unipost(
buildStandardUrl(
baseUrl,
removeRoute,
),
BodyPair(keyValueSerializer, k to v),
BodyPair(keyValueMapSerializer, toRemove),
Unit.serializer(),
)
override suspend fun add(toAdd: Map<Key, List<Value>>) = client.unipost(
buildStandardUrl(
baseUrl,
clearRoute,
),
BodyPair(keyValueMapSerializer, toAdd),
Unit.serializer(),
)
override suspend fun clear(k: Key) = client.unipost(
buildStandardUrl(
baseUrl,

View File

@@ -1,8 +1,6 @@
package dev.inmo.micro_utils.repos.ktor.server.crud
import dev.inmo.micro_utils.ktor.server.includeWebsocketHandling
import dev.inmo.micro_utils.ktor.server.unianswer
import dev.inmo.micro_utils.ktor.server.uniload
import dev.inmo.micro_utils.ktor.server.*
import dev.inmo.micro_utils.repos.WriteStandardCRUDRepo
import dev.inmo.micro_utils.repos.ktor.common.crud.*
import io.ktor.application.call

View File

@@ -1,7 +1,8 @@
package dev.inmo.micro_utils.repos.ktor.server.key_value
import dev.inmo.micro_utils.repos.StandardKeyValueRepo
import io.ktor.routing.*
import io.ktor.routing.Route
import io.ktor.routing.route
import kotlinx.serialization.KSerializer
fun <K, V> Route.configureStandartKeyValueRepoRoutes(

View File

@@ -6,8 +6,9 @@ import dev.inmo.micro_utils.pagination.PaginationResult
import dev.inmo.micro_utils.pagination.extractPagination
import dev.inmo.micro_utils.repos.ReadStandardKeyValueRepo
import dev.inmo.micro_utils.repos.ktor.common.key_value.*
import io.ktor.application.*
import io.ktor.routing.*
import io.ktor.application.call
import io.ktor.routing.Route
import io.ktor.routing.get
import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.serializer

View File

@@ -1,19 +1,21 @@
package dev.inmo.micro_utils.repos.ktor.server.key_value
import dev.inmo.micro_utils.ktor.server.includeWebsocketHandling
import dev.inmo.micro_utils.ktor.server.uniload
import dev.inmo.micro_utils.ktor.server.*
import dev.inmo.micro_utils.repos.WriteStandardKeyValueRepo
import dev.inmo.micro_utils.repos.ktor.common.key_value.*
import io.ktor.application.*
import io.ktor.routing.*
import io.ktor.application.call
import io.ktor.routing.Route
import io.ktor.routing.post
import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.PairSerializer
import kotlinx.serialization.builtins.*
fun <K, V> Route.configureWriteStandartKeyValueRepoRoutes (
originalRepo: WriteStandardKeyValueRepo<K, V>,
keySerializer: KSerializer<K>,
valueSerializer: KSerializer<V>,
) {
val keyValueMapSerializer = MapSerializer(keySerializer, valueSerializer)
val keysListSerializer = ListSerializer(keySerializer)
includeWebsocketHandling(
onNewValueRoute,
originalRepo.onNewValue,
@@ -27,18 +29,16 @@ fun <K, V> Route.configureWriteStandartKeyValueRepoRoutes (
)
post(setRoute) {
val (key, value) = call.uniload(
KeyValuePostObject.serializer(keySerializer, valueSerializer)
val toSet = call.uniload(
keyValueMapSerializer
)
originalRepo.set(key, value)
call.unianswer(Unit.serializer(), originalRepo.set(toSet))
}
post(unsetRoute) {
val key = call.uniload(
keySerializer
)
val toUnset = call.uniload(keysListSerializer)
originalRepo.unset(key)
call.unianswer(Unit.serializer(), originalRepo.unset(toUnset))
}
}

View File

@@ -1,8 +1,6 @@
package dev.inmo.micro_utils.repos.ktor.server.one_to_many
import dev.inmo.micro_utils.ktor.server.decodeUrlQueryValue
import dev.inmo.micro_utils.ktor.server.decodeUrlQueryValueOrSendError
import dev.inmo.micro_utils.ktor.server.unianswer
import dev.inmo.micro_utils.ktor.server.*
import dev.inmo.micro_utils.pagination.PaginationResult
import dev.inmo.micro_utils.pagination.extractPagination
import dev.inmo.micro_utils.repos.ReadOneToManyKeyValueRepo

View File

@@ -7,15 +7,15 @@ import io.ktor.application.call
import io.ktor.routing.Route
import io.ktor.routing.post
import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.PairSerializer
import kotlinx.serialization.builtins.serializer
import kotlinx.serialization.builtins.*
fun <Key, Value> Route.configureOneToManyWriteKeyValueRepoRoutes(
originalRepo: WriteOneToManyKeyValueRepo<Key, Value>,
keySerializer: KSerializer<Key>,
valueSealizer: KSerializer<Value>,
valueSerializer: KSerializer<Value>,
) {
val keyValueSerializer = PairSerializer(keySerializer, valueSealizer)
val keyValueSerializer = PairSerializer(keySerializer, valueSerializer)
val keyValueMapSerializer = MapSerializer(keySerializer, ListSerializer(valueSerializer))
includeWebsocketHandling(
onNewValueRoute,
@@ -34,24 +34,22 @@ fun <Key, Value> Route.configureOneToManyWriteKeyValueRepoRoutes(
)
post(addRoute) {
val obj = call.uniload(
keyValueSerializer
)
val obj = call.uniload(keyValueMapSerializer)
call.unianswer(
Unit.serializer(),
originalRepo.add(obj.first, obj.second)
originalRepo.add(obj)
)
}
post(removeRoute) {
val obj = call.uniload(
keyValueSerializer
keyValueMapSerializer
)
call.unianswer(
Unit.serializer(),
originalRepo.remove(obj.first, obj.second),
originalRepo.remove(obj),
)
}

View File

@@ -16,7 +16,9 @@ String[] includes = [
":ktor:server",
":ktor:common",
":ktor:client",
":coroutines"
":coroutines",
":dokka"
]