From 8ce08a69592126cf597c6cdd40f39628a657bb36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivan=20=E2=80=9CCLOVIS=E2=80=9D=20Canet?= Date: Fri, 4 Sep 2026 22:20:06 +0200 Subject: [PATCH] refactor(driver-multiplatform): Facilitate creating instances of OpMsg --- .../src/commonMain/kotlin/MessageBuilder.kt | 93 +++++++++++++++++++ .../MultiplatformMongoCollectionImpl.kt | 57 +++++------- 2 files changed, 115 insertions(+), 35 deletions(-) create mode 100644 driver-multiplatform/src/commonMain/kotlin/MessageBuilder.kt diff --git a/driver-multiplatform/src/commonMain/kotlin/MessageBuilder.kt b/driver-multiplatform/src/commonMain/kotlin/MessageBuilder.kt new file mode 100644 index 00000000..61d224c4 --- /dev/null +++ b/driver-multiplatform/src/commonMain/kotlin/MessageBuilder.kt @@ -0,0 +1,93 @@ +/* + * 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.multiplatform + +import opensavvy.ktmongo.bson.BsonFieldWriter +import opensavvy.ktmongo.bson.multiplatform.BsonDocument +import opensavvy.ktmongo.bson.multiplatform.BsonFactory +import opensavvy.ktmongo.dsl.LowLevelApi +import opensavvy.ktmongo.multiplatform.wire.Message +import opensavvy.ktmongo.multiplatform.wire.MessageSection + +// Everything is 'internal' to allow everything to be inlined + +@OptIn(LowLevelApi::class) +internal class MessageBuilder( + internal val factory: BsonFactory, +) { + internal var document: BsonDocument? = null + internal val sequences = ArrayList() + + inline fun document(crossinline block: BsonFieldWriter.() -> Unit) { + check(document == null) { "Cannot set 'document' multiple times" } + document = factory.buildDocument { + block() + } + } + + inline fun sequence(id: String, block: MessageSequenceBuilder.() -> Unit) { + sequences += MessageSection.DocumentSequence( + id = id, + lazyDocuments = MessageSequenceBuilder(factory).apply(block).documents, + ) + } +} + +@LowLevelApi +internal class MessageSequenceBuilder( + internal val factory: BsonFactory, +) { + internal val documents = ArrayList>() + + inline fun document(crossinline block: BsonFieldWriter.() -> Unit) { + documents += eager( + factory.buildDocument { + block() + } + ) + } +} + +internal fun MultiplatformMongoClient.createOpMsg( + block: MessageBuilder.() -> Unit, +): Message.OpMsg { + val builder = MessageBuilder(factory).apply(block) + return Message.OpMsg( + body = MessageSection.Body(eager(builder.document!!)), + sequences = builder.sequences.asSequence(), + ) +} + +/** + * Instantiates a [Lazy] value that isn't lazy. + * + * This allows our API to contain lazy values without forcing us to be lazy everywhere. + * + * For example, we often want to be lazy during request sending (so all serialization happens as close as possible to the socket) + * but not during reception (to extract information as quickly as possible and return the lock). + */ +internal fun eager(value: T): Lazy = + object : Lazy { + override val value: T + get() = value + + override fun isInitialized(): Boolean = + true + + override fun toString(): String = + "Lazy($value)" + } diff --git a/driver-multiplatform/src/commonMain/kotlin/MultiplatformMongoCollectionImpl.kt b/driver-multiplatform/src/commonMain/kotlin/MultiplatformMongoCollectionImpl.kt index 971cc372..000eeb2d 100644 --- a/driver-multiplatform/src/commonMain/kotlin/MultiplatformMongoCollectionImpl.kt +++ b/driver-multiplatform/src/commonMain/kotlin/MultiplatformMongoCollectionImpl.kt @@ -31,7 +31,6 @@ import opensavvy.ktmongo.dsl.query.UpdateQuery import opensavvy.ktmongo.dsl.query.UpdateWithPipelineQuery import opensavvy.ktmongo.dsl.query.UpsertQuery import opensavvy.ktmongo.multiplatform.wire.Message -import opensavvy.ktmongo.multiplatform.wire.MessageSection import kotlin.concurrent.atomics.ExperimentalAtomicApi import kotlin.reflect.KType @@ -57,25 +56,19 @@ internal class MultiplatformMongoCollectionImpl( document: Document, options: InsertOneOptions.() -> Unit, ) { - val command = lazy { - database.client.factory.buildDocument { - writeString("insert", name) - writeString($$"$db", database.name) - - InsertOne( - context = database.client.context, - document = document, - documentType = type, - ).writeTo(this) - } - } - val message = database.client.wire.sendSingle( - Message.OpMsg( - body = MessageSection.Body( - command, - ) - ) + database.client.createOpMsg { + document { + writeString("insert", name) + writeString($$"$db", database.name) + + InsertOne( + context = database.client.context, + document = document, + documentType = type, + ).writeTo(this) + } + } ) check(message is Message.OpMsg) @@ -103,23 +96,17 @@ internal class MultiplatformMongoCollectionImpl( } override suspend fun count(): Long { - val command = lazy { - database.client.factory.buildDocument { - writeString("count", name) - writeString($$"$db", database.name) - - Count( - context = database.client.context, - ).writeTo(this) - } - } - val message = database.client.wire.sendSingle( - Message.OpMsg( - body = MessageSection.Body( - command, - ) - ) + database.client.createOpMsg { + document { + writeString("count", name) + writeString($$"$db", database.name) + + Count( + context = database.client.context, + ).writeTo(this) + } + } ) message as Message.OpMsg -- 2.51.2