diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 993feccf5e0..2fe2e4d7cc7 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -37,6 +37,12 @@ kotlin { implementation(projects.kspAnnotations) } } + jvmTest { + dependencies { + implementation(libs.bson) + implementation(libs.kbson) + } + } } } diff --git a/common/src/commonMain/kotlin/serialization/LongOrStringSerializer.kt b/common/src/commonMain/kotlin/serialization/LongOrStringSerializer.kt index 604503d35c9..22d974256f2 100644 --- a/common/src/commonMain/kotlin/serialization/LongOrStringSerializer.kt +++ b/common/src/commonMain/kotlin/serialization/LongOrStringSerializer.kt @@ -5,18 +5,18 @@ import kotlinx.serialization.descriptors.PrimitiveKind import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder +import kotlinx.serialization.json.JsonDecoder +import kotlinx.serialization.json.JsonEncoder import kotlinx.serialization.json.JsonPrimitive import kotlinx.serialization.json.long internal object LongOrStringSerializer : KSerializer { - private val backingSerializer = JsonPrimitive.serializer() - /* * Delegating serializers should not reuse descriptors: * https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/serializers.md#delegating-serializers * - * however `SerialDescriptor("...", backingSerializer.descriptor)` will throw since - * `JsonPrimitive.serializer().kind` is `PrimitiveKind.STRING` (`SerialDescriptor()` does not allow + * however `SerialDescriptor("...", JsonPrimitive.serializer().descriptor)` will throw since + * `JsonPrimitive.serializer().descriptor.kind` is `PrimitiveKind.STRING` (`SerialDescriptor()` does not allow * `PrimitiveKind`) * -> use `PrimitiveSerialDescriptor("...", PrimitiveKind.STRING)` instead */ @@ -26,12 +26,21 @@ internal object LongOrStringSerializer : KSerializer { ) override fun serialize(encoder: Encoder, value: String) { - val jsonPrimitive = value.toLongOrNull()?.let { JsonPrimitive(it) } ?: JsonPrimitive(value) - encoder.encodeSerializableValue(backingSerializer, jsonPrimitive) + if (encoder is JsonEncoder) { + val jsonPrimitive = value.toLongOrNull()?.let { JsonPrimitive(it) } ?: JsonPrimitive(value) + encoder.encodeJsonElement(jsonPrimitive) + } else { + // fall back to a String for non-Json formats + encoder.encodeString(value) + } } - override fun deserialize(decoder: Decoder): String { - val jsonPrimitive = decoder.decodeSerializableValue(backingSerializer) - return if (jsonPrimitive.isString) jsonPrimitive.content else jsonPrimitive.long.toString() - } + override fun deserialize(decoder: Decoder): String = + if (decoder is JsonDecoder) { + val jsonPrimitive = decoder.decodeSerializableValue(JsonPrimitive.serializer()) + if (jsonPrimitive.isString) jsonPrimitive.content else jsonPrimitive.long.toString() + } else { + // fall back to a String for non-Json formats + decoder.decodeString() + } } diff --git a/common/src/jvmTest/kotlin/serialization/LongOrStringSerializerBsonTest.kt b/common/src/jvmTest/kotlin/serialization/LongOrStringSerializerBsonTest.kt new file mode 100644 index 00000000000..8de11b28628 --- /dev/null +++ b/common/src/jvmTest/kotlin/serialization/LongOrStringSerializerBsonTest.kt @@ -0,0 +1,38 @@ +package dev.kord.common.serialization + +import com.github.jershell.kbson.KBson +import dev.kord.common.entity.Permission.* +import dev.kord.common.entity.Permissions +import kotlinx.serialization.Serializable +import kotlin.random.Random +import kotlin.test.Test +import kotlin.test.assertEquals + +class LongOrStringSerializerBsonTest { + + @Serializable + private data class SomeObject( + @Serializable(with = LongOrStringSerializer::class) val someLong: String, + @Serializable(with = LongOrStringSerializer::class) val someString: String, + val somePermissions: Permissions, + ) + + private val someObject = SomeObject( + someLong = Random.nextLong().toString(), + someString = "some totally random string", + somePermissions = Permissions(DeafenMembers, ManageThreads, SendTTSMessages, ModerateMembers), + ) + + @Test + fun `test Bson serialization and deserialization with LongOrStringSerializer`() { + val kBson = KBson.default + assertEquals( + expected = someObject, + actual = kBson.load(SomeObject.serializer(), kBson.stringify(SomeObject.serializer(), someObject)), + ) + assertEquals( + expected = someObject, + actual = kBson.load(SomeObject.serializer(), kBson.dump(SomeObject.serializer(), someObject)), + ) + } +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 385924d0269..abc5b0e2206 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -24,6 +24,8 @@ kotlinpoet = "1.16.0" # https://github.com/square/kotlinpoet # tests junit5 = "5.10.2" # https://github.com/junit-team/junit5 mockk = "1.13.10" # https://github.com/mockk/mockk +bson = "5.0.1" # https://github.com/mongodb/mongo-java-driver +kbson = "0.5.0" # https://github.com/jershell/kbson # plugins dokka = "1.9.20" # https://github.com/Kotlin/dokka @@ -78,6 +80,8 @@ kotlin-test-junit5 = { module = "org.jetbrains.kotlin:kotlin-test-junit5", versi junit-jupiter-engine = { module = "org.junit.jupiter:junit-jupiter-engine", version.ref = "junit5" } mockk = { module = "io.mockk:mockk", version.ref = "mockk" } slf4j-simple = { module = "org.slf4j:slf4j-simple", version.ref = "slf4j" } +bson = { module = "org.mongodb:bson", version.ref = "bson" } +kbson = { module = "com.github.jershell:kbson", version.ref = "kbson" } # actually plugins, not libraries, but used is 'buildSrc/build.gradle.kts' as implementation dependencies: # https://docs.gradle.org/current/userguide/custom_plugins.html#applying_external_plugins_in_precompiled_script_plugins