Compare commits

..

21 Commits

Author SHA1 Message Date
c9e320b72a update compose version 2023-03-09 23:22:33 +06:00
555956087d add android manifest to mapper module 2023-03-09 22:23:56 +06:00
b3f468f901 add docs to mapper serialization and allow to use them as supertypes 2023-03-09 22:21:22 +06:00
f5f7511781 add mapper serializer 2023-03-09 21:55:07 +06:00
4be1d93f60 start 0.17.4 2023-03-09 21:49:15 +06:00
7d684608ef Merge pull request #228 from InsanusMokrassar/0.17.3
0.17.3
2023-03-07 23:30:23 +06:00
2c7fd320eb fixed 2023-03-07 22:28:56 +06:00
88ee82e1c6 add Diff#isEmpty 2023-03-07 21:50:23 +06:00
d6402c624e optimization of nonstrict comparison 2023-03-07 19:14:43 +06:00
8b9c93bc10 diffs improvement 2023-03-07 19:12:12 +06:00
4f5e261d01 start 0.17.3 2023-03-07 18:59:34 +06:00
cf455aebe6 Merge pull request #227 from InsanusMokrassar/0.17.2
0.17.2
2023-03-02 21:57:49 +06:00
1380d5f8e1 fill changelog 2023-03-02 21:38:20 +06:00
5f65260bfe Update DefaultStatesManager.kt 2023-03-02 21:35:59 +06:00
11f0dcfc01 Update DefaultStatesManager.kt 2023-03-02 21:32:58 +06:00
555b7886a4 Update gradle.properties 2023-03-02 21:28:50 +06:00
3707a6c0ce Merge pull request #226 from InsanusMokrassar/0.17.1
0.17.1
2023-02-28 19:36:08 +06:00
4c8d92b4c3 update ktor 2023-02-28 19:32:32 +06:00
8bbd33c896 now all android modules depends on jvm 2023-02-28 19:08:39 +06:00
ac33a3580f start 0.17.1 2023-02-28 19:04:04 +06:00
a64a32fbe6 Merge pull request #225 from InsanusMokrassar/0.17.0
0.17.0
2023-02-28 12:15:41 +06:00
17 changed files with 305 additions and 15 deletions

View File

@@ -1,5 +1,31 @@
# Changelog
## 0.17.4
* `Serialization`:
* `Mapper`:
* Module inited
* `Versions`:
* `Compose`: `1.3.1-rc02` -> `1.3.1`
## 0.17.3
* `Common`:
* Add `fixed` extensions for `Float` and `Double`
* New function `emptyDiff`
* Now you may pass custom `comparisonFun` to all `diff` functions
## 0.17.2
* `FSM`:
* `DefaultStatesManager.onUpdateContextsConflictResolver` and `DefaultStatesManager.onStartContextsConflictResolver` now return `false` by default
## 0.17.1
* **Hotfix** for absence of jvm dependencies in android modules
* `Versions`:
* `Ktor`: `2.2.3` -> `2.2.4`
## 0.17.0
* `Versions`:

View File

@@ -34,7 +34,11 @@ data class Diff<T> internal constructor(
*/
val replaced: List<Pair<@Serializable(IndexedValueSerializer::class) IndexedValue<T>, @Serializable(IndexedValueSerializer::class) IndexedValue<T>>>,
val added: List<@Serializable(IndexedValueSerializer::class) IndexedValue<T>>
)
) {
fun isEmpty(): Boolean = removed.isEmpty() && replaced.isEmpty() && added.isEmpty()
}
fun <T> emptyDiff(): Diff<T> = Diff(emptyList(), emptyList(), emptyList())
private inline fun <T> performChanges(
potentialChanges: MutableList<Pair<IndexedValue<T>?, IndexedValue<T>?>>,
@@ -43,14 +47,14 @@ private inline fun <T> performChanges(
changedList: MutableList<Pair<IndexedValue<T>, IndexedValue<T>>>,
removedList: MutableList<IndexedValue<T>>,
addedList: MutableList<IndexedValue<T>>,
strictComparison: Boolean
comparisonFun: (T?, T?) -> 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)
val oldOneEqualToNewObject = comparisonFun(old ?.value, newObject ?.value)
val newOneEqualToOldObject = comparisonFun(new ?.value, oldObject ?.value)
if (oldOneEqualToNewObject || newOneEqualToOldObject) {
changedList.addAll(
potentialChanges.take(i).mapNotNull {
@@ -104,7 +108,7 @@ private inline fun <T> performChanges(
*/
fun <T> Iterable<T>.calculateDiff(
other: Iterable<T>,
strictComparison: Boolean = false
comparisonFun: (T?, T?) -> Boolean
): Diff<T> {
var i = -1
var j = -1
@@ -132,7 +136,7 @@ fun <T> Iterable<T>.calculateDiff(
}
when {
oldObject === newObject || (oldObject == newObject && !strictComparison) -> {
comparisonFun(oldObject, newObject) -> {
changedObjects.addAll(potentiallyChangedObjects.map {
@Suppress("UNCHECKED_CAST")
it as Pair<IndexedValue<T>, IndexedValue<T>>
@@ -143,23 +147,49 @@ fun <T> Iterable<T>.calculateDiff(
potentiallyChangedObjects.add(oldObject ?.let { IndexedValue(i, oldObject) } to newObject ?.let { IndexedValue(j, newObject) })
val previousOldsAdditionsSize = additionalInOld.size
val previousNewsAdditionsSize = additionalInNew.size
performChanges(potentiallyChangedObjects, additionalInOld, additionalInNew, changedObjects, removedObjects, addedObjects, strictComparison)
performChanges(potentiallyChangedObjects, additionalInOld, additionalInNew, changedObjects, removedObjects, addedObjects, comparisonFun)
i -= (additionalInOld.size - previousOldsAdditionsSize)
j -= (additionalInNew.size - previousNewsAdditionsSize)
}
}
}
potentiallyChangedObjects.add(null to null)
performChanges(potentiallyChangedObjects, additionalInOld, additionalInNew, changedObjects, removedObjects, addedObjects, strictComparison)
performChanges(potentiallyChangedObjects, additionalInOld, additionalInNew, changedObjects, removedObjects, addedObjects, comparisonFun)
return Diff(removedObjects.toList(), changedObjects.toList(), addedObjects.toList())
}
/**
* Calculating [Diff] object
*
* @param strictComparison If this parameter set to true, objects which are not equal by links will be used as different
* objects. For example, in case of two "Example" string they will be equal by value, but CAN be different by links
*/
fun <T> Iterable<T>.calculateDiff(
other: Iterable<T>,
strictComparison: Boolean = false
): Diff<T> = calculateDiff(
other,
comparisonFun = if (strictComparison) {
{ t1, t2 ->
t1 === t2
}
} else {
{ t1, t2 ->
t1 === t2 || t1 == t2 // small optimization for cases when t1 and t2 are the same - comparison will be faster potentially
}
}
)
inline fun <T> Iterable<T>.diff(
other: Iterable<T>,
strictComparison: Boolean = false
): Diff<T> = calculateDiff(other, strictComparison)
inline fun <T> Iterable<T>.diff(
other: Iterable<T>,
noinline comparisonFun: (T?, T?) -> Boolean
): Diff<T> = calculateDiff(other, comparisonFun)
inline fun <T> Diff(old: Iterable<T>, new: Iterable<T>) = old.calculateDiff(new)
inline fun <T> Diff(old: Iterable<T>, new: Iterable<T>) = old.calculateDiff(new, strictComparison = false)
inline fun <T> StrictDiff(old: Iterable<T>, new: Iterable<T>) = old.calculateDiff(new, true)
/**
@@ -187,3 +217,22 @@ fun <T> MutableList<T>.applyDiff(
set(new.index, new.value)
}
}
/**
* This method call [calculateDiff] with strict mode [strictComparison] and then apply differences to [this]
* mutable list
*/
fun <T> MutableList<T>.applyDiff(
source: Iterable<T>,
comparisonFun: (T?, T?) -> Boolean
): Diff<T> = calculateDiff(source, comparisonFun).also {
for (i in it.removed.indices.sortedDescending()) {
removeAt(it.removed[i].index)
}
it.added.forEach { (i, t) ->
add(i, t)
}
it.replaced.forEach { (_, new) ->
set(new.index, new.value)
}
}

View File

@@ -0,0 +1,6 @@
package dev.inmo.micro_utils.common
val FixedSignsRange = 0 .. 100
expect fun Float.fixed(signs: Int): Float
expect fun Double.fixed(signs: Int): Double

View File

@@ -0,0 +1,4 @@
package dev.inmo.micro_utils.common
actual fun Float.fixed(signs: Int): Float = this.asDynamic().toFixed(signs.coerceIn(FixedSignsRange)).unsafeCast<String>().toFloat()
actual fun Double.fixed(signs: Int): Double = this.asDynamic().toFixed(signs.coerceIn(FixedSignsRange)).unsafeCast<String>().toDouble()

View File

@@ -0,0 +1,12 @@
package dev.inmo.micro_utils.common
import java.math.BigDecimal
import java.math.RoundingMode
actual fun Float.fixed(signs: Int): Float = BigDecimal.valueOf(this.toDouble())
.setScale(signs.coerceIn(FixedSignsRange), RoundingMode.HALF_UP)
.toFloat();
actual fun Double.fixed(signs: Int): Double = BigDecimal.valueOf(this)
.setScale(signs.coerceIn(FixedSignsRange), RoundingMode.HALF_UP)
.toDouble();

View File

@@ -48,8 +48,8 @@ interface DefaultStatesManagerRepo<T : State> {
*/
open class DefaultStatesManager<T : State>(
protected val repo: DefaultStatesManagerRepo<T> = InMemoryDefaultStatesManagerRepo(),
protected val onStartContextsConflictResolver: suspend (current: T, new: T) -> Boolean = { _, _ -> true },
protected val onUpdateContextsConflictResolver: suspend (old: T, new: T, currentNew: T) -> Boolean = { _, _, _ -> true }
protected val onStartContextsConflictResolver: suspend (current: T, new: T) -> Boolean = { _, _ -> false },
protected val onUpdateContextsConflictResolver: suspend (old: T, new: T, currentNew: T) -> Boolean = { _, _, _ -> false }
) : StatesManager<T> {
protected val _onChainStateUpdated = MutableSharedFlow<Pair<T, T>>(0)
override val onChainStateUpdated: Flow<Pair<T, T>> = _onChainStateUpdated.asSharedFlow()

View File

@@ -14,5 +14,5 @@ crypto_js_version=4.1.1
# Project data
group=dev.inmo
version=0.17.0
android_code_version=182
version=0.17.4
android_code_version=186

View File

@@ -6,14 +6,14 @@ kt-coroutines = "1.6.4"
kslog = "1.0.0"
jb-compose = "1.3.1-rc01"
jb-compose = "1.3.1"
jb-exposed = "0.41.1"
jb-dokka = "1.7.20"
klock = "3.4.0"
uuid = "0.7.0"
ktor = "2.2.3"
ktor = "2.2.4"
gh-release = "2.4.1"

View File

@@ -50,6 +50,8 @@ kotlin {
implementation libs.android.espresso
}
}
androidMain.dependsOn jvmMain
}
}

View File

@@ -61,6 +61,8 @@ kotlin {
implementation libs.android.espresso
}
}
androidMain.dependsOn jvmMain
}
}

View File

@@ -0,0 +1,7 @@
plugins {
id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.kotlin.plugin.serialization"
id "com.android.library"
}
apply from: "$mppProjectWithSerializationPresetPath"

View File

@@ -0,0 +1,99 @@
package dev.inmo.micro_utils.serialization.mapper
import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.InternalSerializationApi
import kotlinx.serialization.KSerializer
import kotlinx.serialization.SerializationStrategy
import kotlinx.serialization.serializer
import kotlin.reflect.KClass
/**
* Will create [MapperSerializationStrategy] to allow you to map [O] to [I] using [serialize] lambda during
* serialization process
*/
inline fun <reified I : Any, O> SerializationStrategy<I>.mapSerialization(
noinline serialize: (O) -> I
) = MapperSerializationStrategy(
this,
serialize
)
/**
* Will create [MapperDeserializationStrategy] to allow you to map [I] to [O] using [deserialize] lambda during
* deserialization process
*/
inline fun <reified I : Any, O> DeserializationStrategy<I>.mapDeserialization(
noinline deserialize: (I) -> O
) = MapperDeserializationStrategy(
this,
deserialize
)
/**
* Will create [MapperSerializer] to allow you to map [O] to [I] and vice verse using [serialize]/[deserialize] lambda during
* serialization/deserialization process
*/
inline fun <reified I : Any, O> KSerializer<I>.mapFullSerialization(
noinline serialize: (O) -> I,
noinline deserialize: (I) -> O
) = MapperSerializer(
this,
serialize,
deserialize
)
/**
* Will create [MapperSerializationStrategy] to allow you to map [O] to [I] using [serialize] lambda during
* serialization process
*/
@OptIn(InternalSerializationApi::class)
inline fun <reified I : Any, O> KClass<I>.mapSerialization(
serializer: SerializationStrategy<I> = serializer(),
noinline serialize: (O) -> I
) = serializer.mapSerialization(serialize)
/**
* Will create [MapperDeserializationStrategy] to allow you to map [I] to [O] using [deserialize] lambda during
* deserialization process
*/
@OptIn(InternalSerializationApi::class)
inline fun <reified I : Any, O> KClass<I>.mapDeserialization(
serializer: DeserializationStrategy<I> = serializer(),
noinline deserialize: (I) -> O
) = serializer.mapDeserialization(deserialize)
/**
* Will create [MapperSerializer] to allow you to map [O] to [I] and vice verse using [serialize]/[deserialize] lambda during
* serialization/deserialization process
*/
@OptIn(InternalSerializationApi::class)
inline fun <reified I : Any, O> KClass<I>.mapFullSerialization(
serializer: KSerializer<I> = serializer(),
noinline serialize: (O) -> I,
noinline deserialize: (I) -> O
) = serializer.mapFullSerialization(serialize, deserialize)
/**
* Will create [MapperSerializationStrategy] to allow you to map [O] to [I] using [serialize] lambda during
* serialization process
*/
inline fun <reified I : Any, O> mappedSerializationStrategy(
noinline serialize: (O) -> I,
) = serializer<I>().mapSerialization(serialize)
/**
* Will create [MapperDeserializationStrategy] to allow you to map [I] to [O] using [deserialize] lambda during
* deserialization process
*/
inline fun <reified I : Any, O> mappedDeserializationStrategy(
noinline deserialize: (I) -> O
) = serializer<I>().mapDeserialization(deserialize)
/**
* Will create [MapperSerializer] to allow you to map [O] to [I] and vice verse using [serialize]/[deserialize] lambda during
* serialization/deserialization process
*/
inline fun <reified I : Any, O> mappedSerializer(
noinline serialize: (O) -> I,
noinline deserialize: (I) -> O
) = serializer<I>().mapFullSerialization(serialize, deserialize)

View File

@@ -0,0 +1,26 @@
package dev.inmo.micro_utils.serialization.mapper
import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.KSerializer
import kotlinx.serialization.SerializationStrategy
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
/**
* Use this serializer when you have deserializable type [I] and want to map it to some [O] in process of
* deserialization
*
* @param base Serializer for [I]
* @param deserialize Will be used in [deserialize] method to convert deserialized by [base] [I] to [O]
*/
open class MapperDeserializationStrategy<I, O>(
private val base: DeserializationStrategy<I>,
private val deserialize: (I) -> O
) : DeserializationStrategy<O> {
override val descriptor: SerialDescriptor = base.descriptor
override fun deserialize(decoder: Decoder): O {
return deserialize(base.deserialize(decoder))
}
}

View File

@@ -0,0 +1,25 @@
package dev.inmo.micro_utils.serialization.mapper
import kotlinx.serialization.KSerializer
import kotlinx.serialization.SerializationStrategy
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
/**
* Use this serializer when you have serializable type [I] and want to map it to some [O] in process of
* serialization
*
* @param base Serializer for [I]
* @param serialize Will be used in [serialize] method to convert incoming [O] to [I] and serialize with [base]
*/
open class MapperSerializationStrategy<I, O>(
private val base: SerializationStrategy<I>,
private val serialize: (O) -> I
) : SerializationStrategy<O> {
override val descriptor: SerialDescriptor = base.descriptor
override fun serialize(encoder: Encoder, value: O) {
base.serialize(encoder, serialize(value))
}
}

View File

@@ -0,0 +1,30 @@
package dev.inmo.micro_utils.serialization.mapper
import kotlinx.serialization.KSerializer
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
/**
* Use this serializer when you have serializable type [I] and want to map it to some [O] in process of
* serialization/deserialization
*
* @param base Serializer for [I]
* @param serialize Will be used in [serialize] method to convert incoming [O] to [I] and serialize with [base]
* @param deserialize Will be used in [deserialize] method to convert deserialized by [base] [I] to [O]
*/
open class MapperSerializer<I, O>(
private val base: KSerializer<I>,
private val serialize: (O) -> I,
private val deserialize: (I) -> O
) : KSerializer<O> {
override val descriptor: SerialDescriptor = base.descriptor
override fun deserialize(decoder: Decoder): O {
return deserialize(base.deserialize(decoder))
}
override fun serialize(encoder: Encoder, value: O) {
base.serialize(encoder, serialize(value))
}
}

View File

@@ -0,0 +1 @@
<manifest package="dev.inmo.micro_utils.serialization.mapper"/>

View File

@@ -37,6 +37,7 @@ String[] includes = [
":serialization:base64",
":serialization:encapsulator",
":serialization:typed_serializer",
":serialization:mapper",
":startup:plugin",
":startup:launcher",