TextSourceSerializer

This commit is contained in:
InsanusMokrassar 2021-04-12 23:23:18 +06:00
parent 6aba2ff641
commit 0b361163f2
20 changed files with 181 additions and 0 deletions

View File

@ -2,6 +2,10 @@
## 0.33.4
* `Core`:
* All `TextSource` implementators have become `Serializable`
* New serializer `TextSourceSerializer`
## 0.33.3
* `Common`:

View File

@ -1,14 +1,17 @@
package dev.inmo.tgbotapi.CommonAbstracts
import dev.inmo.tgbotapi.types.MessageEntity.textsources.TextSourceSerializer
import dev.inmo.tgbotapi.types.MessageEntity.textsources.regular
import dev.inmo.tgbotapi.types.MessageEntity.toTextParts
import dev.inmo.tgbotapi.types.captionLength
import dev.inmo.tgbotapi.types.textLength
import kotlinx.serialization.Serializable
const val DirectInvocationOfTextSourceConstructor = "It is strongly not recommended to use constructors directly instead of factory methods"
typealias TextSourcesList = List<TextSource>
@Serializable(TextSourceSerializer::class)
interface TextSource {
val markdown: String
val markdownV2: String
@ -17,6 +20,10 @@ interface TextSource {
val asText: String
get() = source
companion object {
fun serializer() = TextSourceSerializer
}
}
@Suppress("NOTHING_TO_INLINE")
@ -28,8 +35,13 @@ inline operator fun TextSource.plus(text: String) = listOf(this, regular(text))
@Suppress("NOTHING_TO_INLINE")
inline operator fun List<TextSource>.plus(text: String) = this + regular(text)
@Serializable(TextSourceSerializer::class)
interface MultilevelTextSource : TextSource {
val subsources: List<TextSource>
companion object {
fun serializer() = TextSourceSerializer
}
}
data class TextPart(

View File

@ -3,10 +3,13 @@ package dev.inmo.tgbotapi.types.MessageEntity.textsources
import dev.inmo.tgbotapi.CommonAbstracts.*
import dev.inmo.tgbotapi.utils.RiskFeature
import dev.inmo.tgbotapi.utils.internal.*
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
/**
* @see bold
*/
@Serializable
data class BoldTextSource @RiskFeature(DirectInvocationOfTextSourceConstructor) constructor (
override val source: String,
override val subsources: List<TextSource>

View File

@ -4,12 +4,14 @@ import dev.inmo.tgbotapi.CommonAbstracts.DirectInvocationOfTextSourceConstructor
import dev.inmo.tgbotapi.CommonAbstracts.TextSource
import dev.inmo.tgbotapi.utils.RiskFeature
import dev.inmo.tgbotapi.utils.internal.*
import kotlinx.serialization.Serializable
private val commandRegex = Regex("[/!][^@\\s]*")
/**
* @see botCommand
*/
@Serializable
data class BotCommandTextSource @RiskFeature(DirectInvocationOfTextSourceConstructor) constructor (
override val source: String
) : TextSource {

View File

@ -3,10 +3,12 @@ package dev.inmo.tgbotapi.types.MessageEntity.textsources
import dev.inmo.tgbotapi.CommonAbstracts.*
import dev.inmo.tgbotapi.utils.RiskFeature
import dev.inmo.tgbotapi.utils.internal.*
import kotlinx.serialization.Serializable
/**
* @see cashTag
*/
@Serializable
data class CashTagTextSource @RiskFeature(DirectInvocationOfTextSourceConstructor) constructor (
override val source: String,
override val subsources: List<TextSource>

View File

@ -4,10 +4,12 @@ import dev.inmo.tgbotapi.CommonAbstracts.DirectInvocationOfTextSourceConstructor
import dev.inmo.tgbotapi.CommonAbstracts.TextSource
import dev.inmo.tgbotapi.utils.RiskFeature
import dev.inmo.tgbotapi.utils.internal.*
import kotlinx.serialization.Serializable
/**
* @see code
*/
@Serializable
data class CodeTextSource @RiskFeature(DirectInvocationOfTextSourceConstructor) constructor (
override val source: String
) : TextSource {

View File

@ -3,10 +3,12 @@ package dev.inmo.tgbotapi.types.MessageEntity.textsources
import dev.inmo.tgbotapi.CommonAbstracts.*
import dev.inmo.tgbotapi.utils.RiskFeature
import dev.inmo.tgbotapi.utils.internal.*
import kotlinx.serialization.Serializable
/**
* @see email
*/
@Serializable
data class EMailTextSource @RiskFeature(DirectInvocationOfTextSourceConstructor) constructor (
override val source: String,
override val subsources: List<TextSource>

View File

@ -3,10 +3,12 @@ package dev.inmo.tgbotapi.types.MessageEntity.textsources
import dev.inmo.tgbotapi.CommonAbstracts.*
import dev.inmo.tgbotapi.utils.RiskFeature
import dev.inmo.tgbotapi.utils.internal.*
import kotlinx.serialization.Serializable
/**
* @see hashtag
*/
@Serializable
data class HashTagTextSource @RiskFeature(DirectInvocationOfTextSourceConstructor) constructor (
override val source: String,
override val subsources: List<TextSource>

View File

@ -3,10 +3,12 @@ package dev.inmo.tgbotapi.types.MessageEntity.textsources
import dev.inmo.tgbotapi.CommonAbstracts.*
import dev.inmo.tgbotapi.utils.RiskFeature
import dev.inmo.tgbotapi.utils.internal.*
import kotlinx.serialization.Serializable
/**
* @see italic
*/
@Serializable
data class ItalicTextSource @RiskFeature(DirectInvocationOfTextSourceConstructor) constructor (
override val source: String,
override val subsources: List<TextSource>

View File

@ -3,6 +3,7 @@ package dev.inmo.tgbotapi.types.MessageEntity.textsources
import dev.inmo.tgbotapi.CommonAbstracts.*
import dev.inmo.tgbotapi.utils.RiskFeature
import dev.inmo.tgbotapi.utils.internal.*
import kotlinx.serialization.Serializable
private val String.withoutCommercialAt
get() = if (startsWith("@")) {
@ -14,6 +15,7 @@ private val String.withoutCommercialAt
/**
* @see mention
*/
@Serializable
data class MentionTextSource @RiskFeature(DirectInvocationOfTextSourceConstructor) constructor (
override val source: String,
override val subsources: List<TextSource>

View File

@ -3,10 +3,12 @@ package dev.inmo.tgbotapi.types.MessageEntity.textsources
import dev.inmo.tgbotapi.CommonAbstracts.*
import dev.inmo.tgbotapi.utils.RiskFeature
import dev.inmo.tgbotapi.utils.internal.*
import kotlinx.serialization.Serializable
/**
* @see phone
*/
@Serializable
data class PhoneNumberTextSource @RiskFeature(DirectInvocationOfTextSourceConstructor) constructor (
override val source: String,
override val subsources: List<TextSource>

View File

@ -4,10 +4,12 @@ import dev.inmo.tgbotapi.CommonAbstracts.DirectInvocationOfTextSourceConstructor
import dev.inmo.tgbotapi.CommonAbstracts.TextSource
import dev.inmo.tgbotapi.utils.RiskFeature
import dev.inmo.tgbotapi.utils.internal.*
import kotlinx.serialization.Serializable
/**
* @see pre
*/
@Serializable
data class PreTextSource @RiskFeature(DirectInvocationOfTextSourceConstructor) constructor (
override val source: String,
val language: String? = null

View File

@ -4,10 +4,12 @@ import dev.inmo.tgbotapi.CommonAbstracts.DirectInvocationOfTextSourceConstructor
import dev.inmo.tgbotapi.CommonAbstracts.TextSource
import dev.inmo.tgbotapi.utils.RiskFeature
import dev.inmo.tgbotapi.utils.internal.*
import kotlinx.serialization.Serializable
/**
* @see regular
*/
@Serializable
data class RegularTextSource @RiskFeature(DirectInvocationOfTextSourceConstructor) constructor (
override val source: String
) : TextSource {

View File

@ -3,10 +3,12 @@ package dev.inmo.tgbotapi.types.MessageEntity.textsources
import dev.inmo.tgbotapi.CommonAbstracts.*
import dev.inmo.tgbotapi.utils.RiskFeature
import dev.inmo.tgbotapi.utils.internal.*
import kotlinx.serialization.Serializable
/**
* @see strikethrough
*/
@Serializable
data class StrikethroughTextSource @RiskFeature(DirectInvocationOfTextSourceConstructor) constructor (
override val source: String,
override val subsources: List<TextSource>

View File

@ -4,10 +4,12 @@ import dev.inmo.tgbotapi.CommonAbstracts.DirectInvocationOfTextSourceConstructor
import dev.inmo.tgbotapi.CommonAbstracts.TextSource
import dev.inmo.tgbotapi.utils.RiskFeature
import dev.inmo.tgbotapi.utils.internal.*
import kotlinx.serialization.Serializable
/**
* @see link
*/
@Serializable
data class TextLinkTextSource @RiskFeature(DirectInvocationOfTextSourceConstructor) constructor (
override val source: String,
val url: String

View File

@ -4,10 +4,12 @@ import dev.inmo.tgbotapi.CommonAbstracts.*
import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.utils.RiskFeature
import dev.inmo.tgbotapi.utils.internal.*
import kotlinx.serialization.Serializable
/**
* @see mention
*/
@Serializable
data class TextMentionTextSource @RiskFeature(DirectInvocationOfTextSourceConstructor) constructor (
override val source: String,
val user: User,

View File

@ -0,0 +1,92 @@
package dev.inmo.tgbotapi.types.MessageEntity.textsources
import dev.inmo.tgbotapi.CommonAbstracts.TextSource
import dev.inmo.tgbotapi.CommonAbstracts.justTextSources
import dev.inmo.tgbotapi.types.MessageEntity.*
import dev.inmo.tgbotapi.types.MessageEntity.RawMessageEntities
import dev.inmo.tgbotapi.utils.RiskFeature
import kotlinx.serialization.*
import kotlinx.serialization.builtins.serializer
import kotlinx.serialization.descriptors.*
import kotlinx.serialization.encoding.*
private val baseSerializers: Map<String, KSerializer<out TextSource>> = mapOf(
"regular" to RegularTextSource.serializer(),
"text_link" to TextLinkTextSource.serializer(),
"code" to CodeTextSource.serializer(),
"url" to URLTextSource.serializer(),
"pre" to PreTextSource.serializer(),
"bot_command" to BotCommandTextSource.serializer(),
"strikethrough" to StrikethroughTextSource.serializer(),
"italic" to ItalicTextSource.serializer(),
"bold" to BoldTextSource.serializer(),
"email" to EMailTextSource.serializer(),
"underline" to UnderlineTextSource.serializer(),
"mention" to MentionTextSource.serializer(),
"phone_number" to PhoneNumberTextSource.serializer(),
"text_mention" to TextMentionTextSource.serializer(),
"hashtag" to HashTagTextSource.serializer(),
"cashtag" to CashTagTextSource.serializer(),
)
@Serializer(TextSource::class)
object TextSourceSerializer : KSerializer<TextSource> {
private val serializers = baseSerializers.toMutableMap()
@InternalSerializationApi
override val descriptor: SerialDescriptor = buildSerialDescriptor(
"TextSourceSerializer",
SerialKind.CONTEXTUAL
) {
element("type", String.serializer().descriptor)
element("value", ContextualSerializer(TextSource::class).descriptor)
}
@InternalSerializationApi
override fun deserialize(decoder: Decoder): TextSource {
return decoder.decodeStructure(descriptor) {
var type: String? = null
lateinit var result: TextSource
while (true) {
when (val index = decodeElementIndex(descriptor)) {
0 -> type = decodeStringElement(descriptor, 0)
1 -> {
require(type != null) { "Type is null, but it is expected that was inited already" }
result = decodeSerializableElement(
descriptor,
1,
serializers.getValue(type)
)
}
CompositeDecoder.DECODE_DONE -> break
else -> error("Unexpected index: $index")
}
}
result
}
}
@InternalSerializationApi
private fun <T : TextSource> CompositeEncoder.encode(value: T) {
encodeSerializableElement(descriptor, 1, value::class.serializer() as KSerializer<T>, value)
}
@InternalSerializationApi
override fun serialize(encoder: Encoder, value: TextSource) {
encoder.encodeStructure(descriptor) {
val valueSerializer = value::class.serializer()
val type = serializers.keys.first { serializers[it] == valueSerializer }
encodeStringElement(descriptor, 0, type)
encode(value)
}
}
fun <T: TextSource> include(type: String, serializer: KSerializer<T>) {
require(type !in baseSerializers.keys)
serializers[type] = serializer
}
fun exclude(type: String) {
require(type !in baseSerializers.keys)
serializers.remove(type)
}
}

View File

@ -4,10 +4,12 @@ import dev.inmo.tgbotapi.CommonAbstracts.DirectInvocationOfTextSourceConstructor
import dev.inmo.tgbotapi.CommonAbstracts.TextSource
import dev.inmo.tgbotapi.utils.RiskFeature
import dev.inmo.tgbotapi.utils.internal.*
import kotlinx.serialization.Serializable
/**
* @see link
*/
@Serializable
data class URLTextSource @RiskFeature(DirectInvocationOfTextSourceConstructor) constructor (
override val source: String
) : TextSource {

View File

@ -3,10 +3,12 @@ package dev.inmo.tgbotapi.types.MessageEntity.textsources
import dev.inmo.tgbotapi.CommonAbstracts.*
import dev.inmo.tgbotapi.utils.RiskFeature
import dev.inmo.tgbotapi.utils.internal.*
import kotlinx.serialization.Serializable
/**
* @see underline
*/
@Serializable
data class UnderlineTextSource @RiskFeature(DirectInvocationOfTextSourceConstructor) constructor (
override val source: String,
override val subsources: List<TextSource>

View File

@ -0,0 +1,40 @@
package dev.inmo.tgbotapi.types
import dev.inmo.tgbotapi.CommonAbstracts.TextSource
import dev.inmo.tgbotapi.CommonAbstracts.makeString
import dev.inmo.tgbotapi.TestsJsonFormat
import dev.inmo.tgbotapi.extensions.utils.formatting.*
import dev.inmo.tgbotapi.types.MessageEntity.textsources.TextSourceSerializer
import kotlinx.serialization.PolymorphicSerializer
import kotlinx.serialization.builtins.ListSerializer
import kotlinx.serialization.encodeToString
import kotlin.test.Test
import kotlin.test.assertEquals
class TextSourcesTests {
@Test
fun testThatTextSourcesSerializedCorrectly() {
val testList = buildEntities {
bold(
buildEntities {
italic("It")
regular(" ")
link("is example", "https://is.example")
}
)
regular(" ")
underline("of")
regular(" ")
strikethrough("complex")
regular(" ")
pre("text", "kotlin")
}
val serialized = TestsJsonFormat.encodeToString(ListSerializer(TextSource.serializer()), testList)
val deserialized = TestsJsonFormat.decodeFromString(
ListSerializer(TextSource.serializer()),
serialized
)
assertEquals(testList, deserialized)
assertEquals(testList.makeString(), deserialized.makeString())
}
}