mirror of
https://github.com/InsanusMokrassar/MicroUtils.git
synced 2025-09-18 14:59:24 +00:00
Compare commits
50 Commits
Author | SHA1 | Date | |
---|---|---|---|
eaba9173ae | |||
2f42b30f87 | |||
35913b95be | |||
8023fa1b76 | |||
4cbe2d1d61 | |||
740036e8d9 | |||
273a7aa9a4 | |||
dccb479c3c | |||
6f5c6e5ebe | |||
8495cc6263 | |||
d242d8dcf4 | |||
864d576e70 | |||
f0127b018e | |||
5eb48a58bf | |||
b690f68c7f | |||
7f19b83828 | |||
98a1ec82db | |||
63313ff964 | |||
210e32bed4 | |||
5b325a8ff9 | |||
4582e0c817 | |||
e2d1c5d6a1 | |||
4019ad7d31 | |||
e7df21e91a | |||
b2e30c9f6d | |||
0b27b5cc06 | |||
347e9c32fe | |||
cc18f58e4c | |||
dbd2e963a1 | |||
6928ca5329 | |||
9ece160aa8 | |||
6115c1bcac | |||
e026e94cbf | |||
56cdd8d6af | |||
899e6760e1 | |||
864d0ffcc6 | |||
c6f417f8c8 | |||
9091fa5bd8 | |||
418af7874d | |||
4972cc9daa | |||
ff905e1491 | |||
64b0184a17 | |||
97cbe44fb5 | |||
452d8778c5 | |||
02ad1a748e | |||
c5a32e2b1b | |||
702300f761 | |||
efcaa08b70 | |||
7f7369b3a8 | |||
45a9a95888 |
63
CHANGELOG.md
63
CHANGELOG.md
@@ -1,5 +1,66 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 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`
|
||||||
|
* `Common`:
|
||||||
|
* Extension `Pagination#reverse` has been added
|
||||||
|
* Factory `PaginationByIndexes`
|
||||||
|
* Shortcut `calculatePagesNumber` with reversed parameters
|
||||||
|
* Value `emptyPagination` for empty `SimplePagination` cases
|
||||||
|
|
||||||
## 0.2.0
|
## 0.2.0
|
||||||
|
|
||||||
* `Repos`
|
* `Repos`
|
||||||
@@ -10,6 +71,7 @@
|
|||||||
* `ExposedCRUDRepo` now extends `ExposedRepo` instead of simple `Repo`
|
* `ExposedCRUDRepo` now extends `ExposedRepo` instead of simple `Repo`
|
||||||
* New extension `initTable` for classes which are `Table` and `ExposedRepo` at the same time
|
* New extension `initTable` for classes which are `Table` and `ExposedRepo` at the same time
|
||||||
* `KeyValue`:
|
* `KeyValue`:
|
||||||
|
* `tableName` parameter
|
||||||
* Class `AbstractExposedReadKeyValueRepo`
|
* Class `AbstractExposedReadKeyValueRepo`
|
||||||
* Renamed to `ExposedReadKeyValueRepo`
|
* Renamed to `ExposedReadKeyValueRepo`
|
||||||
* Changed incoming types to `ColumnAllocator`
|
* Changed incoming types to `ColumnAllocator`
|
||||||
@@ -20,6 +82,7 @@
|
|||||||
* Changed incoming types to `ColumnAllocator`
|
* Changed incoming types to `ColumnAllocator`
|
||||||
* `open` instead of `abstract`
|
* `open` instead of `abstract`
|
||||||
* `OneToMany`:
|
* `OneToMany`:
|
||||||
|
* `tableName` parameter
|
||||||
* Class `AbstractExposedReadOneToManyKeyValueRepo`
|
* Class `AbstractExposedReadOneToManyKeyValueRepo`
|
||||||
* Renamed to `ExposedReadOneToManyKeyValueRepo`
|
* Renamed to `ExposedReadOneToManyKeyValueRepo`
|
||||||
* Changed incoming arguments order
|
* Changed incoming arguments order
|
||||||
|
@@ -1,24 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
function parse() {
|
|
||||||
version=$1
|
|
||||||
|
|
||||||
while IFS= read -r line && [ -z "`echo $line | grep -e "^#\+ $version"`" ]
|
|
||||||
do
|
|
||||||
: # do nothing
|
|
||||||
done
|
|
||||||
|
|
||||||
while IFS= read -r line && [ -z "`echo $line | grep -e "^#\+"`" ]
|
|
||||||
do
|
|
||||||
echo "$line"
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
version=$1
|
|
||||||
file=$2
|
|
||||||
|
|
||||||
if [ -n "$file" ]; then
|
|
||||||
parse $version < "$file"
|
|
||||||
else
|
|
||||||
parse $version
|
|
||||||
fi
|
|
24
changelog_parser.sh
Executable file
24
changelog_parser.sh
Executable file
@@ -0,0 +1,24 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
function parse() {
|
||||||
|
version="$1"
|
||||||
|
|
||||||
|
while IFS= read -r line && [ -z "`echo "$line" | grep -e "^#\+ $version"`" ]
|
||||||
|
do
|
||||||
|
: # do nothing
|
||||||
|
done
|
||||||
|
|
||||||
|
while IFS= read -r line && [ -z "`echo "$line" | grep -e "^#\+"`" ]
|
||||||
|
do
|
||||||
|
echo "$line"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
version="$1"
|
||||||
|
file="$2"
|
||||||
|
|
||||||
|
if [ -n "$file" ]; then
|
||||||
|
parse "$version" < "$file"
|
||||||
|
else
|
||||||
|
parse "$version"
|
||||||
|
fi
|
@@ -1,10 +1,166 @@
|
|||||||
|
@file:Suppress("NOTHING_TO_INLINE")
|
||||||
|
|
||||||
package dev.inmo.micro_utils.common
|
package dev.inmo.micro_utils.common
|
||||||
|
|
||||||
fun <T> Iterable<T>.syncWith(
|
private inline fun <T> getObject(
|
||||||
other: Iterable<T>,
|
additional: MutableList<T>,
|
||||||
removed: (List<T>) -> Unit = {},
|
iterator: Iterator<T>
|
||||||
added: (List<T>) -> Unit = {}
|
): T? = when {
|
||||||
) {
|
additional.isNotEmpty() -> additional.removeFirst()
|
||||||
removed(filter { it !in other })
|
iterator.hasNext() -> iterator.next()
|
||||||
added(other.filter { it !in this })
|
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 })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -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
|
@@ -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
|
@@ -17,3 +17,15 @@ fun <T> CoroutineScope.actor(
|
|||||||
return channel
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -1,9 +1,11 @@
|
|||||||
package dev.inmo.micro_utils.coroutines
|
package dev.inmo.micro_utils.coroutines
|
||||||
|
|
||||||
import kotlinx.coroutines.channels.*
|
import kotlinx.coroutines.channels.*
|
||||||
import kotlinx.coroutines.flow.*
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.asFlow
|
||||||
|
|
||||||
@Suppress("FunctionName")
|
@Suppress("FunctionName")
|
||||||
|
@Deprecated("Deprecated due to stabilization of SharedFlow and StateFlow")
|
||||||
fun <T> BroadcastFlow(
|
fun <T> BroadcastFlow(
|
||||||
internalChannelSize: Int = Channel.BUFFERED
|
internalChannelSize: Int = Channel.BUFFERED
|
||||||
): BroadcastFlow<T> {
|
): BroadcastFlow<T> {
|
||||||
@@ -15,6 +17,7 @@ fun <T> BroadcastFlow(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated("Deprecated due to stabilization of SharedFlow and StateFlow")
|
||||||
class BroadcastFlow<T> internal constructor(
|
class BroadcastFlow<T> internal constructor(
|
||||||
private val channel: BroadcastChannel<T>,
|
private val channel: BroadcastChannel<T>,
|
||||||
private val flow: Flow<T>
|
private val flow: Flow<T>
|
||||||
|
@@ -5,29 +5,64 @@ import kotlinx.coroutines.channels.BroadcastChannel
|
|||||||
import kotlinx.coroutines.channels.Channel
|
import kotlinx.coroutines.channels.Channel
|
||||||
import kotlinx.coroutines.flow.*
|
import kotlinx.coroutines.flow.*
|
||||||
|
|
||||||
|
const val defaultBroadcastStateFlowReplayCacheSize = 1
|
||||||
|
|
||||||
|
@Deprecated("Deprecated due to stabilization of SharedFlow and StateFlow")
|
||||||
class BroadcastStateFlow<T> internal constructor(
|
class BroadcastStateFlow<T> internal constructor(
|
||||||
parentFlow: Flow<T>,
|
parentFlow: Flow<T>,
|
||||||
private val stateGetter: () -> T
|
initial: T,
|
||||||
|
replayCacheSize: Int = defaultBroadcastStateFlowReplayCacheSize,
|
||||||
|
replayScope: CoroutineScope
|
||||||
) : StateFlow<T>, Flow<T> by parentFlow {
|
) : 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
|
override val value: T
|
||||||
get() = stateGetter()
|
get() = deque.last()
|
||||||
}
|
|
||||||
|
|
||||||
fun <T> BroadcastChannel<T>.asStateFlow(value: T, scope: CoroutineScope): StateFlow<T> = asFlow().let {
|
init {
|
||||||
var state: T = value
|
if (replayCacheSize < 1) {
|
||||||
it.onEach { state = it }.launchIn(scope)
|
error("Replay cache size can't be less than 1, but was $replayCacheSize")
|
||||||
BroadcastStateFlow(it) {
|
}
|
||||||
state
|
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
|
channelSize
|
||||||
).let {
|
).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)
|
||||||
|
|
||||||
|
@@ -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
|
||||||
|
)
|
@@ -21,3 +21,10 @@ suspend inline fun <T> safely(
|
|||||||
onException(e)
|
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
88
dokka/build.gradle
Normal 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
3
dokka/gradle.properties
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
dokka_version=1.4.0
|
||||||
|
|
||||||
|
org.gradle.jvmargs=-Xmx1024m
|
@@ -1,8 +1,11 @@
|
|||||||
private String getCurrentVersionChangelog() {
|
private String getCurrentVersionChangelog() {
|
||||||
OutputStream changelogDataOS = new ByteArrayOutputStream()
|
OutputStream changelogDataOS = new ByteArrayOutputStream()
|
||||||
|
exec {
|
||||||
|
commandLine 'chmod', "+x", './changelog_parser.sh'
|
||||||
|
}
|
||||||
exec {
|
exec {
|
||||||
standardOutput = changelogDataOS
|
standardOutput = changelogDataOS
|
||||||
commandLine './changelog_info_retriever', "$library_version", 'CHANGELOG.md'
|
commandLine './changelog_parser.sh', "${project.version}", 'CHANGELOG.md'
|
||||||
}
|
}
|
||||||
|
|
||||||
return changelogDataOS.toString().trim()
|
return changelogDataOS.toString().trim()
|
||||||
@@ -18,9 +21,9 @@ if (new File(projectDir, "secret.gradle").exists()) {
|
|||||||
owner "InsanusMokrassar"
|
owner "InsanusMokrassar"
|
||||||
repo "MicroUtils"
|
repo "MicroUtils"
|
||||||
|
|
||||||
tagName "$library_version"
|
tagName "${project.version}"
|
||||||
releaseName "$library_version"
|
releaseName "${project.version}"
|
||||||
targetCommitish "$library_version"
|
targetCommitish "${project.version}"
|
||||||
|
|
||||||
body getCurrentVersionChangelog()
|
body getCurrentVersionChangelog()
|
||||||
}
|
}
|
||||||
|
@@ -5,9 +5,9 @@ kotlin.incremental=true
|
|||||||
kotlin.incremental.js=true
|
kotlin.incremental.js=true
|
||||||
|
|
||||||
kotlin_version=1.4.10
|
kotlin_version=1.4.10
|
||||||
kotlin_coroutines_version=1.3.9
|
kotlin_coroutines_version=1.4.0
|
||||||
kotlin_serialisation_core_version=1.0.0
|
kotlin_serialisation_core_version=1.0.1
|
||||||
kotlin_exposed_version=0.27.1
|
kotlin_exposed_version=0.28.1
|
||||||
|
|
||||||
ktor_version=1.4.1
|
ktor_version=1.4.1
|
||||||
|
|
||||||
@@ -19,4 +19,4 @@ github_release_plugin_version=2.2.12
|
|||||||
uuidVersion=0.2.2
|
uuidVersion=0.2.2
|
||||||
|
|
||||||
group=dev.inmo
|
group=dev.inmo
|
||||||
version=0.2.0
|
version=0.2.6
|
||||||
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,5 +1,5 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-bin.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
@@ -4,7 +4,8 @@ import dev.inmo.micro_utils.coroutines.safely
|
|||||||
import dev.inmo.micro_utils.ktor.common.*
|
import dev.inmo.micro_utils.ktor.common.*
|
||||||
import io.ktor.client.HttpClient
|
import io.ktor.client.HttpClient
|
||||||
import io.ktor.client.features.websocket.ws
|
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.Flow
|
||||||
import kotlinx.coroutines.flow.channelFlow
|
import kotlinx.coroutines.flow.channelFlow
|
||||||
import kotlinx.serialization.DeserializationStrategy
|
import kotlinx.serialization.DeserializationStrategy
|
||||||
|
@@ -4,7 +4,8 @@ import dev.inmo.micro_utils.ktor.common.*
|
|||||||
import io.ktor.client.HttpClient
|
import io.ktor.client.HttpClient
|
||||||
import io.ktor.client.request.get
|
import io.ktor.client.request.get
|
||||||
import io.ktor.client.request.post
|
import io.ktor.client.request.post
|
||||||
import kotlinx.serialization.*
|
import kotlinx.serialization.DeserializationStrategy
|
||||||
|
import kotlinx.serialization.SerializationStrategy
|
||||||
|
|
||||||
typealias BodyPair<T> = Pair<SerializationStrategy<T>, T>
|
typealias BodyPair<T> = Pair<SerializationStrategy<T>, T>
|
||||||
|
|
||||||
|
@@ -22,13 +22,11 @@ fun <T> Route.includeWebsocketHandling(
|
|||||||
converter: (T) -> StandardKtorSerialInputData
|
converter: (T) -> StandardKtorSerialInputData
|
||||||
) {
|
) {
|
||||||
webSocket(suburl) {
|
webSocket(suburl) {
|
||||||
// println("connected")
|
|
||||||
safely {
|
safely {
|
||||||
flow.collect {
|
flow.collect {
|
||||||
send(converter(it))
|
send(converter(it))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// println("disconnected")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -7,8 +7,8 @@ import io.ktor.http.HttpStatusCode
|
|||||||
import io.ktor.request.receive
|
import io.ktor.request.receive
|
||||||
import io.ktor.response.respond
|
import io.ktor.response.respond
|
||||||
import io.ktor.response.respondBytes
|
import io.ktor.response.respondBytes
|
||||||
import io.ktor.util.toByteArray
|
import kotlinx.serialization.DeserializationStrategy
|
||||||
import kotlinx.serialization.*
|
import kotlinx.serialization.SerializationStrategy
|
||||||
|
|
||||||
suspend fun <T> ApplicationCall.unianswer(
|
suspend fun <T> ApplicationCall.unianswer(
|
||||||
answerSerializer: SerializationStrategy<T>,
|
answerSerializer: SerializationStrategy<T>,
|
||||||
|
@@ -28,6 +28,15 @@ interface Pagination {
|
|||||||
val Pagination.firstIndex: Int
|
val Pagination.firstIndex: Int
|
||||||
get() = page * size
|
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:
|
* 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.
|
* you will retrieve [Pagination.firstIndex] == 10 and [Pagination.lastIndex] == 19.
|
||||||
*/
|
*/
|
||||||
val Pagination.lastIndex: Int
|
val Pagination.lastIndex: Int
|
||||||
get() = firstIndex + size - 1
|
get() = lastIndexExclusive - 1
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates pages count for given [datasetSize]
|
* Calculates pages count for given [datasetSize]
|
||||||
@@ -43,6 +52,11 @@ val Pagination.lastIndex: Int
|
|||||||
fun calculatePagesNumber(datasetSize: Long, pageSize: Int): Int {
|
fun calculatePagesNumber(datasetSize: Long, pageSize: Int): Int {
|
||||||
return ceil(datasetSize.toDouble() / pageSize).toInt()
|
return ceil(datasetSize.toDouble() / pageSize).toInt()
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Calculates pages count for given [datasetSize]. As a fact, it is shortcut for [calculatePagesNumber]
|
||||||
|
* @return calculated page number which can be correctly used in [PaginationResult] as [PaginationResult.page] value
|
||||||
|
*/
|
||||||
|
fun calculatePagesNumber(pageSize: Int, datasetSize: Long): Int = calculatePagesNumber(datasetSize, pageSize)
|
||||||
/**
|
/**
|
||||||
* Calculates pages count for given [datasetSize]
|
* Calculates pages count for given [datasetSize]
|
||||||
*/
|
*/
|
||||||
|
@@ -14,6 +14,8 @@ inline fun FirstPagePagination(size: Int = defaultMediumPageSize) =
|
|||||||
size = size
|
size = size
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val emptyPagination = Pagination(0, 0)
|
||||||
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
inline fun Pagination.nextPage() =
|
inline fun Pagination.nextPage() =
|
||||||
SimplePagination(
|
SimplePagination(
|
||||||
@@ -27,7 +29,21 @@ data class SimplePagination(
|
|||||||
override val size: Int
|
override val size: Int
|
||||||
) : Pagination
|
) : Pagination
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factory for [SimplePagination]
|
||||||
|
*/
|
||||||
fun Pagination(
|
fun Pagination(
|
||||||
page: Int,
|
page: Int,
|
||||||
size: Int
|
size: Int
|
||||||
) = SimplePagination(page, size)
|
) = SimplePagination(page, size)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param firstIndex Inclusive first index of pagination
|
||||||
|
* @param lastIndex INCLUSIVE last index of pagination (last index of object covered by result [SimplePagination])
|
||||||
|
*/
|
||||||
|
fun PaginationByIndexes(
|
||||||
|
firstIndex: Int,
|
||||||
|
lastIndex: Int
|
||||||
|
) = maxOf(0, (lastIndex - firstIndex + 1)).let { size ->
|
||||||
|
Pagination(calculatePage(firstIndex, size), size)
|
||||||
|
}
|
||||||
|
@@ -21,7 +21,12 @@ fun <T> Iterable<T>.paginate(with: Pagination): PaginationResult<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun <T> List<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,
|
with,
|
||||||
size.toLong()
|
size.toLong()
|
||||||
)
|
)
|
||||||
|
@@ -0,0 +1,28 @@
|
|||||||
|
package dev.inmo.micro_utils.pagination.utils
|
||||||
|
|
||||||
|
import dev.inmo.micro_utils.pagination.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Example:
|
||||||
|
*
|
||||||
|
* * `|__f__l_______________________|` will be transformed to `|_______________________f__l__|`
|
||||||
|
* * `|__f__l_|` will be transformed to `|__f__l_|`
|
||||||
|
*
|
||||||
|
* @return Reversed version of this [Pagination]
|
||||||
|
*/
|
||||||
|
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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shortcut for [reverse]
|
||||||
|
*/
|
||||||
|
fun Pagination.reverse(objectsCount: Int) = reverse(objectsCount.toLong())
|
@@ -0,0 +1,24 @@
|
|||||||
|
package dev.inmo.micro_utils.pagination.utils
|
||||||
|
|
||||||
|
import dev.inmo.micro_utils.pagination.*
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
class PaginationReversingTests {
|
||||||
|
@Test
|
||||||
|
fun testThatCommonCaseWorksOk() {
|
||||||
|
val pageSize = 3
|
||||||
|
val collectionSize = 9
|
||||||
|
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
}
|
@@ -1,7 +1,6 @@
|
|||||||
package dev.inmo.micro_utils.repos
|
package dev.inmo.micro_utils.repos
|
||||||
|
|
||||||
import dev.inmo.micro_utils.pagination.Pagination
|
import dev.inmo.micro_utils.pagination.*
|
||||||
import dev.inmo.micro_utils.pagination.PaginationResult
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
interface ReadOneToManyKeyValueRepo<Key, Value> : Repo {
|
interface ReadOneToManyKeyValueRepo<Key, Value> : Repo {
|
||||||
@@ -11,6 +10,30 @@ interface ReadOneToManyKeyValueRepo<Key, Value> : Repo {
|
|||||||
suspend fun contains(k: Key, v: Value): Boolean
|
suspend fun contains(k: Key, v: Value): Boolean
|
||||||
suspend fun count(k: Key): Long
|
suspend fun count(k: Key): Long
|
||||||
suspend fun count(): 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"))
|
@Deprecated("Renamed", ReplaceWith("ReadOneToManyKeyValueRepo", "dev.inmo.micro_utils.repos.ReadOneToManyKeyValueRepo"))
|
||||||
typealias OneToManyReadKeyValueRepo<Key, Value> = ReadOneToManyKeyValueRepo<Key, Value>
|
typealias OneToManyReadKeyValueRepo<Key, Value> = ReadOneToManyKeyValueRepo<Key, Value>
|
||||||
@@ -20,11 +43,51 @@ interface WriteOneToManyKeyValueRepo<Key, Value> : Repo {
|
|||||||
val onValueRemoved: Flow<Pair<Key, Value>>
|
val onValueRemoved: Flow<Pair<Key, Value>>
|
||||||
val onDataCleared: Flow<Key>
|
val onDataCleared: Flow<Key>
|
||||||
|
|
||||||
|
suspend fun add(toAdd: Map<Key, List<Value>>) = toAdd.forEach { (k, values) ->
|
||||||
|
values.forEach { v ->
|
||||||
|
add(k, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@Deprecated("Will be extracted as extension for other add method")
|
||||||
suspend fun add(k: Key, v: Value)
|
suspend fun add(k: Key, v: Value)
|
||||||
|
|
||||||
|
suspend fun remove(toRemove: Map<Key, List<Value>>) = toRemove.forEach { (k, values) ->
|
||||||
|
values.forEach { v ->
|
||||||
|
remove(k, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@Deprecated("Will be extracted as extension for other remove method")
|
||||||
suspend fun remove(k: Key, v: Value)
|
suspend fun remove(k: Key, v: Value)
|
||||||
|
|
||||||
suspend fun clear(k: Key)
|
suspend fun clear(k: Key)
|
||||||
}
|
}
|
||||||
@Deprecated("Renamed", ReplaceWith("WriteOneToManyKeyValueRepo", "dev.inmo.micro_utils.repos.WriteOneToManyKeyValueRepo"))
|
@Deprecated("Renamed", ReplaceWith("WriteOneToManyKeyValueRepo", "dev.inmo.micro_utils.repos.WriteOneToManyKeyValueRepo"))
|
||||||
typealias OneToManyWriteKeyValueRepo<Key, Value> = WriteOneToManyKeyValueRepo<Key, Value>
|
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())
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
package dev.inmo.micro_utils.repos.pagination
|
package dev.inmo.micro_utils.repos.pagination
|
||||||
|
|
||||||
import dev.inmo.micro_utils.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(
|
suspend inline fun <Key, Value, REPO : ReadStandardKeyValueRepo<Key, Value>> REPO.doForAll(
|
||||||
@Suppress("REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE")
|
@Suppress("REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE")
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
package dev.inmo.micro_utils.repos.pagination
|
package dev.inmo.micro_utils.repos.pagination
|
||||||
|
|
||||||
import dev.inmo.micro_utils.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(
|
suspend inline fun <Key, Value, REPO : ReadOneToManyKeyValueRepo<Key, Value>> REPO.doForAll(
|
||||||
@Suppress("REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE")
|
@Suppress("REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE")
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
package dev.inmo.micro_utils.repos.exposed
|
package dev.inmo.micro_utils.repos.exposed
|
||||||
|
|
||||||
import dev.inmo.micro_utils.repos.Repo
|
|
||||||
import org.jetbrains.exposed.sql.*
|
import org.jetbrains.exposed.sql.*
|
||||||
|
|
||||||
interface ExposedCRUDRepo<ObjectType, IdType> : ExposedRepo {
|
interface ExposedCRUDRepo<ObjectType, IdType> : ExposedRepo {
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
package dev.inmo.micro_utils.repos.exposed.keyvalue
|
package dev.inmo.micro_utils.repos.exposed.keyvalue
|
||||||
|
|
||||||
import dev.inmo.micro_utils.repos.StandardKeyValueRepo
|
import dev.inmo.micro_utils.repos.StandardKeyValueRepo
|
||||||
import dev.inmo.micro_utils.repos.exposed.*
|
import dev.inmo.micro_utils.repos.exposed.ColumnAllocator
|
||||||
import kotlinx.coroutines.channels.BroadcastChannel
|
import kotlinx.coroutines.channels.BroadcastChannel
|
||||||
import kotlinx.coroutines.channels.Channel
|
import kotlinx.coroutines.channels.Channel
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
@@ -12,11 +12,13 @@ import org.jetbrains.exposed.sql.transactions.transaction
|
|||||||
open class ExposedKeyValueRepo<Key, Value>(
|
open class ExposedKeyValueRepo<Key, Value>(
|
||||||
database: Database,
|
database: Database,
|
||||||
keyColumnAllocator: ColumnAllocator<Key>,
|
keyColumnAllocator: ColumnAllocator<Key>,
|
||||||
valueColumnAllocator: ColumnAllocator<Value>
|
valueColumnAllocator: ColumnAllocator<Value>,
|
||||||
|
tableName: String? = null
|
||||||
) : StandardKeyValueRepo<Key, Value>, ExposedReadKeyValueRepo<Key, Value>(
|
) : StandardKeyValueRepo<Key, Value>, ExposedReadKeyValueRepo<Key, Value>(
|
||||||
database,
|
database,
|
||||||
keyColumnAllocator,
|
keyColumnAllocator,
|
||||||
valueColumnAllocator
|
valueColumnAllocator,
|
||||||
|
tableName
|
||||||
) {
|
) {
|
||||||
private val onNewValueChannel = BroadcastChannel<Pair<Key, Value>>(Channel.BUFFERED)
|
private val onNewValueChannel = BroadcastChannel<Pair<Key, Value>>(Channel.BUFFERED)
|
||||||
private val onValueRemovedChannel = BroadcastChannel<Key>(Channel.BUFFERED)
|
private val onValueRemovedChannel = BroadcastChannel<Key>(Channel.BUFFERED)
|
||||||
@@ -24,8 +26,6 @@ open class ExposedKeyValueRepo<Key, Value>(
|
|||||||
override val onNewValue: Flow<Pair<Key, Value>> = onNewValueChannel.asFlow()
|
override val onNewValue: Flow<Pair<Key, Value>> = onNewValueChannel.asFlow()
|
||||||
override val onValueRemoved: Flow<Key> = onValueRemovedChannel.asFlow()
|
override val onValueRemoved: Flow<Key> = onValueRemovedChannel.asFlow()
|
||||||
|
|
||||||
init { initTable() }
|
|
||||||
|
|
||||||
override suspend fun set(k: Key, v: Value) {
|
override suspend fun set(k: Key, v: Value) {
|
||||||
transaction(database) {
|
transaction(database) {
|
||||||
if (select { keyColumn.eq(k) }.limit(1).any()) {
|
if (select { keyColumn.eq(k) }.limit(1).any()) {
|
||||||
|
@@ -2,7 +2,8 @@ package dev.inmo.micro_utils.repos.exposed.keyvalue
|
|||||||
|
|
||||||
import dev.inmo.micro_utils.pagination.*
|
import dev.inmo.micro_utils.pagination.*
|
||||||
import dev.inmo.micro_utils.repos.ReadStandardKeyValueRepo
|
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.*
|
||||||
import org.jetbrains.exposed.sql.transactions.transaction
|
import org.jetbrains.exposed.sql.transactions.transaction
|
||||||
|
|
||||||
@@ -10,13 +11,12 @@ open class ExposedReadKeyValueRepo<Key, Value>(
|
|||||||
override val database: Database,
|
override val database: Database,
|
||||||
keyColumnAllocator: ColumnAllocator<Key>,
|
keyColumnAllocator: ColumnAllocator<Key>,
|
||||||
valueColumnAllocator: ColumnAllocator<Value>,
|
valueColumnAllocator: ColumnAllocator<Value>,
|
||||||
) : ReadStandardKeyValueRepo<Key, Value>, ExposedRepo, Table() {
|
tableName: String? = null
|
||||||
|
) : ReadStandardKeyValueRepo<Key, Value>, ExposedRepo, Table(tableName ?: "") {
|
||||||
protected val keyColumn: Column<Key> = keyColumnAllocator()
|
protected val keyColumn: Column<Key> = keyColumnAllocator()
|
||||||
protected val valueColumn: Column<Value> = valueColumnAllocator()
|
protected val valueColumn: Column<Value> = valueColumnAllocator()
|
||||||
override val primaryKey: PrimaryKey = PrimaryKey(keyColumn, valueColumn)
|
override val primaryKey: PrimaryKey = PrimaryKey(keyColumn, valueColumn)
|
||||||
|
|
||||||
init { initTable() }
|
|
||||||
|
|
||||||
override suspend fun get(k: Key): Value? = transaction(database) {
|
override suspend fun get(k: Key): Value? = transaction(database) {
|
||||||
select { keyColumn.eq(k) }.limit(1).firstOrNull() ?.getOrNull(valueColumn)
|
select { keyColumn.eq(k) }.limit(1).firstOrNull() ?.getOrNull(valueColumn)
|
||||||
}
|
}
|
||||||
|
@@ -3,7 +3,6 @@ package dev.inmo.micro_utils.repos.exposed.onetomany
|
|||||||
import dev.inmo.micro_utils.coroutines.BroadcastFlow
|
import dev.inmo.micro_utils.coroutines.BroadcastFlow
|
||||||
import dev.inmo.micro_utils.repos.OneToManyKeyValueRepo
|
import dev.inmo.micro_utils.repos.OneToManyKeyValueRepo
|
||||||
import dev.inmo.micro_utils.repos.exposed.ColumnAllocator
|
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.Flow
|
||||||
import org.jetbrains.exposed.sql.*
|
import org.jetbrains.exposed.sql.*
|
||||||
import org.jetbrains.exposed.sql.transactions.transaction
|
import org.jetbrains.exposed.sql.transactions.transaction
|
||||||
@@ -11,11 +10,13 @@ import org.jetbrains.exposed.sql.transactions.transaction
|
|||||||
open class ExposedOneToManyKeyValueRepo<Key, Value>(
|
open class ExposedOneToManyKeyValueRepo<Key, Value>(
|
||||||
database: Database,
|
database: Database,
|
||||||
keyColumnAllocator: ColumnAllocator<Key>,
|
keyColumnAllocator: ColumnAllocator<Key>,
|
||||||
valueColumnAllocator: ColumnAllocator<Value>
|
valueColumnAllocator: ColumnAllocator<Value>,
|
||||||
|
tableName: String? = null
|
||||||
) : OneToManyKeyValueRepo<Key, Value>, ExposedReadOneToManyKeyValueRepo<Key, Value>(
|
) : OneToManyKeyValueRepo<Key, Value>, ExposedReadOneToManyKeyValueRepo<Key, Value>(
|
||||||
database,
|
database,
|
||||||
keyColumnAllocator,
|
keyColumnAllocator,
|
||||||
valueColumnAllocator
|
valueColumnAllocator,
|
||||||
|
tableName
|
||||||
) {
|
) {
|
||||||
protected val _onNewValue: BroadcastFlow<Pair<Key, Value>> = BroadcastFlow()
|
protected val _onNewValue: BroadcastFlow<Pair<Key, Value>> = BroadcastFlow()
|
||||||
override val onNewValue: Flow<Pair<Key, Value>>
|
override val onNewValue: Flow<Pair<Key, Value>>
|
||||||
@@ -27,8 +28,6 @@ open class ExposedOneToManyKeyValueRepo<Key, Value>(
|
|||||||
override val onDataCleared: Flow<Key>
|
override val onDataCleared: Flow<Key>
|
||||||
get() = _onDataCleared
|
get() = _onDataCleared
|
||||||
|
|
||||||
init { initTable() }
|
|
||||||
|
|
||||||
override suspend fun add(k: Key, v: Value) {
|
override suspend fun add(k: Key, v: Value) {
|
||||||
transaction(database) {
|
transaction(database) {
|
||||||
insert {
|
insert {
|
||||||
|
@@ -9,8 +9,9 @@ import org.jetbrains.exposed.sql.transactions.transaction
|
|||||||
open class ExposedReadOneToManyKeyValueRepo<Key, Value>(
|
open class ExposedReadOneToManyKeyValueRepo<Key, Value>(
|
||||||
override val database: Database,
|
override val database: Database,
|
||||||
keyColumnAllocator: ColumnAllocator<Key>,
|
keyColumnAllocator: ColumnAllocator<Key>,
|
||||||
valueColumnAllocator: ColumnAllocator<Value>
|
valueColumnAllocator: ColumnAllocator<Value>,
|
||||||
) : ReadOneToManyKeyValueRepo<Key, Value>, ExposedRepo, Table() {
|
tableName: String? = null
|
||||||
|
) : ReadOneToManyKeyValueRepo<Key, Value>, ExposedRepo, Table(tableName ?: "") {
|
||||||
protected val keyColumn: Column<Key> = keyColumnAllocator()
|
protected val keyColumn: Column<Key> = keyColumnAllocator()
|
||||||
protected val valueColumn: Column<Value> = valueColumnAllocator()
|
protected val valueColumn: Column<Value> = valueColumnAllocator()
|
||||||
|
|
||||||
|
@@ -1,7 +1,10 @@
|
|||||||
package dev.inmo.micro_utils.repos
|
package dev.inmo.micro_utils.repos
|
||||||
|
|
||||||
import dev.inmo.micro_utils.coroutines.BroadcastFlow
|
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.Flow
|
||||||
|
|
||||||
class ReadMapKeyValueRepo<Key, Value>(
|
class ReadMapKeyValueRepo<Key, Value>(
|
||||||
@@ -13,31 +16,27 @@ class ReadMapKeyValueRepo<Key, Value>(
|
|||||||
pagination: Pagination,
|
pagination: Pagination,
|
||||||
reversed: Boolean
|
reversed: Boolean
|
||||||
): PaginationResult<Value> {
|
): PaginationResult<Value> {
|
||||||
val firstIndex: Int = if (reversed) {
|
val values = map.values
|
||||||
val size = map.size
|
val actualPagination = if (reversed) pagination.reverse(values.size) else pagination
|
||||||
(size - pagination.lastIndex).let { if (it < 0) 0 else it }
|
return values.paginate(actualPagination).let {
|
||||||
} else {
|
if (reversed) {
|
||||||
pagination.firstIndex
|
it.copy(results = it.results.reversed())
|
||||||
|
} else {
|
||||||
|
it
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return map.keys.drop(firstIndex).take(pagination.size).mapNotNull { map[it] }.createPaginationResult(
|
|
||||||
firstIndex,
|
|
||||||
count()
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun keys(pagination: Pagination, reversed: Boolean): PaginationResult<Key> {
|
override suspend fun keys(pagination: Pagination, reversed: Boolean): PaginationResult<Key> {
|
||||||
val firstIndex: Int = if (reversed) {
|
val keys = map.keys
|
||||||
val size = map.size
|
val actualPagination = if (reversed) pagination.reverse(keys.size) else pagination
|
||||||
(size - pagination.lastIndex).let { if (it < 0) 0 else it }
|
return keys.paginate(actualPagination).let {
|
||||||
} else {
|
if (reversed) {
|
||||||
pagination.firstIndex
|
it.copy(results = it.results.reversed())
|
||||||
|
} else {
|
||||||
|
it
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return map.keys.drop(firstIndex).take(pagination.size).createPaginationResult(
|
|
||||||
firstIndex,
|
|
||||||
count()
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun contains(key: Key): Boolean = map.containsKey(key)
|
override suspend fun contains(key: Key): Boolean = map.containsKey(key)
|
||||||
|
@@ -3,6 +3,7 @@ package dev.inmo.micro_utils.repos
|
|||||||
import dev.inmo.micro_utils.coroutines.BroadcastFlow
|
import dev.inmo.micro_utils.coroutines.BroadcastFlow
|
||||||
import dev.inmo.micro_utils.pagination.*
|
import dev.inmo.micro_utils.pagination.*
|
||||||
import dev.inmo.micro_utils.pagination.utils.paginate
|
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.Flow
|
||||||
|
|
||||||
class MapReadOneToManyKeyValueRepo<Key, Value>(
|
class MapReadOneToManyKeyValueRepo<Key, Value>(
|
||||||
@@ -13,8 +14,7 @@ class MapReadOneToManyKeyValueRepo<Key, Value>(
|
|||||||
|
|
||||||
return list.paginate(
|
return list.paginate(
|
||||||
if (reversed) {
|
if (reversed) {
|
||||||
val firstIndex = (map.size - pagination.lastIndex).let { if (it < 0) 0 else it }
|
pagination.reverse(list.size)
|
||||||
SimplePagination(firstIndex, pagination.size)
|
|
||||||
} else {
|
} else {
|
||||||
pagination
|
pagination
|
||||||
}
|
}
|
||||||
@@ -22,17 +22,15 @@ class MapReadOneToManyKeyValueRepo<Key, Value>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun keys(pagination: Pagination, reversed: Boolean): PaginationResult<Key> {
|
override suspend fun keys(pagination: Pagination, reversed: Boolean): PaginationResult<Key> {
|
||||||
val firstIndex: Int = if (reversed) {
|
val keys = map.keys
|
||||||
val size = map.size
|
val actualPagination = if (reversed) pagination.reverse(keys.size) else pagination
|
||||||
(size - pagination.lastIndex).let { if (it < 0) 0 else it }
|
return keys.paginate(actualPagination).let {
|
||||||
} else {
|
if (reversed) {
|
||||||
pagination.firstIndex
|
it.copy(results = it.results.reversed())
|
||||||
|
} else {
|
||||||
|
it
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return map.keys.drop(firstIndex).take(pagination.size).createPaginationResult(
|
|
||||||
firstIndex,
|
|
||||||
count()
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun contains(k: Key): Boolean = map.containsKey(k)
|
override suspend fun contains(k: Key): Boolean = map.containsKey(k)
|
||||||
|
@@ -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.encodeUrlQueryValue
|
||||||
import dev.inmo.micro_utils.ktor.client.uniget
|
import dev.inmo.micro_utils.ktor.client.uniget
|
||||||
import dev.inmo.micro_utils.ktor.common.buildStandardUrl
|
import dev.inmo.micro_utils.ktor.common.buildStandardUrl
|
||||||
import dev.inmo.micro_utils.pagination.Pagination
|
import dev.inmo.micro_utils.pagination.*
|
||||||
import dev.inmo.micro_utils.pagination.PaginationResult
|
|
||||||
import dev.inmo.micro_utils.pagination.asUrlQueryParts
|
|
||||||
import dev.inmo.micro_utils.repos.ReadStandardCRUDRepo
|
import dev.inmo.micro_utils.repos.ReadStandardCRUDRepo
|
||||||
import dev.inmo.micro_utils.repos.ktor.common.crud.*
|
import dev.inmo.micro_utils.repos.ktor.common.crud.*
|
||||||
import io.ktor.client.HttpClient
|
import io.ktor.client.HttpClient
|
||||||
|
@@ -1,8 +1,6 @@
|
|||||||
package dev.inmo.micro_utils.repos.ktor.client.crud
|
package dev.inmo.micro_utils.repos.ktor.client.crud
|
||||||
|
|
||||||
import dev.inmo.micro_utils.ktor.client.BodyPair
|
import dev.inmo.micro_utils.ktor.client.*
|
||||||
import dev.inmo.micro_utils.ktor.client.createStandardWebsocketFlow
|
|
||||||
import dev.inmo.micro_utils.ktor.client.unipost
|
|
||||||
import dev.inmo.micro_utils.ktor.common.buildStandardUrl
|
import dev.inmo.micro_utils.ktor.common.buildStandardUrl
|
||||||
import dev.inmo.micro_utils.repos.UpdatedValuePair
|
import dev.inmo.micro_utils.repos.UpdatedValuePair
|
||||||
import dev.inmo.micro_utils.repos.WriteStandardCRUDRepo
|
import dev.inmo.micro_utils.repos.WriteStandardCRUDRepo
|
||||||
|
@@ -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.encodeUrlQueryValue
|
||||||
import dev.inmo.micro_utils.ktor.client.uniget
|
import dev.inmo.micro_utils.ktor.client.uniget
|
||||||
import dev.inmo.micro_utils.ktor.common.buildStandardUrl
|
import dev.inmo.micro_utils.ktor.common.buildStandardUrl
|
||||||
import dev.inmo.micro_utils.pagination.Pagination
|
import dev.inmo.micro_utils.pagination.*
|
||||||
import dev.inmo.micro_utils.pagination.PaginationResult
|
|
||||||
import dev.inmo.micro_utils.pagination.asUrlQueryParts
|
|
||||||
import dev.inmo.micro_utils.repos.ReadStandardKeyValueRepo
|
import dev.inmo.micro_utils.repos.ReadStandardKeyValueRepo
|
||||||
import dev.inmo.micro_utils.repos.ktor.common.key_value.*
|
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.KSerializer
|
||||||
import kotlinx.serialization.builtins.serializer
|
import kotlinx.serialization.builtins.serializer
|
||||||
|
|
||||||
|
@@ -1,9 +1,7 @@
|
|||||||
package dev.inmo.micro_utils.repos.ktor.client.key_value
|
package dev.inmo.micro_utils.repos.ktor.client.key_value
|
||||||
|
|
||||||
import dev.inmo.micro_utils.repos.StandardKeyValueRepo
|
import dev.inmo.micro_utils.repos.*
|
||||||
import dev.inmo.micro_utils.repos.ReadStandardKeyValueRepo
|
import io.ktor.client.HttpClient
|
||||||
import dev.inmo.micro_utils.repos.WriteStandardKeyValueRepo
|
|
||||||
import io.ktor.client.*
|
|
||||||
import kotlinx.serialization.KSerializer
|
import kotlinx.serialization.KSerializer
|
||||||
|
|
||||||
class KtorStandartKeyValueRepo<K, V> (
|
class KtorStandartKeyValueRepo<K, V> (
|
||||||
|
@@ -1,12 +1,10 @@
|
|||||||
package dev.inmo.micro_utils.repos.ktor.client.key_value
|
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.*
|
||||||
import dev.inmo.micro_utils.ktor.client.createStandardWebsocketFlow
|
|
||||||
import dev.inmo.micro_utils.ktor.client.unipost
|
|
||||||
import dev.inmo.micro_utils.ktor.common.buildStandardUrl
|
import dev.inmo.micro_utils.ktor.common.buildStandardUrl
|
||||||
import dev.inmo.micro_utils.repos.WriteStandardKeyValueRepo
|
import dev.inmo.micro_utils.repos.WriteStandardKeyValueRepo
|
||||||
import dev.inmo.micro_utils.repos.ktor.common.key_value.*
|
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.coroutines.flow.Flow
|
||||||
import kotlinx.serialization.KSerializer
|
import kotlinx.serialization.KSerializer
|
||||||
import kotlinx.serialization.builtins.PairSerializer
|
import kotlinx.serialization.builtins.PairSerializer
|
||||||
|
@@ -1,8 +1,6 @@
|
|||||||
package dev.inmo.micro_utils.repos.ktor.client.one_to_many
|
package dev.inmo.micro_utils.repos.ktor.client.one_to_many
|
||||||
|
|
||||||
import dev.inmo.micro_utils.repos.OneToManyKeyValueRepo
|
import dev.inmo.micro_utils.repos.*
|
||||||
import dev.inmo.micro_utils.repos.ReadOneToManyKeyValueRepo
|
|
||||||
import dev.inmo.micro_utils.repos.WriteOneToManyKeyValueRepo
|
|
||||||
import io.ktor.client.HttpClient
|
import io.ktor.client.HttpClient
|
||||||
import kotlinx.serialization.KSerializer
|
import kotlinx.serialization.KSerializer
|
||||||
|
|
||||||
|
@@ -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.encodeUrlQueryValue
|
||||||
import dev.inmo.micro_utils.ktor.client.uniget
|
import dev.inmo.micro_utils.ktor.client.uniget
|
||||||
import dev.inmo.micro_utils.ktor.common.buildStandardUrl
|
import dev.inmo.micro_utils.ktor.common.buildStandardUrl
|
||||||
import dev.inmo.micro_utils.pagination.Pagination
|
import dev.inmo.micro_utils.pagination.*
|
||||||
import dev.inmo.micro_utils.pagination.PaginationResult
|
|
||||||
import dev.inmo.micro_utils.pagination.asUrlQueryParts
|
|
||||||
import dev.inmo.micro_utils.repos.ReadOneToManyKeyValueRepo
|
import dev.inmo.micro_utils.repos.ReadOneToManyKeyValueRepo
|
||||||
import dev.inmo.micro_utils.repos.ktor.common.one_to_many.*
|
import dev.inmo.micro_utils.repos.ktor.common.one_to_many.*
|
||||||
import io.ktor.client.HttpClient
|
import io.ktor.client.HttpClient
|
||||||
|
@@ -1,12 +1,10 @@
|
|||||||
package dev.inmo.micro_utils.repos.ktor.client.one_to_many
|
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.client.*
|
||||||
import dev.inmo.micro_utils.ktor.common.buildStandardUrl
|
import dev.inmo.micro_utils.ktor.common.buildStandardUrl
|
||||||
import dev.inmo.micro_utils.repos.WriteOneToManyKeyValueRepo
|
import dev.inmo.micro_utils.repos.WriteOneToManyKeyValueRepo
|
||||||
import dev.inmo.micro_utils.repos.ktor.common.one_to_many.*
|
import dev.inmo.micro_utils.repos.ktor.common.one_to_many.*
|
||||||
import io.ktor.client.HttpClient
|
import io.ktor.client.HttpClient
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.serialization.KSerializer
|
import kotlinx.serialization.KSerializer
|
||||||
import kotlinx.serialization.builtins.PairSerializer
|
import kotlinx.serialization.builtins.PairSerializer
|
||||||
|
@@ -1,8 +1,6 @@
|
|||||||
package dev.inmo.micro_utils.repos.ktor.server.crud
|
package dev.inmo.micro_utils.repos.ktor.server.crud
|
||||||
|
|
||||||
import dev.inmo.micro_utils.ktor.server.includeWebsocketHandling
|
import dev.inmo.micro_utils.ktor.server.*
|
||||||
import dev.inmo.micro_utils.ktor.server.unianswer
|
|
||||||
import dev.inmo.micro_utils.ktor.server.uniload
|
|
||||||
import dev.inmo.micro_utils.repos.WriteStandardCRUDRepo
|
import dev.inmo.micro_utils.repos.WriteStandardCRUDRepo
|
||||||
import dev.inmo.micro_utils.repos.ktor.common.crud.*
|
import dev.inmo.micro_utils.repos.ktor.common.crud.*
|
||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
|
@@ -1,7 +1,8 @@
|
|||||||
package dev.inmo.micro_utils.repos.ktor.server.key_value
|
package dev.inmo.micro_utils.repos.ktor.server.key_value
|
||||||
|
|
||||||
import dev.inmo.micro_utils.repos.StandardKeyValueRepo
|
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
|
import kotlinx.serialization.KSerializer
|
||||||
|
|
||||||
fun <K, V> Route.configureStandartKeyValueRepoRoutes(
|
fun <K, V> Route.configureStandartKeyValueRepoRoutes(
|
||||||
|
@@ -6,8 +6,9 @@ import dev.inmo.micro_utils.pagination.PaginationResult
|
|||||||
import dev.inmo.micro_utils.pagination.extractPagination
|
import dev.inmo.micro_utils.pagination.extractPagination
|
||||||
import dev.inmo.micro_utils.repos.ReadStandardKeyValueRepo
|
import dev.inmo.micro_utils.repos.ReadStandardKeyValueRepo
|
||||||
import dev.inmo.micro_utils.repos.ktor.common.key_value.*
|
import dev.inmo.micro_utils.repos.ktor.common.key_value.*
|
||||||
import io.ktor.application.*
|
import io.ktor.application.call
|
||||||
import io.ktor.routing.*
|
import io.ktor.routing.Route
|
||||||
|
import io.ktor.routing.get
|
||||||
import kotlinx.serialization.KSerializer
|
import kotlinx.serialization.KSerializer
|
||||||
import kotlinx.serialization.builtins.serializer
|
import kotlinx.serialization.builtins.serializer
|
||||||
|
|
||||||
|
@@ -4,8 +4,9 @@ import dev.inmo.micro_utils.ktor.server.includeWebsocketHandling
|
|||||||
import dev.inmo.micro_utils.ktor.server.uniload
|
import dev.inmo.micro_utils.ktor.server.uniload
|
||||||
import dev.inmo.micro_utils.repos.WriteStandardKeyValueRepo
|
import dev.inmo.micro_utils.repos.WriteStandardKeyValueRepo
|
||||||
import dev.inmo.micro_utils.repos.ktor.common.key_value.*
|
import dev.inmo.micro_utils.repos.ktor.common.key_value.*
|
||||||
import io.ktor.application.*
|
import io.ktor.application.call
|
||||||
import io.ktor.routing.*
|
import io.ktor.routing.Route
|
||||||
|
import io.ktor.routing.post
|
||||||
import kotlinx.serialization.KSerializer
|
import kotlinx.serialization.KSerializer
|
||||||
import kotlinx.serialization.builtins.PairSerializer
|
import kotlinx.serialization.builtins.PairSerializer
|
||||||
|
|
||||||
|
@@ -1,8 +1,6 @@
|
|||||||
package dev.inmo.micro_utils.repos.ktor.server.one_to_many
|
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.*
|
||||||
import dev.inmo.micro_utils.ktor.server.decodeUrlQueryValueOrSendError
|
|
||||||
import dev.inmo.micro_utils.ktor.server.unianswer
|
|
||||||
import dev.inmo.micro_utils.pagination.PaginationResult
|
import dev.inmo.micro_utils.pagination.PaginationResult
|
||||||
import dev.inmo.micro_utils.pagination.extractPagination
|
import dev.inmo.micro_utils.pagination.extractPagination
|
||||||
import dev.inmo.micro_utils.repos.ReadOneToManyKeyValueRepo
|
import dev.inmo.micro_utils.repos.ReadOneToManyKeyValueRepo
|
||||||
|
@@ -16,7 +16,9 @@ String[] includes = [
|
|||||||
":ktor:server",
|
":ktor:server",
|
||||||
":ktor:common",
|
":ktor:common",
|
||||||
":ktor:client",
|
":ktor:client",
|
||||||
":coroutines"
|
":coroutines",
|
||||||
|
|
||||||
|
":dokka"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user