1
0
mirror of https://github.com/InsanusMokrassar/TelegramBotAPI.git synced 2024-11-09 09:53:47 +00:00

add common customizable serializer

This commit is contained in:
InsanusMokrassar 2024-08-22 01:56:41 +06:00
parent d7e9c6a3a1
commit c03440c353
4 changed files with 197 additions and 48 deletions

View File

@ -7,6 +7,7 @@ import dev.inmo.tgbotapi.types.updateIdField
import dev.inmo.tgbotapi.utils.RiskFeature
import dev.inmo.tgbotapi.utils.decodeDataAndJson
import dev.inmo.tgbotapi.utils.nonstrictJsonFormat
import dev.inmo.tgbotapi.utils.serializers.CallbackCustomizableDeserializationStrategy
import kotlinx.serialization.*
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
@ -47,52 +48,21 @@ object UpdateSerializerWithoutSerialization : KSerializer<Update> {
* @see StringFormat.parse
* @see kotlinx.serialization.json.Json.parse
*/
object UpdateDeserializationStrategy : DeserializationStrategy<Update> {
private val _customDeserializationStrategies = LinkedHashSet<JsonDeserializerStrategy>()
/**
* Contains [JsonDeserializerStrategy] which will be used in [deserialize] method when standard
* [RawUpdate] serializer will be unable to create [RawUpdate] (and [Update] as well)
*/
val customDeserializationStrategies: Set<JsonDeserializerStrategy>
get() = _customDeserializationStrategies.toSet()
fun interface JsonDeserializerStrategy {
fun deserializeOrNull(json: JsonElement): Update?
}
override val descriptor: SerialDescriptor = JsonElement.serializer().descriptor
override fun deserialize(decoder: Decoder): Update {
val asJson = JsonElement.serializer().deserialize(decoder)
return runCatching {
object UpdateDeserializationStrategy : CallbackCustomizableDeserializationStrategy<Update>(
JsonElement.serializer().descriptor,
{ _, jsonElement ->
nonstrictJsonFormat.decodeFromJsonElement(
RawUpdate.serializer(),
asJson
jsonElement!!
).asUpdate(
asJson
jsonElement
)
}.getOrElse {
customDeserializationStrategies.firstNotNullOfOrNull {
it.deserializeOrNull(asJson)
} ?: UnknownUpdate(
UpdateId((asJson as? JsonObject) ?.get(updateIdField) ?.jsonPrimitive ?.longOrNull ?: -1L),
asJson,
},
{ it, _, jsonElement ->
UnknownUpdate(
UpdateId((jsonElement as? JsonObject) ?.get(updateIdField) ?.jsonPrimitive ?.longOrNull ?: -1L),
jsonElement!!,
it
)
}
}
/**
* Adding [deserializationStrategy] into [customDeserializationStrategies] for using in case of unknown update
*/
fun addUpdateDeserializationStrategy(
deserializationStrategy: JsonDeserializerStrategy
) = _customDeserializationStrategies.add(deserializationStrategy)
/**
* Removing [deserializationStrategy] from [customDeserializationStrategies]
*/
fun removeUpdateDeserializationStrategy(
deserializationStrategy: JsonDeserializerStrategy
) = _customDeserializationStrategies.remove(deserializationStrategy)
}
)

View File

@ -0,0 +1,78 @@
package dev.inmo.tgbotapi.utils.serializers
import dev.inmo.tgbotapi.types.update.RawUpdate
import dev.inmo.tgbotapi.types.update.abstracts.Update
import dev.inmo.tgbotapi.types.update.abstracts.UpdateDeserializationStrategy.deserialize
import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.json.JsonDecoder
import kotlinx.serialization.json.JsonElement
interface CustomizableDeserializationStrategy<T> : DeserializationStrategy<T> {
fun interface JsonDeserializerStrategy<T> {
fun deserializeOrNull(json: JsonElement): T?
}
/**
* Contains [JsonDeserializerStrategy] which will be used in [deserialize] method when standard
* [RawUpdate] serializer will be unable to create [RawUpdate] (and [Update] as well)
*/
val customDeserializationStrategies: Set<JsonDeserializerStrategy<T>>
/**
* Adding [deserializationStrategy] into [customDeserializationStrategies] for using in case of unknown update
*/
fun addUpdateDeserializationStrategy(
deserializationStrategy: JsonDeserializerStrategy<T>
): Boolean
/**
* Removing [deserializationStrategy] from [customDeserializationStrategies]
*/
fun removeUpdateDeserializationStrategy(
deserializationStrategy: JsonDeserializerStrategy<T>
): Boolean
}
open class CallbackCustomizableDeserializationStrategy<T>(
override val descriptor: SerialDescriptor,
private val defaultDeserializeCallback: (decoder: Decoder, jsonElement: JsonElement?) -> T,
private val fallbackDeserialization: (initialException: Throwable, decoder: Decoder, jsonElement: JsonElement?) -> T = { initialException, _, _ -> throw initialException }
) : CustomizableDeserializationStrategy<T> {
protected val _customDeserializationStrategies = LinkedHashSet<CustomizableDeserializationStrategy.JsonDeserializerStrategy<T>>()
/**
* Contains [JsonDeserializerStrategy] which will be used in [deserialize] method when standard
* [RawUpdate] serializer will be unable to create [RawUpdate] (and [Update] as well)
*/
override val customDeserializationStrategies: Set<CustomizableDeserializationStrategy.JsonDeserializerStrategy<T>>
get() = _customDeserializationStrategies.toSet()
override fun deserialize(decoder: Decoder): T {
val jsonDecoder = decoder as? JsonDecoder
val jsonElement = jsonDecoder ?.decodeJsonElement()
return runCatching {
defaultDeserializeCallback(decoder, jsonElement)
}.onFailure {
return (jsonElement ?.let {
customDeserializationStrategies.firstNotNullOfOrNull {
it.deserializeOrNull(jsonElement)
}
}) ?: fallbackDeserialization(it, decoder, jsonElement)
}.getOrThrow()
}
/**
* Adding [deserializationStrategy] into [customDeserializationStrategies] for using in case of unknown update
*/
override fun addUpdateDeserializationStrategy(
deserializationStrategy: CustomizableDeserializationStrategy.JsonDeserializerStrategy<T>
): Boolean = _customDeserializationStrategies.add(deserializationStrategy)
/**
* Removing [deserializationStrategy] from [customDeserializationStrategies]
*/
override fun removeUpdateDeserializationStrategy(
deserializationStrategy: CustomizableDeserializationStrategy.JsonDeserializerStrategy<T>
): Boolean = _customDeserializationStrategies.remove(deserializationStrategy)
}

View File

@ -0,0 +1,74 @@
package dev.inmo.tgbotapi.utils.serializers
import dev.inmo.tgbotapi.types.update.RawUpdate
import dev.inmo.tgbotapi.types.update.abstracts.Update
import dev.inmo.tgbotapi.types.update.abstracts.UpdateDeserializationStrategy.deserialize
import kotlinx.serialization.SerializationStrategy
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.json.JsonElement
interface CustomizableSerializationStrategy<T> : SerializationStrategy<T> {
fun interface CustomSerializerStrategy<T> {
fun optionallySerialize(encoder: Encoder, value: T): Boolean
}
/**
* Contains [CustomSerializerStrategy] which will be used in [Serialize] method when standard
* [RawUpdate] serializer will be unable to create [RawUpdate] (and [Update] as well)
*/
val customSerializationStrategies: Set<CustomSerializerStrategy<T>>
/**
* Adding [deserializationStrategy] into [customSerializationStrategies] for using in case of unknown update
*/
fun addUpdateSerializationStrategy(
deserializationStrategy: CustomSerializerStrategy<T>
): Boolean
/**
* Removing [deserializationStrategy] from [customSerializationStrategies]
*/
fun removeUpdateSerializationStrategy(
deserializationStrategy: CustomSerializerStrategy<T>
): Boolean
}
open class CallbackCustomizableSerializationStrategy<T>(
override val descriptor: SerialDescriptor,
private val defaultSerializeCallback: (encoder: Encoder, value: T) -> Unit,
private val fallbackSerialization: (initialException: Throwable, encoder: Encoder, value: T) -> T = { initialException, _, _ -> throw initialException }
) : CustomizableSerializationStrategy<T> {
protected val _customSerializationStrategies = LinkedHashSet<CustomizableSerializationStrategy.CustomSerializerStrategy<T>>()
/**
* Contains [JsonSerializerStrategy] which will be used in [deserialize] method when standard
* [RawUpdate] serializer will be unable to create [RawUpdate] (and [Update] as well)
*/
override val customSerializationStrategies: Set<CustomizableSerializationStrategy.CustomSerializerStrategy<T>>
get() = _customSerializationStrategies.toSet()
override fun serialize(encoder: Encoder, value: T) {
runCatching {
defaultSerializeCallback(encoder, value)
}.onFailure {
customSerializationStrategies.firstOrNull() {
it.optionallySerialize(encoder, value)
} ?: fallbackSerialization(it, encoder, value)
}
}
/**
* Adding [deserializationStrategy] into [customSerializationStrategies] for using in case of unknown update
*/
override fun addUpdateSerializationStrategy(
deserializationStrategy: CustomizableSerializationStrategy.CustomSerializerStrategy<T>
): Boolean = _customSerializationStrategies.add(deserializationStrategy)
/**
* Removing [deserializationStrategy] from [customSerializationStrategies]
*/
override fun removeUpdateSerializationStrategy(
deserializationStrategy: CustomizableSerializationStrategy.CustomSerializerStrategy<T>
): Boolean = _customSerializationStrategies.remove(deserializationStrategy)
}

View File

@ -0,0 +1,27 @@
package dev.inmo.tgbotapi.utils.serializers
import dev.inmo.tgbotapi.types.update.RawUpdate
import dev.inmo.tgbotapi.types.update.abstracts.Update
import dev.inmo.tgbotapi.types.update.abstracts.UpdateDeserializationStrategy.deserialize
import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializer
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.json.JsonDecoder
import kotlinx.serialization.json.JsonElement
interface CustomizableSerializer<T> : KSerializer<T>, CustomizableSerializationStrategy<T>, CustomizableDeserializationStrategy<T> {
}
open class CallbacksCustomizableDeserializationStrategy<T>(
override val descriptor: SerialDescriptor,
defaultDeserializeCallback: (decoder: Decoder, jsonElement: JsonElement?) -> T,
defaultSerializeCallback: (encoder: Encoder, value: T) -> Unit,
fallbackDeserialization: (initialException: Throwable, decoder: Decoder, jsonElement: JsonElement?) -> T = { initialException, _, _ -> throw initialException },
fallbackSerialization: (initialException: Throwable, encoder: Encoder, value: T) -> T = { initialException, _, _ -> throw initialException }
) : CustomizableSerializer<T>,
CustomizableSerializationStrategy<T> by CallbackCustomizableSerializationStrategy(descriptor, defaultSerializeCallback, fallbackSerialization),
CustomizableDeserializationStrategy<T> by CallbackCustomizableDeserializationStrategy(descriptor, defaultDeserializeCallback, fallbackDeserialization){
}