diff --git a/bson/build.gradle.kts b/bson/build.gradle.kts index 810a16e2..1081a0df 100644 --- a/bson/build.gradle.kts +++ b/bson/build.gradle.kts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024-2025, OpenSavvy and contributors. + * Copyright (c) 2024-2026, OpenSavvy and contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -65,6 +65,10 @@ kotlin { implementation(libsCommon.opensavvy.prepared.testBalloon) implementation(libsCommon.kotlin.test) } + + sourceSets.jvmMain.dependencies { + compileOnly(libs.mongodb.kotlinx.serialization) + } } library { diff --git a/bson/src/commonMain/kotlin/types/ObjectId.kt b/bson/src/commonMain/kotlin/types/ObjectId.kt index 4e43de7d..cc107efc 100644 --- a/bson/src/commonMain/kotlin/types/ObjectId.kt +++ b/bson/src/commonMain/kotlin/types/ObjectId.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, OpenSavvy and contributors. + * Copyright (c) 2025-2026, OpenSavvy and contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -285,14 +285,41 @@ class ObjectId : Comparable { @LowLevelApi override fun serialize(encoder: Encoder, value: ObjectId) { - encoder.encodeString(value.hex) + serializeObjectIdPlatformSpecific(encoder, value) } override fun deserialize(decoder: Decoder): ObjectId = - decoder.decodeString().let(::ObjectId) + deserializeObjectIdPlatformSpecific(decoder) } } +@ExperimentalTime +internal fun serializeObjectIdAsString(encoder: Encoder, value: ObjectId) { + encoder.encodeString(value.hex) +} + +@ExperimentalTime +internal fun deserializeObjectIdAsString(decoder: Decoder): ObjectId = + decoder.decodeString().let(::ObjectId) + +/** + * On the JVM, when using KotlinX.Serialization with the official driver, we must hard-code a different behavior. + * + * All non-JVM platforms implement this function by calling [serializeObjectIdAsString]. + * This could be simplified with [KT-20427](https://youtrack.jetbrains.com/projects/KT/issues/KT-20427). + */ +@ExperimentalTime +internal expect fun serializeObjectIdPlatformSpecific(encoder: Encoder, value: ObjectId) + +/** + * On the JVM, when using KotlinX.Serialization with the official driver, we must hard-code a different behavior. + * + * All non-JVM platforms implement this function by calling [deserializeObjectIdAsString]. + * This could be simplified with [KT-20427](https://youtrack.jetbrains.com/projects/KT/issues/KT-20427). + */ +@ExperimentalTime +internal expect fun deserializeObjectIdPlatformSpecific(decoder: Decoder): ObjectId + @ExperimentalTime operator fun Instant.compareTo(objectId: ObjectId): Int = -objectId.compareTo(this) diff --git a/bson/src/jvmMain/kotlin/types/ObjectId.jvm.kt b/bson/src/jvmMain/kotlin/types/ObjectId.jvm.kt new file mode 100644 index 00000000..dc3b7879 --- /dev/null +++ b/bson/src/jvmMain/kotlin/types/ObjectId.jvm.kt @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2026, OpenSavvy and contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package opensavvy.ktmongo.bson.types + +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import org.bson.codecs.kotlinx.BsonDecoder +import org.bson.codecs.kotlinx.BsonEncoder +import kotlin.time.ExperimentalTime + +private val isOfficialKotlinSerializationEnabled = + ClassLoader.getSystemClassLoader().loadClass("org.bson.codecs.kotlinx.BsonEncoder") != null + +@OptIn(ExperimentalTime::class, ExperimentalSerializationApi::class) +internal actual fun serializeObjectIdPlatformSpecific(encoder: Encoder, value: ObjectId) { + if (isOfficialKotlinSerializationEnabled && encoder is BsonEncoder) { + encoder.encodeObjectId(org.bson.types.ObjectId(value.bytes)) + } else { + serializeObjectIdAsString(encoder, value) + } +} + +@OptIn(ExperimentalSerializationApi::class) +@ExperimentalTime +internal actual fun deserializeObjectIdPlatformSpecific(decoder: Decoder): ObjectId = + if (isOfficialKotlinSerializationEnabled && decoder is BsonDecoder) + ObjectId(decoder.decodeObjectId().toByteArray()) + else + deserializeObjectIdAsString(decoder) diff --git a/bson/src/nativeMain/kotlin/types/ObjectId.native.kt b/bson/src/nativeMain/kotlin/types/ObjectId.native.kt new file mode 100644 index 00000000..cdb32a8d --- /dev/null +++ b/bson/src/nativeMain/kotlin/types/ObjectId.native.kt @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2026, OpenSavvy and contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package opensavvy.ktmongo.bson.types + +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import kotlin.time.ExperimentalTime + +@ExperimentalTime +internal actual fun serializeObjectIdPlatformSpecific(encoder: Encoder, value: ObjectId) { + serializeObjectIdAsString(encoder, value) +} + +@ExperimentalTime +internal actual fun deserializeObjectIdPlatformSpecific(decoder: Decoder): ObjectId = + deserializeObjectIdAsString(decoder) diff --git a/bson/src/wasmWasiMain/kotlin/types/ObjectId.wasmWasi.kt b/bson/src/wasmWasiMain/kotlin/types/ObjectId.wasmWasi.kt new file mode 100644 index 00000000..cdb32a8d --- /dev/null +++ b/bson/src/wasmWasiMain/kotlin/types/ObjectId.wasmWasi.kt @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2026, OpenSavvy and contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package opensavvy.ktmongo.bson.types + +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import kotlin.time.ExperimentalTime + +@ExperimentalTime +internal actual fun serializeObjectIdPlatformSpecific(encoder: Encoder, value: ObjectId) { + serializeObjectIdAsString(encoder, value) +} + +@ExperimentalTime +internal actual fun deserializeObjectIdPlatformSpecific(decoder: Decoder): ObjectId = + deserializeObjectIdAsString(decoder) diff --git a/bson/src/webMain/kotlin/types/ObjectId.web.kt b/bson/src/webMain/kotlin/types/ObjectId.web.kt new file mode 100644 index 00000000..cdb32a8d --- /dev/null +++ b/bson/src/webMain/kotlin/types/ObjectId.web.kt @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2026, OpenSavvy and contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package opensavvy.ktmongo.bson.types + +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import kotlin.time.ExperimentalTime + +@ExperimentalTime +internal actual fun serializeObjectIdPlatformSpecific(encoder: Encoder, value: ObjectId) { + serializeObjectIdAsString(encoder, value) +} + +@ExperimentalTime +internal actual fun deserializeObjectIdPlatformSpecific(decoder: Decoder): ObjectId = + deserializeObjectIdAsString(decoder)