mirror of
https://github.com/InsanusMokrassar/MicroUtils.git
synced 2025-09-19 07:19:22 +00:00
Compare commits
48 Commits
Author | SHA1 | Date | |
---|---|---|---|
7b5e84f80b | |||
27822a8a66 | |||
36f4e7ec37 | |||
187e84ad65 | |||
195fe221c4 | |||
6f9c19bbf6 | |||
65bdab4f7e | |||
c4d5fcfc22 | |||
43232afa62 | |||
18908a01d7 | |||
b22fbfb3bc | |||
57aaea88b6 | |||
140949c5ea | |||
639241e0d3 | |||
e0eb42bc2d | |||
7990b21cc5 | |||
2ba5c97709 | |||
33b7c85fc2 | |||
7f6c02ffdf | |||
f368616e6f | |||
dd632f4203 | |||
a8f3ae501b | |||
ea76963ac2 | |||
99dfa97958 | |||
f68270a5b3 | |||
542ed81034 | |||
404a11f5e7 | |||
411221070e | |||
25d35d0c76 | |||
c72904d61c | |||
b94c9acd26 | |||
bdb4988569 | |||
1dafd1352a | |||
31fbd3cad7 | |||
848bc5ec10 | |||
4d0ad826a0 | |||
6955820fcc | |||
ef530624b9 | |||
9b9e7dd88f | |||
a13cc9e961 | |||
0d2b923378 | |||
fba84c8ac8 | |||
db10fe1b2c | |||
175dd980f8 | |||
8364020671 | |||
eba44cd394 | |||
b3bac8015a | |||
0b48afd251 |
57
CHANGELOG.md
57
CHANGELOG.md
@@ -1,5 +1,62 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 0.10.7
|
||||||
|
|
||||||
|
* `Pagination`:
|
||||||
|
* Now it is possible to use `doForAll*` and `getForAll` functions in non suspend places
|
||||||
|
|
||||||
|
## 0.10.6
|
||||||
|
|
||||||
|
* `Versions`
|
||||||
|
* `Ktor`: `2.0.1` -> `2.0.2`
|
||||||
|
* `Common`
|
||||||
|
* `JS`:
|
||||||
|
* Add `ResizeObserver` functionality
|
||||||
|
|
||||||
|
## 0.10.5
|
||||||
|
|
||||||
|
* `Versions`
|
||||||
|
* `Compose`: `1.2.0-alpha01-dev683` -> `1.2.0-alpha01-dev686`
|
||||||
|
* `Repos`
|
||||||
|
* `Android`:
|
||||||
|
* New function `SharedPreferencesKeyValueRepo`
|
||||||
|
* `FSM`
|
||||||
|
* Add `StateHandlingErrorHandler` and opportunity to handle states handling errors
|
||||||
|
|
||||||
|
## 0.10.4
|
||||||
|
|
||||||
|
* `Versions`:
|
||||||
|
* `Serialization`: `1.3.2` -> `1.3.3`
|
||||||
|
|
||||||
|
## 0.10.3
|
||||||
|
|
||||||
|
* `Versions`:
|
||||||
|
* `Compose`: `1.2.0-alpha01-dev682` -> `1.2.0-alpha01-dev683`
|
||||||
|
* `Coroutines`:
|
||||||
|
* Fixes in `AccumulatorFlow`
|
||||||
|
|
||||||
|
## 0.10.2
|
||||||
|
|
||||||
|
* `Versions`:
|
||||||
|
* `Compose`: `1.2.0-alpha01-dev675` -> `1.2.0-alpha01-dev682`
|
||||||
|
|
||||||
|
## 0.10.1
|
||||||
|
|
||||||
|
* `Versions`:
|
||||||
|
* `Ktor`: `2.0.0` -> `2.0.1`
|
||||||
|
* `Crypto`:
|
||||||
|
* Add `hmacSha256`
|
||||||
|
* Add `hex`
|
||||||
|
|
||||||
|
## 0.10.0
|
||||||
|
|
||||||
|
* `Versions`:
|
||||||
|
* `Kotlin`: `1.6.10` -> `1.6.21`
|
||||||
|
* `Compose`: `1.1.1` -> `1.2.0-alpha01-dev675`
|
||||||
|
* `Exposed`: `0.37.3` -> `0.38.2`
|
||||||
|
* `Ktor`: `1.6.8` -> `2.0.0`
|
||||||
|
* `Dokka`: `1.6.10` -> `1.6.21`
|
||||||
|
|
||||||
## 0.9.24
|
## 0.9.24
|
||||||
|
|
||||||
* `Ktor`:
|
* `Ktor`:
|
||||||
|
@@ -21,6 +21,7 @@ allprojects {
|
|||||||
mavenLocal()
|
mavenLocal()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
google()
|
google()
|
||||||
|
maven { url "https://maven.pkg.jetbrains.space/public/p/compose/dev" }
|
||||||
}
|
}
|
||||||
|
|
||||||
// temporal crutch until legacy tests will be stabled or legacy target will be removed
|
// temporal crutch until legacy tests will be stabled or legacy target will be removed
|
||||||
|
@@ -1,3 +1,5 @@
|
|||||||
|
@file:Suppress("OPT_IN_IS_NOT_ENABLED")
|
||||||
|
|
||||||
package dev.inmo.micro_utils.common
|
package dev.inmo.micro_utils.common
|
||||||
|
|
||||||
@RequiresOptIn(
|
@RequiresOptIn(
|
||||||
|
@@ -43,6 +43,7 @@ private inline fun <T> performChanges(
|
|||||||
if (oldOneEqualToNewObject || newOneEqualToOldObject) {
|
if (oldOneEqualToNewObject || newOneEqualToOldObject) {
|
||||||
changedList.addAll(
|
changedList.addAll(
|
||||||
potentialChanges.take(i).mapNotNull {
|
potentialChanges.take(i).mapNotNull {
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
if (it.first != null && it.second != null) it as Pair<IndexedValue<T>, IndexedValue<T>> else null
|
if (it.first != null && it.second != null) it as Pair<IndexedValue<T>, IndexedValue<T>> else null
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -121,7 +122,10 @@ fun <T> Iterable<T>.calculateDiff(
|
|||||||
|
|
||||||
when {
|
when {
|
||||||
oldObject === newObject || (oldObject == newObject && !strictComparison) -> {
|
oldObject === newObject || (oldObject == newObject && !strictComparison) -> {
|
||||||
changedObjects.addAll(potentiallyChangedObjects.map { it as Pair<IndexedValue<T>, IndexedValue<T>> })
|
changedObjects.addAll(potentiallyChangedObjects.map {
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
it as Pair<IndexedValue<T>, IndexedValue<T>>
|
||||||
|
})
|
||||||
potentiallyChangedObjects.clear()
|
potentiallyChangedObjects.clear()
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
|
@@ -27,20 +27,13 @@ sealed interface Either<T1, T2> {
|
|||||||
@Deprecated("Use optionalT2 instead", ReplaceWith("optionalT2"))
|
@Deprecated("Use optionalT2 instead", ReplaceWith("optionalT2"))
|
||||||
val t2: T2?
|
val t2: T2?
|
||||||
get() = optionalT2.dataOrNull()
|
get() = optionalT2.dataOrNull()
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun <T1, T2> serializer(
|
|
||||||
t1Serializer: KSerializer<T1>,
|
|
||||||
t2Serializer: KSerializer<T2>,
|
|
||||||
): KSerializer<Either<T1, T2>> = EitherSerializer(t1Serializer, t2Serializer)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class EitherSerializer<T1, T2>(
|
class EitherSerializer<T1, T2>(
|
||||||
t1Serializer: KSerializer<T1>,
|
t1Serializer: KSerializer<T1>,
|
||||||
t2Serializer: KSerializer<T2>,
|
t2Serializer: KSerializer<T2>,
|
||||||
) : KSerializer<Either<T1, T2>> {
|
) : KSerializer<Either<T1, T2>> {
|
||||||
@OptIn(ExperimentalSerializationApi::class, InternalSerializationApi::class)
|
@OptIn(InternalSerializationApi::class)
|
||||||
override val descriptor: SerialDescriptor = buildSerialDescriptor(
|
override val descriptor: SerialDescriptor = buildSerialDescriptor(
|
||||||
"TypedSerializer",
|
"TypedSerializer",
|
||||||
SerialKind.CONTEXTUAL
|
SerialKind.CONTEXTUAL
|
||||||
|
@@ -32,7 +32,7 @@ class DiffUtilsTests {
|
|||||||
val withIndex = oldList.withIndex()
|
val withIndex = oldList.withIndex()
|
||||||
|
|
||||||
for (count in 1 .. (floor(oldList.size.toFloat() / 2).toInt())) {
|
for (count in 1 .. (floor(oldList.size.toFloat() / 2).toInt())) {
|
||||||
for ((i, v) in withIndex) {
|
for ((i, _) in withIndex) {
|
||||||
if (i + count > oldList.lastIndex) {
|
if (i + count > oldList.lastIndex) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -55,7 +55,7 @@ class DiffUtilsTests {
|
|||||||
val withIndex = oldList.withIndex()
|
val withIndex = oldList.withIndex()
|
||||||
|
|
||||||
for (step in oldList.indices) {
|
for (step in oldList.indices) {
|
||||||
for ((i, v) in withIndex) {
|
for ((i, _) in withIndex) {
|
||||||
val mutable = oldList.toMutableList()
|
val mutable = oldList.toMutableList()
|
||||||
val changes = (
|
val changes = (
|
||||||
if (step == 0) i until oldList.size else (i until oldList.size step step)
|
if (step == 0) i until oldList.size else (i until oldList.size step step)
|
||||||
@@ -104,7 +104,7 @@ class DiffUtilsTests {
|
|||||||
val withIndex = oldList.withIndex()
|
val withIndex = oldList.withIndex()
|
||||||
|
|
||||||
for (count in 1 .. (floor(oldList.size.toFloat() / 2).toInt())) {
|
for (count in 1 .. (floor(oldList.size.toFloat() / 2).toInt())) {
|
||||||
for ((i, v) in withIndex) {
|
for ((i, _) in withIndex) {
|
||||||
if (i + count > oldList.lastIndex) {
|
if (i + count > oldList.lastIndex) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -129,15 +129,20 @@ class DiffUtilsTests {
|
|||||||
val withIndex = oldList.withIndex()
|
val withIndex = oldList.withIndex()
|
||||||
|
|
||||||
for (step in oldList.indices) {
|
for (step in oldList.indices) {
|
||||||
for ((i, v) in withIndex) {
|
for ((i, _) in withIndex) {
|
||||||
val mutable = oldList.toMutableList()
|
val mutable = oldList.toMutableList()
|
||||||
val changes = (
|
|
||||||
if (step == 0) i until oldList.size else (i until oldList.size step step)
|
val newList = if (step == 0) {
|
||||||
).map { index ->
|
i until oldList.size
|
||||||
|
} else {
|
||||||
|
i until oldList.size step step
|
||||||
|
}
|
||||||
|
newList.forEach { index ->
|
||||||
IndexedValue(index, mutable[index]) to IndexedValue(index, "changed$index").also {
|
IndexedValue(index, mutable[index]) to IndexedValue(index, "changed$index").also {
|
||||||
mutable[index] = it.value
|
mutable[index] = it.value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val mutableOldList = oldList.toMutableList()
|
val mutableOldList = oldList.toMutableList()
|
||||||
mutableOldList.applyDiff(mutable)
|
mutableOldList.applyDiff(mutable)
|
||||||
assertEquals(
|
assertEquals(
|
||||||
|
@@ -0,0 +1,58 @@
|
|||||||
|
package dev.inmo.micro_utils.common
|
||||||
|
|
||||||
|
import org.w3c.dom.*
|
||||||
|
import kotlin.js.Json
|
||||||
|
import kotlin.js.json
|
||||||
|
|
||||||
|
external class ResizeObserver(
|
||||||
|
callback: (Array<ResizeObserverEntry>, ResizeObserver) -> Unit
|
||||||
|
) {
|
||||||
|
fun observe(target: Element, options: Json = definedExternally)
|
||||||
|
|
||||||
|
fun unobserve(target: Element)
|
||||||
|
|
||||||
|
fun disconnect()
|
||||||
|
}
|
||||||
|
|
||||||
|
external interface ResizeObserverSize {
|
||||||
|
val blockSize: Float
|
||||||
|
val inlineSize: Float
|
||||||
|
}
|
||||||
|
|
||||||
|
external interface ResizeObserverEntry {
|
||||||
|
val borderBoxSize: Array<ResizeObserverSize>
|
||||||
|
val contentBoxSize: Array<ResizeObserverSize>
|
||||||
|
val devicePixelContentBoxSize: Array<ResizeObserverSize>
|
||||||
|
val contentRect: DOMRectReadOnly
|
||||||
|
val target: Element
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ResizeObserver.observe(target: Element, options: ResizeObserverObserveOptions) = observe(
|
||||||
|
target,
|
||||||
|
json(
|
||||||
|
"box" to options.box ?.name
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
class ResizeObserverObserveOptions(
|
||||||
|
val box: Box? = null
|
||||||
|
) {
|
||||||
|
sealed interface Box {
|
||||||
|
val name: String
|
||||||
|
|
||||||
|
object Content : Box {
|
||||||
|
override val name: String
|
||||||
|
get() = "content-box"
|
||||||
|
}
|
||||||
|
|
||||||
|
object Border : Box {
|
||||||
|
override val name: String
|
||||||
|
get() = "border-box"
|
||||||
|
}
|
||||||
|
|
||||||
|
object DevicePixelContent : Box {
|
||||||
|
override val name: String
|
||||||
|
get() = "device-pixel-content-box"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -16,6 +16,7 @@ fun <T> Flow<T>.toMutableState(
|
|||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
inline fun <T> StateFlow<T>.toMutableState(
|
inline fun <T> StateFlow<T>.toMutableState(
|
||||||
scope: CoroutineScope
|
scope: CoroutineScope
|
||||||
): MutableState<T> = toMutableState(value, scope)
|
): MutableState<T> = toMutableState(value, scope)
|
||||||
|
@@ -6,11 +6,12 @@ import kotlinx.coroutines.channels.Channel
|
|||||||
import kotlinx.coroutines.flow.*
|
import kotlinx.coroutines.flow.*
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import kotlinx.coroutines.sync.withLock
|
import kotlinx.coroutines.sync.withLock
|
||||||
|
import kotlin.coroutines.cancellation.CancellationException
|
||||||
|
|
||||||
private sealed interface AccumulatorFlowStep
|
private sealed interface AccumulatorFlowStep<T>
|
||||||
private data class DataRetrievedAccumulatorFlowStep(val data: Any) : AccumulatorFlowStep
|
private data class DataRetrievedAccumulatorFlowStep<T>(val data: T) : AccumulatorFlowStep<T>
|
||||||
private data class SubscribeAccumulatorFlowStep(val channel: Channel<Any>) : AccumulatorFlowStep
|
private data class SubscribeAccumulatorFlowStep<T>(val channel: Channel<T>) : AccumulatorFlowStep<T>
|
||||||
private data class UnsubscribeAccumulatorFlowStep(val channel: Channel<Any>) : AccumulatorFlowStep
|
private data class UnsubscribeAccumulatorFlowStep<T>(val channel: Channel<T>) : AccumulatorFlowStep<T>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This [Flow] will have behaviour very similar to [SharedFlow], but there are several differences:
|
* This [Flow] will have behaviour very similar to [SharedFlow], but there are several differences:
|
||||||
@@ -26,12 +27,12 @@ class AccumulatorFlow<T>(
|
|||||||
private val subscope = scope.LinkedSupervisorScope()
|
private val subscope = scope.LinkedSupervisorScope()
|
||||||
private val activeData = ArrayDeque<T>()
|
private val activeData = ArrayDeque<T>()
|
||||||
private val dataMutex = Mutex()
|
private val dataMutex = Mutex()
|
||||||
private val channelsForBroadcast = mutableListOf<Channel<Any>>()
|
private val channelsForBroadcast = mutableListOf<Channel<T>>()
|
||||||
private val channelsMutex = Mutex()
|
private val channelsMutex = Mutex()
|
||||||
private val steps = subscope.actor<AccumulatorFlowStep> { step ->
|
private val steps = subscope.actor<AccumulatorFlowStep<T>> { step ->
|
||||||
when (step) {
|
when (step) {
|
||||||
is DataRetrievedAccumulatorFlowStep -> {
|
is DataRetrievedAccumulatorFlowStep -> {
|
||||||
if (activeData.first() === step.data) {
|
if (activeData.firstOrNull() === step.data) {
|
||||||
dataMutex.withLock {
|
dataMutex.withLock {
|
||||||
activeData.removeFirst()
|
activeData.removeFirst()
|
||||||
}
|
}
|
||||||
@@ -42,7 +43,7 @@ class AccumulatorFlow<T>(
|
|||||||
dataMutex.withLock {
|
dataMutex.withLock {
|
||||||
val dataToSend = activeData.toList()
|
val dataToSend = activeData.toList()
|
||||||
safelyWithoutExceptions {
|
safelyWithoutExceptions {
|
||||||
dataToSend.forEach { step.channel.send(it as Any) }
|
dataToSend.forEach { step.channel.send(it) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -58,24 +59,29 @@ class AccumulatorFlow<T>(
|
|||||||
channelsMutex.withLock {
|
channelsMutex.withLock {
|
||||||
channelsForBroadcast.forEach { channel ->
|
channelsForBroadcast.forEach { channel ->
|
||||||
safelyWithResult {
|
safelyWithResult {
|
||||||
channel.send(it as Any)
|
channel.send(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun collectSafely(collector: FlowCollector<T>) {
|
override suspend fun collectSafely(collector: FlowCollector<T>) {
|
||||||
val channel = Channel<Any>(Channel.UNLIMITED, BufferOverflow.SUSPEND)
|
val channel = Channel<T>(Channel.UNLIMITED, BufferOverflow.SUSPEND)
|
||||||
steps.send(SubscribeAccumulatorFlowStep(channel))
|
steps.send(SubscribeAccumulatorFlowStep(channel))
|
||||||
|
val result = runCatchingSafely {
|
||||||
for (data in channel) {
|
for (data in channel) {
|
||||||
try {
|
val emitResult = runCatchingSafely {
|
||||||
collector.emit(data as T)
|
collector.emit(data)
|
||||||
|
}
|
||||||
|
if (emitResult.isSuccess || emitResult.exceptionOrNull() is CancellationException) {
|
||||||
steps.send(DataRetrievedAccumulatorFlowStep(data))
|
steps.send(DataRetrievedAccumulatorFlowStep(data))
|
||||||
} finally {
|
}
|
||||||
|
emitResult.getOrThrow()
|
||||||
|
}
|
||||||
|
}
|
||||||
channel.cancel()
|
channel.cancel()
|
||||||
steps.send(UnsubscribeAccumulatorFlowStep(channel))
|
steps.send(UnsubscribeAccumulatorFlowStep(channel))
|
||||||
}
|
result.getOrThrow()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -0,0 +1,15 @@
|
|||||||
|
package dev.inmo.micro_utils.crypto
|
||||||
|
|
||||||
|
val HEX_ARRAY = "0123456789abcdef".toCharArray()
|
||||||
|
|
||||||
|
fun SourceBytes.hex(): String {
|
||||||
|
val hexChars = CharArray(size * 2)
|
||||||
|
for (j in indices) {
|
||||||
|
val v: Int = this[j].toInt() and 0xFF
|
||||||
|
hexChars[j * 2] = HEX_ARRAY[v ushr 4]
|
||||||
|
hexChars[j * 2 + 1] = HEX_ARRAY[v and 0x0F]
|
||||||
|
}
|
||||||
|
return hexChars.concatToString()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun SourceString.hex(): String = encodeToByteArray().hex()
|
@@ -0,0 +1,3 @@
|
|||||||
|
package dev.inmo.micro_utils.crypto
|
||||||
|
|
||||||
|
expect fun SourceString.hmacSha256(key: String): String
|
@@ -0,0 +1,15 @@
|
|||||||
|
package dev.inmo.micro_utils.crypto
|
||||||
|
|
||||||
|
import kotlin.test.*
|
||||||
|
|
||||||
|
class Hex {
|
||||||
|
@Test
|
||||||
|
fun testSimpleHmacSHA256Message() {
|
||||||
|
val text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
|
||||||
|
val resultHex = text.hex()
|
||||||
|
assertEquals(
|
||||||
|
"4c6f72656d20697073756d20646f6c6f722073697420616d65742c20636f6e73656374657475722061646970697363696e6720656c69742c2073656420646f20656975736d6f642074656d706f7220696e6369646964756e74207574206c61626f726520657420646f6c6f7265206d61676e6120616c697175612e20557420656e696d206164206d696e696d2076656e69616d2c2071756973206e6f737472756420657865726369746174696f6e20756c6c616d636f206c61626f726973206e69736920757420616c697175697020657820656120636f6d6d6f646f20636f6e7365717561742e2044756973206175746520697275726520646f6c6f7220696e20726570726568656e646572697420696e20766f6c7570746174652076656c697420657373652063696c6c756d20646f6c6f726520657520667567696174206e756c6c612070617269617475722e204578636570746575722073696e74206f6363616563617420637570696461746174206e6f6e2070726f6964656e742c2073756e7420696e2063756c706120717569206f666669636961206465736572756e74206d6f6c6c697420616e696d20696420657374206c61626f72756d2e",
|
||||||
|
resultHex
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,12 @@
|
|||||||
|
package dev.inmo.micro_utils.crypto
|
||||||
|
|
||||||
|
import kotlin.test.*
|
||||||
|
|
||||||
|
class HmacSHA256 {
|
||||||
|
@Test
|
||||||
|
fun testSimpleHmacSHA256Message() {
|
||||||
|
val text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
|
||||||
|
val resultSha = text.hmacSha256("Example")
|
||||||
|
assertEquals("5a481d59329ef862b158eedc95392ebb22492ba3014661a3379d8201db992484", resultSha)
|
||||||
|
}
|
||||||
|
}
|
@@ -7,3 +7,7 @@ external interface CryptoJs {
|
|||||||
@JsModule("crypto-js")
|
@JsModule("crypto-js")
|
||||||
@JsNonModule
|
@JsNonModule
|
||||||
external val CryptoJS: CryptoJs
|
external val CryptoJS: CryptoJs
|
||||||
|
|
||||||
|
actual fun SourceString.hmacSha256(key: String): String {
|
||||||
|
return CryptoJS.asDynamic().HmacSHA256(this, key).toString().unsafeCast<String>()
|
||||||
|
}
|
||||||
|
@@ -0,0 +1,13 @@
|
|||||||
|
package dev.inmo.micro_utils.crypto
|
||||||
|
|
||||||
|
import javax.crypto.Mac
|
||||||
|
import javax.crypto.spec.SecretKeySpec
|
||||||
|
|
||||||
|
actual fun SourceString.hmacSha256(key: String): String {
|
||||||
|
val mac = Mac.getInstance("HmacSHA256")
|
||||||
|
|
||||||
|
val secretKey = SecretKeySpec(key.toByteArray(), "HmacSHA256")
|
||||||
|
mac.init(secretKey)
|
||||||
|
|
||||||
|
return mac.doFinal(toByteArray()).hex()
|
||||||
|
}
|
@@ -3,6 +3,8 @@ package dev.inmo.micro_utils.fsm.common
|
|||||||
import dev.inmo.micro_utils.common.Optional
|
import dev.inmo.micro_utils.common.Optional
|
||||||
import dev.inmo.micro_utils.common.onPresented
|
import dev.inmo.micro_utils.common.onPresented
|
||||||
import dev.inmo.micro_utils.coroutines.*
|
import dev.inmo.micro_utils.coroutines.*
|
||||||
|
import dev.inmo.micro_utils.fsm.common.utils.StateHandlingErrorHandler
|
||||||
|
import dev.inmo.micro_utils.fsm.common.utils.defaultStateHandlingErrorHandler
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import kotlinx.coroutines.sync.withLock
|
import kotlinx.coroutines.sync.withLock
|
||||||
@@ -15,11 +17,22 @@ import kotlinx.coroutines.sync.withLock
|
|||||||
interface StatesMachine<T : State> : StatesHandler<T, T> {
|
interface StatesMachine<T : State> : StatesHandler<T, T> {
|
||||||
suspend fun launchStateHandling(
|
suspend fun launchStateHandling(
|
||||||
state: T,
|
state: T,
|
||||||
handlers: List<CheckableHandlerHolder<in T, T>>
|
handlers: List<CheckableHandlerHolder<in T, T>>,
|
||||||
|
onStateHandlingErrorHandler: StateHandlingErrorHandler<T>
|
||||||
): T? {
|
): T? {
|
||||||
return handlers.firstOrNull { it.checkHandleable(state) } ?.run {
|
return runCatchingSafely {
|
||||||
|
handlers.firstOrNull { it.checkHandleable(state) } ?.run {
|
||||||
handleState(state)
|
handleState(state)
|
||||||
}
|
}
|
||||||
|
}.getOrElse {
|
||||||
|
onStateHandlingErrorHandler(state, it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
suspend fun launchStateHandling(
|
||||||
|
state: T,
|
||||||
|
handlers: List<CheckableHandlerHolder<in T, T>>
|
||||||
|
): T? {
|
||||||
|
return launchStateHandling(state, handlers, defaultStateHandlingErrorHandler())
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -38,8 +51,9 @@ interface StatesMachine<T : State> : StatesHandler<T, T> {
|
|||||||
*/
|
*/
|
||||||
operator fun <T: State> invoke(
|
operator fun <T: State> invoke(
|
||||||
statesManager: StatesManager<T>,
|
statesManager: StatesManager<T>,
|
||||||
handlers: List<CheckableHandlerHolder<in T, T>>
|
handlers: List<CheckableHandlerHolder<in T, T>>,
|
||||||
) = DefaultStatesMachine(statesManager, handlers)
|
onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler()
|
||||||
|
) = DefaultStatesMachine(statesManager, handlers, onStateHandlingErrorHandler)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,12 +66,17 @@ interface StatesMachine<T : State> : StatesHandler<T, T> {
|
|||||||
open class DefaultStatesMachine <T: State>(
|
open class DefaultStatesMachine <T: State>(
|
||||||
protected val statesManager: StatesManager<T>,
|
protected val statesManager: StatesManager<T>,
|
||||||
protected val handlers: List<CheckableHandlerHolder<in T, T>>,
|
protected val handlers: List<CheckableHandlerHolder<in T, T>>,
|
||||||
|
protected val onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler()
|
||||||
) : StatesMachine<T> {
|
) : StatesMachine<T> {
|
||||||
/**
|
/**
|
||||||
* Will call [launchStateHandling] for state handling
|
* Will call [launchStateHandling] for state handling
|
||||||
*/
|
*/
|
||||||
override suspend fun StatesMachine<in T>.handleState(state: T): T? = launchStateHandling(state, handlers)
|
override suspend fun StatesMachine<in T>.handleState(state: T): T? = launchStateHandling(state, handlers)
|
||||||
|
|
||||||
|
override suspend fun launchStateHandling(state: T, handlers: List<CheckableHandlerHolder<in T, T>>): T? {
|
||||||
|
return launchStateHandling(state, handlers, onStateHandlingErrorHandler)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This
|
* This
|
||||||
*/
|
*/
|
||||||
|
@@ -1,6 +1,8 @@
|
|||||||
package dev.inmo.micro_utils.fsm.common
|
package dev.inmo.micro_utils.fsm.common
|
||||||
|
|
||||||
import dev.inmo.micro_utils.common.*
|
import dev.inmo.micro_utils.common.*
|
||||||
|
import dev.inmo.micro_utils.fsm.common.utils.StateHandlingErrorHandler
|
||||||
|
import dev.inmo.micro_utils.fsm.common.utils.defaultStateHandlingErrorHandler
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.sync.withLock
|
import kotlinx.coroutines.sync.withLock
|
||||||
|
|
||||||
@@ -20,9 +22,11 @@ interface UpdatableStatesMachine<T : State> : StatesMachine<T> {
|
|||||||
open class DefaultUpdatableStatesMachine<T : State>(
|
open class DefaultUpdatableStatesMachine<T : State>(
|
||||||
statesManager: StatesManager<T>,
|
statesManager: StatesManager<T>,
|
||||||
handlers: List<CheckableHandlerHolder<in T, T>>,
|
handlers: List<CheckableHandlerHolder<in T, T>>,
|
||||||
|
onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler()
|
||||||
) : DefaultStatesMachine<T>(
|
) : DefaultStatesMachine<T>(
|
||||||
statesManager,
|
statesManager,
|
||||||
handlers
|
handlers,
|
||||||
|
onStateHandlingErrorHandler
|
||||||
), UpdatableStatesMachine<T> {
|
), UpdatableStatesMachine<T> {
|
||||||
protected val jobsStates = mutableMapOf<Job, T>()
|
protected val jobsStates = mutableMapOf<Job, T>()
|
||||||
|
|
||||||
@@ -34,7 +38,7 @@ open class DefaultUpdatableStatesMachine<T : State>(
|
|||||||
*/
|
*/
|
||||||
override suspend fun performStateUpdate(previousState: Optional<T>, actualState: T, scope: CoroutineScope) {
|
override suspend fun performStateUpdate(previousState: Optional<T>, actualState: T, scope: CoroutineScope) {
|
||||||
statesJobsMutex.withLock {
|
statesJobsMutex.withLock {
|
||||||
if (compare(previousState, actualState)) {
|
if (shouldReplaceJob(previousState, actualState)) {
|
||||||
statesJobs[actualState] ?.cancel()
|
statesJobs[actualState] ?.cancel()
|
||||||
}
|
}
|
||||||
val job = previousState.mapOnPresented {
|
val job = previousState.mapOnPresented {
|
||||||
|
@@ -0,0 +1,6 @@
|
|||||||
|
package dev.inmo.micro_utils.fsm.common.utils
|
||||||
|
|
||||||
|
typealias StateHandlingErrorHandler<T> = suspend (T, Throwable) -> T?
|
||||||
|
val DefaultStateHandlingErrorHandler: StateHandlingErrorHandler<*> = { _, _ -> null }
|
||||||
|
inline fun <T> defaultStateHandlingErrorHandler(): StateHandlingErrorHandler<T> = DefaultStateHandlingErrorHandler as StateHandlingErrorHandler<T>
|
||||||
|
|
@@ -14,5 +14,5 @@ crypto_js_version=4.1.1
|
|||||||
# Project data
|
# Project data
|
||||||
|
|
||||||
group=dev.inmo
|
group=dev.inmo
|
||||||
version=0.9.24
|
version=0.10.7
|
||||||
android_code_version=114
|
android_code_version=122
|
||||||
|
@@ -1,22 +1,22 @@
|
|||||||
[versions]
|
[versions]
|
||||||
|
|
||||||
kt = "1.6.10"
|
kt = "1.6.21"
|
||||||
kt-serialization = "1.3.2"
|
kt-serialization = "1.3.3"
|
||||||
kt-coroutines = "1.6.1"
|
kt-coroutines = "1.6.1"
|
||||||
|
|
||||||
jb-compose = "1.1.1"
|
jb-compose = "1.2.0-alpha01-dev686"
|
||||||
jb-exposed = "0.37.3"
|
jb-exposed = "0.38.2"
|
||||||
jb-dokka = "1.6.10"
|
jb-dokka = "1.6.21"
|
||||||
|
|
||||||
klock = "2.7.0"
|
klock = "2.7.0"
|
||||||
uuid = "0.4.0"
|
uuid = "0.4.0"
|
||||||
|
|
||||||
ktor = "1.6.8"
|
ktor = "2.0.2"
|
||||||
|
|
||||||
gh-release = "2.2.12"
|
gh-release = "2.3.7"
|
||||||
|
|
||||||
android-gradle = "7.0.4"
|
android-gradle = "7.0.4"
|
||||||
dexcount = "3.0.1"
|
dexcount = "3.1.0"
|
||||||
|
|
||||||
android-coreKtx = "1.7.0"
|
android-coreKtx = "1.7.0"
|
||||||
android-recyclerView = "1.2.1"
|
android-recyclerView = "1.2.1"
|
||||||
@@ -46,6 +46,8 @@ ktor-server = { module = "io.ktor:ktor-server", version.ref = "ktor" }
|
|||||||
ktor-server-cio = { module = "io.ktor:ktor-server-cio", version.ref = "ktor" }
|
ktor-server-cio = { module = "io.ktor:ktor-server-cio", version.ref = "ktor" }
|
||||||
ktor-server-host-common = { module = "io.ktor:ktor-server-host-common", version.ref = "ktor" }
|
ktor-server-host-common = { module = "io.ktor:ktor-server-host-common", version.ref = "ktor" }
|
||||||
ktor-websockets = { module = "io.ktor:ktor-websockets", version.ref = "ktor" }
|
ktor-websockets = { module = "io.ktor:ktor-websockets", version.ref = "ktor" }
|
||||||
|
ktor-server-websockets = { module = "io.ktor:ktor-server-websockets", version.ref = "ktor" }
|
||||||
|
ktor-server-statusPages = { module = "io.ktor:ktor-server-status-pages", version.ref = "ktor" }
|
||||||
|
|
||||||
|
|
||||||
klock = { module = "com.soywiz.korlibs.klock:klock", version.ref = "klock" }
|
klock = { module = "com.soywiz.korlibs.klock:klock", version.ref = "klock" }
|
||||||
|
@@ -1,14 +1,18 @@
|
|||||||
package dev.inmo.micro_utils.ktor.client
|
package dev.inmo.micro_utils.ktor.client
|
||||||
|
|
||||||
|
import dev.inmo.micro_utils.coroutines.runCatchingSafely
|
||||||
import dev.inmo.micro_utils.coroutines.safely
|
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.plugins.pluginOrNull
|
||||||
|
import io.ktor.client.plugins.websocket.WebSockets
|
||||||
|
import io.ktor.client.plugins.websocket.ws
|
||||||
import io.ktor.client.request.HttpRequestBuilder
|
import io.ktor.client.request.HttpRequestBuilder
|
||||||
import io.ktor.http.cio.websocket.Frame
|
import io.ktor.websocket.Frame
|
||||||
import io.ktor.http.cio.websocket.readBytes
|
import io.ktor.websocket.readBytes
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.channelFlow
|
import kotlinx.coroutines.flow.channelFlow
|
||||||
|
import kotlinx.coroutines.isActive
|
||||||
import kotlinx.serialization.DeserializationStrategy
|
import kotlinx.serialization.DeserializationStrategy
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -17,43 +21,41 @@ import kotlinx.serialization.DeserializationStrategy
|
|||||||
*/
|
*/
|
||||||
inline fun <T> HttpClient.createStandardWebsocketFlow(
|
inline fun <T> HttpClient.createStandardWebsocketFlow(
|
||||||
url: String,
|
url: String,
|
||||||
crossinline checkReconnection: (Throwable?) -> Boolean = { true },
|
crossinline checkReconnection: suspend (Throwable?) -> Boolean = { true },
|
||||||
noinline requestBuilder: HttpRequestBuilder.() -> Unit = {},
|
noinline requestBuilder: HttpRequestBuilder.() -> Unit = {},
|
||||||
crossinline conversation: suspend (StandardKtorSerialInputData) -> T
|
crossinline conversation: suspend (StandardKtorSerialInputData) -> T
|
||||||
): Flow<T> {
|
): Flow<T> {
|
||||||
|
pluginOrNull(WebSockets) ?: error("Plugin $WebSockets must be installed for using createStandardWebsocketFlow")
|
||||||
|
|
||||||
val correctedUrl = url.asCorrectWebSocketUrl
|
val correctedUrl = url.asCorrectWebSocketUrl
|
||||||
|
|
||||||
return channelFlow {
|
return channelFlow {
|
||||||
val producerScope = this@channelFlow
|
|
||||||
do {
|
do {
|
||||||
val reconnect = try {
|
val reconnect = runCatchingSafely {
|
||||||
safely {
|
|
||||||
ws(correctedUrl, requestBuilder) {
|
ws(correctedUrl, requestBuilder) {
|
||||||
for (received in incoming) {
|
for (received in incoming) {
|
||||||
when (received) {
|
when (received) {
|
||||||
is Frame.Binary -> producerScope.send(conversation(received.readBytes()))
|
is Frame.Binary -> send(conversation(received.data))
|
||||||
else -> {
|
else -> {
|
||||||
producerScope.close()
|
close()
|
||||||
return@ws
|
return@ws
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
checkReconnection(null)
|
checkReconnection(null)
|
||||||
} catch (e: Throwable) {
|
}.getOrElse { e ->
|
||||||
checkReconnection(e).also {
|
checkReconnection(e).also {
|
||||||
if (!it) {
|
if (!it) {
|
||||||
producerScope.close(e)
|
close(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} while (reconnect)
|
} while (reconnect && isActive)
|
||||||
if (!producerScope.isClosedForSend) {
|
|
||||||
safely(
|
if (isActive) {
|
||||||
{ it.printStackTrace() }
|
safely {
|
||||||
) {
|
close()
|
||||||
producerScope.close()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -65,8 +67,8 @@ inline fun <T> HttpClient.createStandardWebsocketFlow(
|
|||||||
*/
|
*/
|
||||||
inline fun <T> HttpClient.createStandardWebsocketFlow(
|
inline fun <T> HttpClient.createStandardWebsocketFlow(
|
||||||
url: String,
|
url: String,
|
||||||
crossinline checkReconnection: (Throwable?) -> Boolean = { true },
|
|
||||||
deserializer: DeserializationStrategy<T>,
|
deserializer: DeserializationStrategy<T>,
|
||||||
|
crossinline checkReconnection: suspend (Throwable?) -> Boolean = { true },
|
||||||
serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat,
|
serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat,
|
||||||
noinline requestBuilder: HttpRequestBuilder.() -> Unit = {},
|
noinline requestBuilder: HttpRequestBuilder.() -> Unit = {},
|
||||||
) = createStandardWebsocketFlow(
|
) = createStandardWebsocketFlow(
|
||||||
|
@@ -4,8 +4,10 @@ import dev.inmo.micro_utils.common.MPPFile
|
|||||||
import dev.inmo.micro_utils.common.filename
|
import dev.inmo.micro_utils.common.filename
|
||||||
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.call.body
|
||||||
import io.ktor.client.request.*
|
import io.ktor.client.request.*
|
||||||
import io.ktor.client.request.forms.*
|
import io.ktor.client.request.forms.*
|
||||||
|
import io.ktor.client.statement.readBytes
|
||||||
import io.ktor.http.*
|
import io.ktor.http.*
|
||||||
import io.ktor.utils.io.core.ByteReadPacket
|
import io.ktor.utils.io.core.ByteReadPacket
|
||||||
import kotlinx.serialization.*
|
import kotlinx.serialization.*
|
||||||
@@ -85,10 +87,10 @@ class UnifiedRequester(
|
|||||||
|
|
||||||
fun <T> createStandardWebsocketFlow(
|
fun <T> createStandardWebsocketFlow(
|
||||||
url: String,
|
url: String,
|
||||||
checkReconnection: (Throwable?) -> Boolean,
|
checkReconnection: suspend (Throwable?) -> Boolean,
|
||||||
deserializer: DeserializationStrategy<T>,
|
deserializer: DeserializationStrategy<T>,
|
||||||
requestBuilder: HttpRequestBuilder.() -> Unit = {},
|
requestBuilder: HttpRequestBuilder.() -> Unit = {},
|
||||||
) = client.createStandardWebsocketFlow(url, checkReconnection, deserializer, serialFormat, requestBuilder)
|
) = client.createStandardWebsocketFlow(url, deserializer, checkReconnection, serialFormat, requestBuilder)
|
||||||
|
|
||||||
fun <T> createStandardWebsocketFlow(
|
fun <T> createStandardWebsocketFlow(
|
||||||
url: String,
|
url: String,
|
||||||
@@ -103,10 +105,8 @@ suspend fun <ResultType> HttpClient.uniget(
|
|||||||
url: String,
|
url: String,
|
||||||
resultDeserializer: DeserializationStrategy<ResultType>,
|
resultDeserializer: DeserializationStrategy<ResultType>,
|
||||||
serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat
|
serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat
|
||||||
) = get<StandardKtorSerialInputData>(
|
) = get(url).let {
|
||||||
url
|
serialFormat.decodeDefault(resultDeserializer, it.body<StandardKtorSerialInputData>())
|
||||||
).let {
|
|
||||||
serialFormat.decodeDefault(resultDeserializer, it)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -123,10 +123,12 @@ suspend fun <BodyType, ResultType> HttpClient.unipost(
|
|||||||
bodyInfo: BodyPair<BodyType>,
|
bodyInfo: BodyPair<BodyType>,
|
||||||
resultDeserializer: DeserializationStrategy<ResultType>,
|
resultDeserializer: DeserializationStrategy<ResultType>,
|
||||||
serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat
|
serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat
|
||||||
) = post<StandardKtorSerialInputData>(url) {
|
) = post(url) {
|
||||||
body = serialFormat.encodeDefault(bodyInfo.first, bodyInfo.second)
|
setBody(
|
||||||
|
serialFormat.encodeDefault(bodyInfo.first, bodyInfo.second)
|
||||||
|
)
|
||||||
}.let {
|
}.let {
|
||||||
serialFormat.decodeDefault(resultDeserializer, it)
|
serialFormat.decodeDefault(resultDeserializer, it.body<StandardKtorSerialInputData>())
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun <ResultType> HttpClient.unimultipart(
|
suspend fun <ResultType> HttpClient.unimultipart(
|
||||||
@@ -139,7 +141,7 @@ suspend fun <ResultType> HttpClient.unimultipart(
|
|||||||
dataHeadersBuilder: HeadersBuilder.() -> Unit = {},
|
dataHeadersBuilder: HeadersBuilder.() -> Unit = {},
|
||||||
requestBuilder: HttpRequestBuilder.() -> Unit = {},
|
requestBuilder: HttpRequestBuilder.() -> Unit = {},
|
||||||
serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat
|
serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat
|
||||||
): ResultType = submitFormWithBinaryData<StandardKtorSerialInputData>(
|
): ResultType = submitFormWithBinaryData(
|
||||||
url,
|
url,
|
||||||
formData = formData {
|
formData = formData {
|
||||||
append(
|
append(
|
||||||
@@ -155,7 +157,7 @@ suspend fun <ResultType> HttpClient.unimultipart(
|
|||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
requestBuilder()
|
requestBuilder()
|
||||||
}.let { serialFormat.decodeDefault(resultDeserializer, it) }
|
}.let { serialFormat.decodeDefault(resultDeserializer, it.body<StandardKtorSerialInputData>()) }
|
||||||
|
|
||||||
suspend fun <BodyType, ResultType> HttpClient.unimultipart(
|
suspend fun <BodyType, ResultType> HttpClient.unimultipart(
|
||||||
url: String,
|
url: String,
|
||||||
|
@@ -4,9 +4,10 @@ import dev.inmo.micro_utils.common.MPPFile
|
|||||||
import dev.inmo.micro_utils.common.filename
|
import dev.inmo.micro_utils.common.filename
|
||||||
import dev.inmo.micro_utils.ktor.common.TemporalFileId
|
import dev.inmo.micro_utils.ktor.common.TemporalFileId
|
||||||
import io.ktor.client.HttpClient
|
import io.ktor.client.HttpClient
|
||||||
import io.ktor.client.features.onUpload
|
import io.ktor.client.plugins.onUpload
|
||||||
import io.ktor.client.request.forms.formData
|
import io.ktor.client.request.forms.formData
|
||||||
import io.ktor.client.request.forms.submitFormWithBinaryData
|
import io.ktor.client.request.forms.submitFormWithBinaryData
|
||||||
|
import io.ktor.client.statement.bodyAsText
|
||||||
import io.ktor.http.Headers
|
import io.ktor.http.Headers
|
||||||
import io.ktor.http.HttpHeaders
|
import io.ktor.http.HttpHeaders
|
||||||
import java.net.URLConnection
|
import java.net.URLConnection
|
||||||
@@ -20,7 +21,7 @@ actual suspend fun HttpClient.tempUpload(
|
|||||||
onUpload: (Long, Long) -> Unit
|
onUpload: (Long, Long) -> Unit
|
||||||
): TemporalFileId {
|
): TemporalFileId {
|
||||||
val inputProvider = file.inputProvider()
|
val inputProvider = file.inputProvider()
|
||||||
val fileId = submitFormWithBinaryData<String>(
|
val fileId = submitFormWithBinaryData(
|
||||||
fullTempUploadDraftPath,
|
fullTempUploadDraftPath,
|
||||||
formData = formData {
|
formData = formData {
|
||||||
append(
|
append(
|
||||||
@@ -34,6 +35,6 @@ actual suspend fun HttpClient.tempUpload(
|
|||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
onUpload(onUpload)
|
onUpload(onUpload)
|
||||||
}
|
}.bodyAsText()
|
||||||
return TemporalFileId(fileId)
|
return TemporalFileId(fileId)
|
||||||
}
|
}
|
||||||
|
@@ -1,3 +1,5 @@
|
|||||||
|
@file:Suppress("NOTHING_TO_INLINE")
|
||||||
|
|
||||||
package dev.inmo.micro_utils.ktor.common
|
package dev.inmo.micro_utils.ktor.common
|
||||||
|
|
||||||
import kotlinx.serialization.*
|
import kotlinx.serialization.*
|
||||||
|
@@ -19,7 +19,8 @@ kotlin {
|
|||||||
api libs.ktor.server
|
api libs.ktor.server
|
||||||
api libs.ktor.server.cio
|
api libs.ktor.server.cio
|
||||||
api libs.ktor.server.host.common
|
api libs.ktor.server.host.common
|
||||||
api libs.ktor.websockets
|
api libs.ktor.server.websockets
|
||||||
|
api libs.ktor.server.statusPages
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -2,26 +2,26 @@ package dev.inmo.micro_utils.ktor.server
|
|||||||
|
|
||||||
import dev.inmo.micro_utils.coroutines.safely
|
import dev.inmo.micro_utils.coroutines.safely
|
||||||
import dev.inmo.micro_utils.ktor.common.*
|
import dev.inmo.micro_utils.ktor.common.*
|
||||||
import io.ktor.application.featureOrNull
|
|
||||||
import io.ktor.application.install
|
|
||||||
import io.ktor.http.URLProtocol
|
import io.ktor.http.URLProtocol
|
||||||
import io.ktor.http.cio.websocket.*
|
import io.ktor.server.application.install
|
||||||
import io.ktor.routing.Route
|
import io.ktor.server.application.pluginOrNull
|
||||||
import io.ktor.routing.application
|
import io.ktor.server.routing.Route
|
||||||
import io.ktor.websocket.*
|
import io.ktor.server.routing.application
|
||||||
|
import io.ktor.server.websocket.*
|
||||||
|
import io.ktor.websocket.send
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.serialization.SerializationStrategy
|
import kotlinx.serialization.SerializationStrategy
|
||||||
|
|
||||||
fun <T> Route.includeWebsocketHandling(
|
fun <T> Route.includeWebsocketHandling(
|
||||||
suburl: String,
|
suburl: String,
|
||||||
flow: Flow<T>,
|
flow: Flow<T>,
|
||||||
protocol: URLProtocol = URLProtocol.WS,
|
protocol: URLProtocol? = null,
|
||||||
converter: suspend WebSocketServerSession.(T) -> StandardKtorSerialInputData?
|
converter: suspend WebSocketServerSession.(T) -> StandardKtorSerialInputData?
|
||||||
) {
|
) {
|
||||||
application.apply {
|
application.apply {
|
||||||
featureOrNull(io.ktor.websocket.WebSockets) ?: install(io.ktor.websocket.WebSockets)
|
pluginOrNull(WebSockets) ?: install(WebSockets)
|
||||||
}
|
}
|
||||||
webSocket(suburl, protocol.name) {
|
webSocket(suburl, protocol ?.name) {
|
||||||
safely {
|
safely {
|
||||||
flow.collect {
|
flow.collect {
|
||||||
converter(it) ?.let { data ->
|
converter(it) ?.let { data ->
|
||||||
@@ -37,7 +37,7 @@ fun <T> Route.includeWebsocketHandling(
|
|||||||
flow: Flow<T>,
|
flow: Flow<T>,
|
||||||
serializer: SerializationStrategy<T>,
|
serializer: SerializationStrategy<T>,
|
||||||
serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat,
|
serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat,
|
||||||
protocol: URLProtocol = URLProtocol.WS,
|
protocol: URLProtocol? = null,
|
||||||
filter: (suspend WebSocketServerSession.(T) -> Boolean)? = null
|
filter: (suspend WebSocketServerSession.(T) -> Boolean)? = null
|
||||||
) = includeWebsocketHandling(
|
) = includeWebsocketHandling(
|
||||||
suburl,
|
suburl,
|
||||||
|
@@ -3,25 +3,21 @@ package dev.inmo.micro_utils.ktor.server
|
|||||||
import dev.inmo.micro_utils.common.*
|
import dev.inmo.micro_utils.common.*
|
||||||
import dev.inmo.micro_utils.coroutines.safely
|
import dev.inmo.micro_utils.coroutines.safely
|
||||||
import dev.inmo.micro_utils.ktor.common.*
|
import dev.inmo.micro_utils.ktor.common.*
|
||||||
import io.ktor.application.ApplicationCall
|
|
||||||
import io.ktor.application.call
|
|
||||||
import io.ktor.http.*
|
import io.ktor.http.*
|
||||||
import io.ktor.http.content.PartData
|
import io.ktor.http.content.*
|
||||||
import io.ktor.http.content.forEachPart
|
import io.ktor.server.application.ApplicationCall
|
||||||
import io.ktor.request.receive
|
import io.ktor.server.application.call
|
||||||
import io.ktor.request.receiveMultipart
|
import io.ktor.server.request.receive
|
||||||
import io.ktor.response.respond
|
import io.ktor.server.request.receiveMultipart
|
||||||
import io.ktor.response.respondBytes
|
import io.ktor.server.response.respond
|
||||||
import io.ktor.routing.Route
|
import io.ktor.server.response.respondBytes
|
||||||
import io.ktor.util.asStream
|
import io.ktor.server.routing.Route
|
||||||
import io.ktor.util.cio.writeChannel
|
import io.ktor.server.websocket.WebSocketServerSession
|
||||||
import io.ktor.util.pipeline.PipelineContext
|
import io.ktor.util.pipeline.PipelineContext
|
||||||
import io.ktor.utils.io.core.*
|
import io.ktor.utils.io.core.*
|
||||||
import io.ktor.websocket.WebSocketServerSession
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.serialization.*
|
import kotlinx.serialization.DeserializationStrategy
|
||||||
import java.io.File
|
import kotlinx.serialization.SerializationStrategy
|
||||||
import java.io.File.createTempFile
|
|
||||||
|
|
||||||
class UnifiedRouter(
|
class UnifiedRouter(
|
||||||
val serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat,
|
val serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat,
|
||||||
@@ -31,7 +27,7 @@ class UnifiedRouter(
|
|||||||
suburl: String,
|
suburl: String,
|
||||||
flow: Flow<T>,
|
flow: Flow<T>,
|
||||||
serializer: SerializationStrategy<T>,
|
serializer: SerializationStrategy<T>,
|
||||||
protocol: URLProtocol = URLProtocol.WS,
|
protocol: URLProtocol? = null,
|
||||||
filter: (suspend WebSocketServerSession.(T) -> Boolean)? = null
|
filter: (suspend WebSocketServerSession.(T) -> Boolean)? = null
|
||||||
) = includeWebsocketHandling(suburl, flow, serializer, serialFormat, protocol, filter)
|
) = includeWebsocketHandling(suburl, flow, serializer, serialFormat, protocol, filter)
|
||||||
|
|
||||||
@@ -197,7 +193,9 @@ suspend fun <T> ApplicationCall.uniloadMultipartFile(
|
|||||||
".${name.extension}"
|
".${name.extension}"
|
||||||
).apply {
|
).apply {
|
||||||
outputStream().use { fileStream ->
|
outputStream().use { fileStream ->
|
||||||
it.provider().asStream().copyTo(fileStream)
|
it.streamProvider().use {
|
||||||
|
it.copyTo(fileStream)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -239,7 +237,9 @@ suspend fun ApplicationCall.uniloadMultipartFile(
|
|||||||
".${name.extension}"
|
".${name.extension}"
|
||||||
).apply {
|
).apply {
|
||||||
outputStream().use { fileStream ->
|
outputStream().use { fileStream ->
|
||||||
it.provider().asStream().copyTo(fileStream)
|
it.streamProvider().use {
|
||||||
|
it.copyTo(fileStream)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
package dev.inmo.micro_utils.ktor.server
|
package dev.inmo.micro_utils.ktor.server
|
||||||
|
|
||||||
import dev.inmo.micro_utils.ktor.server.configurators.KtorApplicationConfigurator
|
import dev.inmo.micro_utils.ktor.server.configurators.KtorApplicationConfigurator
|
||||||
import io.ktor.application.Application
|
import io.ktor.server.application.Application
|
||||||
import io.ktor.server.cio.CIO
|
import io.ktor.server.cio.CIO
|
||||||
import io.ktor.server.cio.CIOApplicationEngine
|
import io.ktor.server.cio.CIOApplicationEngine
|
||||||
import io.ktor.server.engine.*
|
import io.ktor.server.engine.*
|
||||||
|
@@ -7,14 +7,14 @@ import dev.inmo.micro_utils.coroutines.launchSafelyWithoutExceptions
|
|||||||
import dev.inmo.micro_utils.ktor.common.DefaultTemporalFilesSubPath
|
import dev.inmo.micro_utils.ktor.common.DefaultTemporalFilesSubPath
|
||||||
import dev.inmo.micro_utils.ktor.common.TemporalFileId
|
import dev.inmo.micro_utils.ktor.common.TemporalFileId
|
||||||
import dev.inmo.micro_utils.ktor.server.configurators.ApplicationRoutingConfigurator
|
import dev.inmo.micro_utils.ktor.server.configurators.ApplicationRoutingConfigurator
|
||||||
import io.ktor.application.call
|
|
||||||
import io.ktor.http.HttpStatusCode
|
import io.ktor.http.HttpStatusCode
|
||||||
import io.ktor.http.content.PartData
|
import io.ktor.http.content.PartData
|
||||||
import io.ktor.http.content.streamProvider
|
import io.ktor.http.content.streamProvider
|
||||||
import io.ktor.request.receiveMultipart
|
import io.ktor.server.application.call
|
||||||
import io.ktor.response.respond
|
import io.ktor.server.request.receiveMultipart
|
||||||
import io.ktor.routing.Route
|
import io.ktor.server.response.respond
|
||||||
import io.ktor.routing.post
|
import io.ktor.server.routing.Route
|
||||||
|
import io.ktor.server.routing.post
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.flow.*
|
import kotlinx.coroutines.flow.*
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
|
@@ -1,14 +1,15 @@
|
|||||||
package dev.inmo.micro_utils.ktor.server.configurators
|
package dev.inmo.micro_utils.ktor.server.configurators
|
||||||
|
|
||||||
import io.ktor.application.Application
|
import io.ktor.server.application.Application
|
||||||
import io.ktor.application.install
|
import io.ktor.server.application.install
|
||||||
import io.ktor.features.CachingHeaders
|
import io.ktor.server.plugins.cachingheaders.CachingHeaders
|
||||||
|
import io.ktor.server.plugins.cachingheaders.CachingHeadersConfig
|
||||||
import kotlinx.serialization.Contextual
|
import kotlinx.serialization.Contextual
|
||||||
|
|
||||||
data class ApplicationCachingHeadersConfigurator(
|
data class ApplicationCachingHeadersConfigurator(
|
||||||
private val elements: List<@Contextual Element>
|
private val elements: List<@Contextual Element>
|
||||||
) : KtorApplicationConfigurator {
|
) : KtorApplicationConfigurator {
|
||||||
fun interface Element { operator fun CachingHeaders.Configuration.invoke() }
|
fun interface Element { operator fun CachingHeadersConfig.invoke() }
|
||||||
|
|
||||||
override fun Application.configure() {
|
override fun Application.configure() {
|
||||||
install(CachingHeaders) {
|
install(CachingHeaders) {
|
||||||
|
@@ -1,8 +1,9 @@
|
|||||||
package dev.inmo.micro_utils.ktor.server.configurators
|
package dev.inmo.micro_utils.ktor.server.configurators
|
||||||
|
|
||||||
import io.ktor.application.*
|
import dev.inmo.micro_utils.ktor.server.configurators.ApplicationRoutingConfigurator.Element
|
||||||
import io.ktor.routing.Route
|
import io.ktor.server.application.*
|
||||||
import io.ktor.routing.Routing
|
import io.ktor.server.routing.Route
|
||||||
|
import io.ktor.server.routing.Routing
|
||||||
import kotlinx.serialization.Contextual
|
import kotlinx.serialization.Contextual
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@@ -18,7 +19,7 @@ class ApplicationRoutingConfigurator(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun Application.configure() {
|
override fun Application.configure() {
|
||||||
featureOrNull(Routing) ?.apply {
|
pluginOrNull(Routing) ?.apply {
|
||||||
rootInstaller.apply { invoke() }
|
rootInstaller.apply { invoke() }
|
||||||
} ?: install(Routing) {
|
} ?: install(Routing) {
|
||||||
rootInstaller.apply { invoke() }
|
rootInstaller.apply { invoke() }
|
||||||
|
@@ -1,14 +1,15 @@
|
|||||||
package dev.inmo.micro_utils.ktor.server.configurators
|
package dev.inmo.micro_utils.ktor.server.configurators
|
||||||
|
|
||||||
import io.ktor.application.Application
|
import io.ktor.server.application.Application
|
||||||
import io.ktor.application.install
|
import io.ktor.server.application.install
|
||||||
import io.ktor.sessions.Sessions
|
import io.ktor.server.sessions.Sessions
|
||||||
|
import io.ktor.server.sessions.SessionsConfig
|
||||||
import kotlinx.serialization.Contextual
|
import kotlinx.serialization.Contextual
|
||||||
|
|
||||||
class ApplicationSessionsConfigurator(
|
class ApplicationSessionsConfigurator(
|
||||||
private val elements: List<@Contextual Element>
|
private val elements: List<@Contextual Element>
|
||||||
) : KtorApplicationConfigurator {
|
) : KtorApplicationConfigurator {
|
||||||
fun interface Element { operator fun Sessions.Configuration.invoke() }
|
fun interface Element { operator fun SessionsConfig.invoke() }
|
||||||
|
|
||||||
override fun Application.configure() {
|
override fun Application.configure() {
|
||||||
install(Sessions) {
|
install(Sessions) {
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
package dev.inmo.micro_utils.ktor.server.configurators
|
package dev.inmo.micro_utils.ktor.server.configurators
|
||||||
|
|
||||||
import io.ktor.application.Application
|
import io.ktor.server.application.Application
|
||||||
|
|
||||||
interface KtorApplicationConfigurator {
|
interface KtorApplicationConfigurator {
|
||||||
fun Application.configure()
|
fun Application.configure()
|
||||||
|
@@ -1,14 +1,15 @@
|
|||||||
package dev.inmo.micro_utils.ktor.server.configurators
|
package dev.inmo.micro_utils.ktor.server.configurators
|
||||||
|
|
||||||
import io.ktor.application.Application
|
import io.ktor.server.application.Application
|
||||||
import io.ktor.application.install
|
import io.ktor.server.application.install
|
||||||
import io.ktor.features.StatusPages
|
import io.ktor.server.plugins.statuspages.StatusPages
|
||||||
|
import io.ktor.server.plugins.statuspages.StatusPagesConfig
|
||||||
import kotlinx.serialization.Contextual
|
import kotlinx.serialization.Contextual
|
||||||
|
|
||||||
class StatusPagesConfigurator(
|
class StatusPagesConfigurator(
|
||||||
private val elements: List<@Contextual Element>
|
private val elements: List<@Contextual Element>
|
||||||
) : KtorApplicationConfigurator {
|
) : KtorApplicationConfigurator {
|
||||||
fun interface Element { operator fun StatusPages.Configuration.invoke() }
|
fun interface Element { operator fun StatusPagesConfig.invoke() }
|
||||||
|
|
||||||
override fun Application.configure() {
|
override fun Application.configure() {
|
||||||
install(StatusPages) {
|
install(StatusPages) {
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
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.statement.bodyAsText
|
||||||
import kotlinx.serialization.SerialName
|
import kotlinx.serialization.SerialName
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.serialization.builtins.ListSerializer
|
import kotlinx.serialization.builtins.ListSerializer
|
||||||
@@ -164,7 +165,7 @@ suspend fun main(vararg args: String) {
|
|||||||
|
|
||||||
val ietfLanguageCodes = json.decodeFromString(
|
val ietfLanguageCodes = json.decodeFromString(
|
||||||
ListSerializer(LanguageCode.serializer()),
|
ListSerializer(LanguageCode.serializer()),
|
||||||
client.get(ietfLanguageCodesLink)
|
client.get(ietfLanguageCodesLink).bodyAsText()
|
||||||
).map {
|
).map {
|
||||||
it.copy(
|
it.copy(
|
||||||
title = it.title
|
title = it.title
|
||||||
@@ -175,7 +176,7 @@ suspend fun main(vararg args: String) {
|
|||||||
}
|
}
|
||||||
val ietfLanguageCodesWithTagsMap = json.decodeFromString(
|
val ietfLanguageCodesWithTagsMap = json.decodeFromString(
|
||||||
ListSerializer(LanguageCodeWithTag.serializer()),
|
ListSerializer(LanguageCodeWithTag.serializer()),
|
||||||
client.get(ietfLanguageCodesAdditionalTagsLink)
|
client.get(ietfLanguageCodesAdditionalTagsLink).bodyAsText()
|
||||||
).filter { it.withSubtag != it.tag }.groupBy { it.tag }
|
).filter { it.withSubtag != it.tag }.groupBy { it.tag }
|
||||||
|
|
||||||
val tags = ietfLanguageCodes.map {
|
val tags = ietfLanguageCodes.map {
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
package dev.inmo.micro_utils.mime_types
|
package dev.inmo.micro_utils.mime_types
|
||||||
|
|
||||||
import kotlinx.serialization.KSerializer
|
import kotlinx.serialization.*
|
||||||
import kotlinx.serialization.Serializer
|
|
||||||
import kotlinx.serialization.descriptors.*
|
import kotlinx.serialization.descriptors.*
|
||||||
import kotlinx.serialization.encoding.Decoder
|
import kotlinx.serialization.encoding.Decoder
|
||||||
import kotlinx.serialization.encoding.Encoder
|
import kotlinx.serialization.encoding.Encoder
|
||||||
@@ -16,6 +15,7 @@ fun mimeType(raw: String) = mimesCache.getOrPut(raw) {
|
|||||||
|
|
||||||
internal fun parseMimeType(raw: String): MimeType = CustomMimeType(raw)
|
internal fun parseMimeType(raw: String): MimeType = CustomMimeType(raw)
|
||||||
|
|
||||||
|
@Suppress("OPT_IN_USAGE")
|
||||||
@Serializer(MimeType::class)
|
@Serializer(MimeType::class)
|
||||||
object MimeTypeSerializer : KSerializer<MimeType> {
|
object MimeTypeSerializer : KSerializer<MimeType> {
|
||||||
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("mimeType", PrimitiveKind.STRING)
|
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("mimeType", PrimitiveKind.STRING)
|
||||||
|
@@ -27,4 +27,5 @@ inline fun <T> PaginationResult<T>.thisPageIfNotEmpty(): PaginationResult<T>? =
|
|||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
inline fun <T> PaginationResult<T>.currentPageIfNotEmpty() = thisPageIfNotEmpty()
|
inline fun <T> PaginationResult<T>.currentPageIfNotEmpty() = thisPageIfNotEmpty()
|
||||||
|
@@ -2,19 +2,19 @@ package dev.inmo.micro_utils.pagination.utils
|
|||||||
|
|
||||||
import dev.inmo.micro_utils.pagination.*
|
import dev.inmo.micro_utils.pagination.*
|
||||||
|
|
||||||
suspend fun <T> doForAll(
|
inline fun <T> doForAll(
|
||||||
initialPagination: Pagination = FirstPagePagination(),
|
initialPagination: Pagination = FirstPagePagination(),
|
||||||
paginationMapper: (PaginationResult<T>) -> Pagination?,
|
paginationMapper: (PaginationResult<T>) -> Pagination?,
|
||||||
block: suspend (Pagination) -> PaginationResult<T>
|
block: (Pagination) -> PaginationResult<T>
|
||||||
) {
|
) {
|
||||||
doWithPagination(initialPagination) {
|
doWithPagination(initialPagination) {
|
||||||
block(it).let(paginationMapper)
|
block(it).let(paginationMapper)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun <T> doForAllWithNextPaging(
|
inline fun <T> doForAllWithNextPaging(
|
||||||
initialPagination: Pagination = FirstPagePagination(),
|
initialPagination: Pagination = FirstPagePagination(),
|
||||||
block: suspend (Pagination) -> PaginationResult<T>
|
block: (Pagination) -> PaginationResult<T>
|
||||||
) {
|
) {
|
||||||
doForAll(
|
doForAll(
|
||||||
initialPagination,
|
initialPagination,
|
||||||
@@ -23,9 +23,9 @@ suspend fun <T> doForAllWithNextPaging(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun <T> doAllWithCurrentPaging(
|
inline fun <T> doAllWithCurrentPaging(
|
||||||
initialPagination: Pagination = FirstPagePagination(),
|
initialPagination: Pagination = FirstPagePagination(),
|
||||||
block: suspend (Pagination) -> PaginationResult<T>
|
block: (Pagination) -> PaginationResult<T>
|
||||||
) {
|
) {
|
||||||
doForAll(
|
doForAll(
|
||||||
initialPagination,
|
initialPagination,
|
||||||
@@ -34,7 +34,7 @@ suspend fun <T> doAllWithCurrentPaging(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun <T> doForAllWithCurrentPaging(
|
inline fun <T> doForAllWithCurrentPaging(
|
||||||
initialPagination: Pagination = FirstPagePagination(),
|
initialPagination: Pagination = FirstPagePagination(),
|
||||||
block: suspend (Pagination) -> PaginationResult<T>
|
block: (Pagination) -> PaginationResult<T>
|
||||||
) = doAllWithCurrentPaging(initialPagination, block)
|
) = doAllWithCurrentPaging(initialPagination, block)
|
||||||
|
@@ -2,10 +2,10 @@ package dev.inmo.micro_utils.pagination.utils
|
|||||||
|
|
||||||
import dev.inmo.micro_utils.pagination.*
|
import dev.inmo.micro_utils.pagination.*
|
||||||
|
|
||||||
suspend fun <T> getAll(
|
inline fun <T> getAll(
|
||||||
initialPagination: Pagination = FirstPagePagination(),
|
initialPagination: Pagination = FirstPagePagination(),
|
||||||
paginationMapper: (PaginationResult<T>) -> Pagination?,
|
paginationMapper: (PaginationResult<T>) -> Pagination?,
|
||||||
block: suspend (Pagination) -> PaginationResult<T>
|
block: (Pagination) -> PaginationResult<T>
|
||||||
): List<T> {
|
): List<T> {
|
||||||
val results = mutableListOf<T>()
|
val results = mutableListOf<T>()
|
||||||
doForAll(initialPagination, paginationMapper) {
|
doForAll(initialPagination, paginationMapper) {
|
||||||
@@ -16,46 +16,45 @@ suspend fun <T> getAll(
|
|||||||
return results.toList()
|
return results.toList()
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun <T, R> R.getAllBy(
|
inline fun <T, R> R.getAllBy(
|
||||||
initialPagination: Pagination = FirstPagePagination(),
|
initialPagination: Pagination = FirstPagePagination(),
|
||||||
paginationMapper: R.(PaginationResult<T>) -> Pagination?,
|
paginationMapper: R.(PaginationResult<T>) -> Pagination?,
|
||||||
block: suspend R.(Pagination) -> PaginationResult<T>
|
block: R.(Pagination) -> PaginationResult<T>
|
||||||
): List<T> = getAll(
|
): List<T> = getAll(
|
||||||
initialPagination,
|
initialPagination,
|
||||||
{ paginationMapper(it) },
|
{ paginationMapper(it) },
|
||||||
{ block(it) }
|
{ block(it) }
|
||||||
)
|
)
|
||||||
|
|
||||||
suspend fun <T> getAllWithNextPaging(
|
inline fun <T> getAllWithNextPaging(
|
||||||
initialPagination: Pagination = FirstPagePagination(),
|
initialPagination: Pagination = FirstPagePagination(),
|
||||||
block: suspend (Pagination) -> PaginationResult<T>
|
block: (Pagination) -> PaginationResult<T>
|
||||||
): List<T> = getAll(
|
): List<T> = getAll(
|
||||||
initialPagination,
|
initialPagination,
|
||||||
{ it.nextPageIfNotEmpty() },
|
{ it.nextPageIfNotEmpty() },
|
||||||
block
|
block
|
||||||
)
|
)
|
||||||
|
|
||||||
suspend fun <T, R> R.getAllByWithNextPaging(
|
inline fun <T, R> R.getAllByWithNextPaging(
|
||||||
initialPagination: Pagination = FirstPagePagination(),
|
initialPagination: Pagination = FirstPagePagination(),
|
||||||
block: suspend R.(Pagination) -> PaginationResult<T>
|
block: R.(Pagination) -> PaginationResult<T>
|
||||||
): List<T> = getAllWithNextPaging(
|
): List<T> = getAllWithNextPaging(
|
||||||
initialPagination,
|
initialPagination,
|
||||||
{ block(it) }
|
{ block(it) }
|
||||||
)
|
)
|
||||||
|
|
||||||
suspend fun <T> getAllWithCurrentPaging(
|
inline fun <T> getAllWithCurrentPaging(
|
||||||
initialPagination: Pagination = FirstPagePagination(),
|
initialPagination: Pagination = FirstPagePagination(),
|
||||||
block: suspend (Pagination) -> PaginationResult<T>
|
block: (Pagination) -> PaginationResult<T>
|
||||||
): List<T> = getAll(
|
): List<T> = getAll(
|
||||||
initialPagination,
|
initialPagination,
|
||||||
{ it.currentPageIfNotEmpty() },
|
{ it.currentPageIfNotEmpty() },
|
||||||
block
|
block
|
||||||
)
|
)
|
||||||
|
|
||||||
suspend fun <T, R> R.getAllByWithCurrentPaging(
|
inline fun <T, R> R.getAllByWithCurrentPaging(
|
||||||
initialPagination: Pagination = FirstPagePagination(),
|
initialPagination: Pagination = FirstPagePagination(),
|
||||||
block: suspend R.(Pagination) -> PaginationResult<T>
|
block: R.(Pagination) -> PaginationResult<T>
|
||||||
): List<T> = getAllWithCurrentPaging(
|
): List<T> = getAllWithCurrentPaging(
|
||||||
initialPagination,
|
initialPagination
|
||||||
{ block(it) }
|
) { block(it) }
|
||||||
)
|
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
package dev.inmo.micro_utils.pagination
|
package dev.inmo.micro_utils.pagination
|
||||||
|
|
||||||
import io.ktor.application.ApplicationCall
|
|
||||||
import io.ktor.http.Parameters
|
import io.ktor.http.Parameters
|
||||||
|
import io.ktor.server.application.ApplicationCall
|
||||||
|
|
||||||
val Parameters.extractPagination: Pagination
|
val Parameters.extractPagination: Pagination
|
||||||
get() = SimplePagination(
|
get() = SimplePagination(
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
package dev.inmo.micro_utils.repos
|
package dev.inmo.micro_utils.repos
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
interface MapperRepo<FromKey, FromValue, ToKey, ToValue> {
|
interface MapperRepo<FromKey, FromValue, ToKey, ToValue> {
|
||||||
suspend fun FromKey.toOutKey() = this as ToKey
|
suspend fun FromKey.toOutKey() = this as ToKey
|
||||||
suspend fun FromValue.toOutValue() = this as ToValue
|
suspend fun FromValue.toOutValue() = this as ToValue
|
||||||
|
@@ -126,9 +126,10 @@ inline fun <reified FromKey, reified FromValue, reified ToKey, reified ToValue>
|
|||||||
mapper(keyFromToTo, valueFromToTo, keyToToFrom, valueToToFrom)
|
mapper(keyFromToTo, valueFromToTo, keyToToFrom, valueToToFrom)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@Suppress("DELEGATED_MEMBER_HIDES_SUPERTYPE_OVERRIDE")
|
||||||
open class MapperStandardKeyValueRepo<FromKey, FromValue, ToKey, ToValue>(
|
open class MapperStandardKeyValueRepo<FromKey, FromValue, ToKey, ToValue>(
|
||||||
private val to: StandardKeyValueRepo<ToKey, ToValue>,
|
private val to: StandardKeyValueRepo<ToKey, ToValue>,
|
||||||
mapper: MapperRepo<FromKey, FromValue, ToKey, ToValue>
|
private val mapper: MapperRepo<FromKey, FromValue, ToKey, ToValue>
|
||||||
) : StandardKeyValueRepo<FromKey, FromValue>,
|
) : StandardKeyValueRepo<FromKey, FromValue>,
|
||||||
MapperRepo<FromKey, FromValue, ToKey, ToValue> by mapper,
|
MapperRepo<FromKey, FromValue, ToKey, ToValue> by mapper,
|
||||||
ReadStandardKeyValueRepo<FromKey, FromValue> by MapperReadStandardKeyValueRepo(to, mapper),
|
ReadStandardKeyValueRepo<FromKey, FromValue> by MapperReadStandardKeyValueRepo(to, mapper),
|
||||||
|
@@ -132,6 +132,7 @@ inline fun <reified FromKey, reified FromValue, reified ToKey, reified ToValue>
|
|||||||
mapper(keyFromToTo, valueFromToTo, keyToToFrom, valueToToFrom)
|
mapper(keyFromToTo, valueFromToTo, keyToToFrom, valueToToFrom)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@Suppress("DELEGATED_MEMBER_HIDES_SUPERTYPE_OVERRIDE")
|
||||||
open class MapperOneToManyKeyValueRepo<FromKey, FromValue, ToKey, ToValue>(
|
open class MapperOneToManyKeyValueRepo<FromKey, FromValue, ToKey, ToValue>(
|
||||||
private val to: OneToManyKeyValueRepo<ToKey, ToValue>,
|
private val to: OneToManyKeyValueRepo<ToKey, ToValue>,
|
||||||
mapper: MapperRepo<FromKey, FromValue, ToKey, ToValue>
|
mapper: MapperRepo<FromKey, FromValue, ToKey, ToValue>
|
||||||
|
@@ -175,9 +175,11 @@ class FileWriteStandardKeyValueRepo(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Warning("Files watching will not correctly works on Android Platform with version of API lower than API 26")
|
@Warning("Files watching will not correctly works on Android Platform with version of API lower than API 26")
|
||||||
|
@Suppress("DELEGATED_MEMBER_HIDES_SUPERTYPE_OVERRIDE")
|
||||||
class FileStandardKeyValueRepo(
|
class FileStandardKeyValueRepo(
|
||||||
folder: File,
|
folder: File,
|
||||||
filesChangedProcessingScope: CoroutineScope? = null
|
filesChangedProcessingScope: CoroutineScope? = null
|
||||||
) : StandardKeyValueRepo<String, File>,
|
) : StandardKeyValueRepo<String, File>,
|
||||||
WriteStandardKeyValueRepo<String, File> by FileWriteStandardKeyValueRepo(folder, filesChangedProcessingScope),
|
WriteStandardKeyValueRepo<String, File> by FileWriteStandardKeyValueRepo(folder, filesChangedProcessingScope),
|
||||||
ReadStandardKeyValueRepo<String, File> by FileReadStandardKeyValueRepo(folder)
|
ReadStandardKeyValueRepo<String, File> by FileReadStandardKeyValueRepo(folder) {
|
||||||
|
}
|
||||||
|
@@ -16,6 +16,7 @@ fun <T : Any> Context.keyValueStore(
|
|||||||
name: String = "default",
|
name: String = "default",
|
||||||
cacheValues: Boolean = false
|
cacheValues: Boolean = false
|
||||||
): StandardKeyValueRepo<String, T> {
|
): StandardKeyValueRepo<String, T> {
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
return cache.getOrPut(name) {
|
return cache.getOrPut(name) {
|
||||||
KeyValueStore<T>(this, name, cacheValues)
|
KeyValueStore<T>(this, name, cacheValues)
|
||||||
} as KeyValueStore<T>
|
} as KeyValueStore<T>
|
||||||
@@ -62,6 +63,7 @@ class KeyValueStore<T : Any> internal constructor (
|
|||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun get(k: String): T? {
|
override suspend fun get(k: String): T? {
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
return (cachedData ?. get(k) ?: sharedPreferences.all[k]) as? T
|
return (cachedData ?. get(k) ?: sharedPreferences.all[k]) as? T
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,7 +75,10 @@ class KeyValueStore<T : Any> internal constructor (
|
|||||||
PaginationResult(
|
PaginationResult(
|
||||||
it.page,
|
it.page,
|
||||||
it.pagesNumber,
|
it.pagesNumber,
|
||||||
it.results.map { it as T }.let { if (reversed) it.reversed() else it },
|
it.results.map {
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
it as T
|
||||||
|
}.let { if (reversed) it.reversed() else it },
|
||||||
it.size
|
it.size
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -155,3 +160,9 @@ class KeyValueStore<T : Any> internal constructor (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline fun <T : Any> SharedPreferencesKeyValueRepo(
|
||||||
|
context: Context,
|
||||||
|
name: String = "default",
|
||||||
|
cacheValues: Boolean = false
|
||||||
|
) = context.keyValueStore<T>(name, cacheValues)
|
||||||
|
@@ -7,10 +7,10 @@ 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.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.application.call
|
|
||||||
import io.ktor.http.ContentType
|
import io.ktor.http.ContentType
|
||||||
import io.ktor.routing.Route
|
import io.ktor.server.application.call
|
||||||
import io.ktor.routing.get
|
import io.ktor.server.routing.Route
|
||||||
|
import io.ktor.server.routing.get
|
||||||
import kotlinx.serialization.KSerializer
|
import kotlinx.serialization.KSerializer
|
||||||
import kotlinx.serialization.builtins.serializer
|
import kotlinx.serialization.builtins.serializer
|
||||||
|
|
||||||
|
@@ -6,8 +6,8 @@ import dev.inmo.micro_utils.ktor.server.UnifiedRouter
|
|||||||
import dev.inmo.micro_utils.ktor.server.standardKtorSerialFormatContentType
|
import dev.inmo.micro_utils.ktor.server.standardKtorSerialFormatContentType
|
||||||
import dev.inmo.micro_utils.repos.StandardCRUDRepo
|
import dev.inmo.micro_utils.repos.StandardCRUDRepo
|
||||||
import io.ktor.http.ContentType
|
import io.ktor.http.ContentType
|
||||||
import io.ktor.routing.Route
|
import io.ktor.server.routing.Route
|
||||||
import io.ktor.routing.route
|
import io.ktor.server.routing.route
|
||||||
import kotlinx.serialization.KSerializer
|
import kotlinx.serialization.KSerializer
|
||||||
|
|
||||||
fun <ObjectType, IdType, InputValue> Route.configureStandardCrudRepoRoutes(
|
fun <ObjectType, IdType, InputValue> Route.configureStandardCrudRepoRoutes(
|
||||||
|
@@ -5,10 +5,9 @@ import dev.inmo.micro_utils.ktor.common.standardKtorSerialFormat
|
|||||||
import dev.inmo.micro_utils.ktor.server.*
|
import dev.inmo.micro_utils.ktor.server.*
|
||||||
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.http.ContentType
|
import io.ktor.http.ContentType
|
||||||
import io.ktor.routing.Route
|
import io.ktor.server.routing.Route
|
||||||
import io.ktor.routing.post
|
import io.ktor.server.routing.post
|
||||||
import kotlinx.serialization.KSerializer
|
import kotlinx.serialization.KSerializer
|
||||||
import kotlinx.serialization.builtins.*
|
import kotlinx.serialization.builtins.*
|
||||||
|
|
||||||
|
@@ -6,8 +6,8 @@ import dev.inmo.micro_utils.ktor.server.UnifiedRouter
|
|||||||
import dev.inmo.micro_utils.ktor.server.standardKtorSerialFormatContentType
|
import dev.inmo.micro_utils.ktor.server.standardKtorSerialFormatContentType
|
||||||
import dev.inmo.micro_utils.repos.StandardKeyValueRepo
|
import dev.inmo.micro_utils.repos.StandardKeyValueRepo
|
||||||
import io.ktor.http.ContentType
|
import io.ktor.http.ContentType
|
||||||
import io.ktor.routing.Route
|
import io.ktor.server.routing.Route
|
||||||
import io.ktor.routing.route
|
import io.ktor.server.routing.route
|
||||||
import kotlinx.serialization.KSerializer
|
import kotlinx.serialization.KSerializer
|
||||||
|
|
||||||
fun <K, V> Route.configureStandardKeyValueRepoRoutes(
|
fun <K, V> Route.configureStandardKeyValueRepoRoutes(
|
||||||
|
@@ -8,10 +8,10 @@ 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 dev.inmo.micro_utils.repos.ktor.common.valueParameterName
|
import dev.inmo.micro_utils.repos.ktor.common.valueParameterName
|
||||||
import io.ktor.application.call
|
|
||||||
import io.ktor.http.ContentType
|
import io.ktor.http.ContentType
|
||||||
import io.ktor.routing.Route
|
import io.ktor.server.application.call
|
||||||
import io.ktor.routing.get
|
import io.ktor.server.routing.Route
|
||||||
|
import io.ktor.server.routing.get
|
||||||
import kotlinx.serialization.KSerializer
|
import kotlinx.serialization.KSerializer
|
||||||
import kotlinx.serialization.builtins.serializer
|
import kotlinx.serialization.builtins.serializer
|
||||||
|
|
||||||
|
@@ -6,8 +6,8 @@ import dev.inmo.micro_utils.ktor.server.*
|
|||||||
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.http.ContentType
|
import io.ktor.http.ContentType
|
||||||
import io.ktor.routing.Route
|
import io.ktor.server.routing.Route
|
||||||
import io.ktor.routing.post
|
import io.ktor.server.routing.post
|
||||||
import kotlinx.serialization.KSerializer
|
import kotlinx.serialization.KSerializer
|
||||||
import kotlinx.serialization.builtins.*
|
import kotlinx.serialization.builtins.*
|
||||||
|
|
||||||
|
@@ -6,8 +6,8 @@ import dev.inmo.micro_utils.ktor.server.UnifiedRouter
|
|||||||
import dev.inmo.micro_utils.ktor.server.standardKtorSerialFormatContentType
|
import dev.inmo.micro_utils.ktor.server.standardKtorSerialFormatContentType
|
||||||
import dev.inmo.micro_utils.repos.OneToManyKeyValueRepo
|
import dev.inmo.micro_utils.repos.OneToManyKeyValueRepo
|
||||||
import io.ktor.http.ContentType
|
import io.ktor.http.ContentType
|
||||||
import io.ktor.routing.Route
|
import io.ktor.server.routing.Route
|
||||||
import io.ktor.routing.route
|
import io.ktor.server.routing.route
|
||||||
import kotlinx.serialization.KSerializer
|
import kotlinx.serialization.KSerializer
|
||||||
|
|
||||||
fun <Key, Value> Route.configureOneToManyKeyValueRepoRoutes(
|
fun <Key, Value> Route.configureOneToManyKeyValueRepoRoutes(
|
||||||
|
@@ -6,15 +6,14 @@ import dev.inmo.micro_utils.ktor.server.*
|
|||||||
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
|
||||||
import dev.inmo.micro_utils.repos.ktor.common.*
|
|
||||||
import dev.inmo.micro_utils.repos.ktor.common.keyParameterName
|
import dev.inmo.micro_utils.repos.ktor.common.keyParameterName
|
||||||
import dev.inmo.micro_utils.repos.ktor.common.one_to_many.*
|
import dev.inmo.micro_utils.repos.ktor.common.one_to_many.*
|
||||||
import dev.inmo.micro_utils.repos.ktor.common.valueParameterName
|
import dev.inmo.micro_utils.repos.ktor.common.valueParameterName
|
||||||
import dev.inmo.micro_utils.repos.ktor.common.reversedParameterName
|
import dev.inmo.micro_utils.repos.ktor.common.reversedParameterName
|
||||||
import io.ktor.application.call
|
|
||||||
import io.ktor.http.ContentType
|
import io.ktor.http.ContentType
|
||||||
import io.ktor.routing.Route
|
import io.ktor.server.application.call
|
||||||
import io.ktor.routing.get
|
import io.ktor.server.routing.Route
|
||||||
|
import io.ktor.server.routing.get
|
||||||
import kotlinx.serialization.KSerializer
|
import kotlinx.serialization.KSerializer
|
||||||
import kotlinx.serialization.builtins.serializer
|
import kotlinx.serialization.builtins.serializer
|
||||||
|
|
||||||
|
@@ -5,10 +5,9 @@ import dev.inmo.micro_utils.ktor.common.standardKtorSerialFormat
|
|||||||
import dev.inmo.micro_utils.ktor.server.*
|
import dev.inmo.micro_utils.ktor.server.*
|
||||||
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.application.call
|
|
||||||
import io.ktor.http.ContentType
|
import io.ktor.http.ContentType
|
||||||
import io.ktor.routing.Route
|
import io.ktor.server.routing.Route
|
||||||
import io.ktor.routing.post
|
import io.ktor.server.routing.post
|
||||||
import kotlinx.serialization.KSerializer
|
import kotlinx.serialization.KSerializer
|
||||||
import kotlinx.serialization.builtins.*
|
import kotlinx.serialization.builtins.*
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user