From 49f6cc9889314495039e0a166d0d1427e6f0554e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivan=20=E2=80=9CCLOVIS=E2=80=9D=20Canet?= Date: Fri, 2 May 2025 09:51:46 +0200 Subject: [PATCH] feat(bson): Read and write MinKey and MaxKey --- .../MultiplatformBsonArrayFieldWriter.kt | 10 ++++ .../kotlin/MultiplatformBsonFieldWriter.kt | 12 +++++ .../MultiplatformBsonSingleFieldWriter.kt | 10 ++++ .../kotlin/MultiplatformBsonValueReader.kt | 12 +++++ .../src/commonTest/kotlin/Bson.kt | 1 + .../src/jvmMain/kotlin/BsonContext.jvm.kt | 26 ++++++++++ .../src/jvmMain/kotlin/BsonReader.jvm.kt | 10 ++++ .../src/commonMain/kotlin/BsonWriterTest.kt | 1 + .../commonMain/kotlin/raw/MinMaxKeyTest.kt | 52 +++++++++++++++++++ bson/src/commonMain/kotlin/BsonReader.kt | 8 +++ bson/src/commonMain/kotlin/BsonWriter.kt | 12 ++++- 11 files changed, 152 insertions(+), 2 deletions(-) create mode 100644 bson-tests/src/commonMain/kotlin/raw/MinMaxKeyTest.kt diff --git a/bson-multiplatform/src/commonMain/kotlin/MultiplatformBsonArrayFieldWriter.kt b/bson-multiplatform/src/commonMain/kotlin/MultiplatformBsonArrayFieldWriter.kt index a1ab2947..41be0901 100644 --- a/bson-multiplatform/src/commonMain/kotlin/MultiplatformBsonArrayFieldWriter.kt +++ b/bson-multiplatform/src/commonMain/kotlin/MultiplatformBsonArrayFieldWriter.kt @@ -122,6 +122,16 @@ internal class MultiplatformBsonArrayFieldWriter( writer.writeJavaScript(nextIndex(), code) } + @LowLevelApi + override fun writeMinKey() { + writer.writeMinKey(nextIndex()) + } + + @LowLevelApi + override fun writeMaxKey() { + writer.writeMaxKey(nextIndex()) + } + @LowLevelApi override fun writeDocument(block: BsonFieldWriter.() -> Unit) { writer.writeDocument(nextIndex(), block) diff --git a/bson-multiplatform/src/commonMain/kotlin/MultiplatformBsonFieldWriter.kt b/bson-multiplatform/src/commonMain/kotlin/MultiplatformBsonFieldWriter.kt index f6367fd7..ae389ee5 100644 --- a/bson-multiplatform/src/commonMain/kotlin/MultiplatformBsonFieldWriter.kt +++ b/bson-multiplatform/src/commonMain/kotlin/MultiplatformBsonFieldWriter.kt @@ -165,6 +165,18 @@ internal class MultiplatformBsonFieldWriter( writer.writeString(code) } + @LowLevelApi + override fun writeMinKey(name: String) { + writeType(BsonType.MinKey) + writeName(name) + } + + @LowLevelApi + override fun writeMaxKey(name: String) { + writeType(BsonType.MaxKey) + writeName(name) + } + private inline fun writeArbitraryDocument(writeTo: (BsonFieldWriter) -> Unit) { // We create the entire document in a child buffer so we can measure the size. // Once we know the size, we can write it entirely to the real buffer. diff --git a/bson-multiplatform/src/commonMain/kotlin/MultiplatformBsonSingleFieldWriter.kt b/bson-multiplatform/src/commonMain/kotlin/MultiplatformBsonSingleFieldWriter.kt index cf6f5f5c..f1236566 100644 --- a/bson-multiplatform/src/commonMain/kotlin/MultiplatformBsonSingleFieldWriter.kt +++ b/bson-multiplatform/src/commonMain/kotlin/MultiplatformBsonSingleFieldWriter.kt @@ -121,6 +121,16 @@ internal class MultiplatformBsonSingleFieldWriter( writer.writeArray(name, block) } + @LowLevelApi + override fun writeMinKey() { + writer.writeMinKey(name) + } + + @LowLevelApi + override fun writeMaxKey() { + writer.writeMaxKey(name) + } + @LowLevelApi override fun writeObjectSafe(obj: T) { writer.writeObjectSafe(name, obj) diff --git a/bson-multiplatform/src/commonMain/kotlin/MultiplatformBsonValueReader.kt b/bson-multiplatform/src/commonMain/kotlin/MultiplatformBsonValueReader.kt index 51aac134..db80814a 100644 --- a/bson-multiplatform/src/commonMain/kotlin/MultiplatformBsonValueReader.kt +++ b/bson-multiplatform/src/commonMain/kotlin/MultiplatformBsonValueReader.kt @@ -165,6 +165,16 @@ internal class MultiplatformBsonValueReader( return bytes.reader.readString() } + @LowLevelApi + override fun readMinKey() { + checkType(BsonType.MinKey) + } + + @LowLevelApi + override fun readMaxKey() { + checkType(BsonType.MaxKey) + } + @LowLevelApi override fun readDocument(): BsonDocumentReader { checkType(BsonType.Document) @@ -198,6 +208,8 @@ internal class MultiplatformBsonValueReader( """{"${'$'}binary": {"base64": "$base64", "subType": "${subType.toString(16).padStart(2, '0')}"}}""" } + BsonType.MinKey -> """{"${'$'}minKey": 1}""" + BsonType.MaxKey -> """{"${'$'}maxKey": 1}""" else -> "{$type}: $bytes" // TODO } diff --git a/bson-multiplatform/src/commonTest/kotlin/Bson.kt b/bson-multiplatform/src/commonTest/kotlin/Bson.kt index 0a063a7e..319c8c19 100644 --- a/bson-multiplatform/src/commonTest/kotlin/Bson.kt +++ b/bson-multiplatform/src/commonTest/kotlin/Bson.kt @@ -37,4 +37,5 @@ class MultiplatformBsonWriterTest : PreparedSpec({ binary(context) code(context) datetime(context) + minMaxKey(context) }) diff --git a/bson-official/src/jvmMain/kotlin/BsonContext.jvm.kt b/bson-official/src/jvmMain/kotlin/BsonContext.jvm.kt index b052d5a7..4ae38850 100644 --- a/bson-official/src/jvmMain/kotlin/BsonContext.jvm.kt +++ b/bson-official/src/jvmMain/kotlin/BsonContext.jvm.kt @@ -191,6 +191,14 @@ private class JavaBsonWriter( writer.writeDecimal128(name, Decimal128.fromIEEE754BIDEncoding(high, low)) } + override fun writeMinKey(name: String) { + writer.writeMinKey(name) + } + + override fun writeMaxKey(name: String) { + writer.writeMaxKey(name) + } + override fun writeBoolean(value: Boolean) { writer.writeBoolean(value) } @@ -290,6 +298,14 @@ private class JavaBsonWriter( override fun writeDecimal128(low: Long, high: Long) { writer.writeDecimal128(Decimal128.fromIEEE754BIDEncoding(high, low)) } + + override fun writeMinKey() { + writer.writeMinKey() + } + + override fun writeMaxKey() { + writer.writeMaxKey() + } } @LowLevelApi @@ -407,4 +423,14 @@ private class JavaRootArrayWriter( array.add(document) } + @LowLevelApi + override fun writeMinKey() { + array.add(BsonMinKey()) + } + + @LowLevelApi + override fun writeMaxKey() { + array.add(BsonMaxKey()) + } + } diff --git a/bson-official/src/jvmMain/kotlin/BsonReader.jvm.kt b/bson-official/src/jvmMain/kotlin/BsonReader.jvm.kt index f0ad8f80..21bd270d 100644 --- a/bson-official/src/jvmMain/kotlin/BsonReader.jvm.kt +++ b/bson-official/src/jvmMain/kotlin/BsonReader.jvm.kt @@ -228,6 +228,16 @@ private class BsonValueReader( return value.asJavaScript().code } + @LowLevelApi + override fun readMinKey() { + ensureType(BsonType.MinKey) { value.bsonType == org.bson.BsonType.MIN_KEY } + } + + @LowLevelApi + override fun readMaxKey() { + ensureType(BsonType.MaxKey) { value.bsonType == org.bson.BsonType.MAX_KEY } + } + @LowLevelApi override fun readDocument(): BsonDocumentReader { ensureType(BsonType.Document) { value.isDocument } diff --git a/bson-tests/src/commonMain/kotlin/BsonWriterTest.kt b/bson-tests/src/commonMain/kotlin/BsonWriterTest.kt index 484fe0a2..69a99f95 100644 --- a/bson-tests/src/commonMain/kotlin/BsonWriterTest.kt +++ b/bson-tests/src/commonMain/kotlin/BsonWriterTest.kt @@ -93,6 +93,7 @@ fun SuiteDsl.writerTests( binary(prepareContext) code(prepareContext) datetime(prepareContext) + minMaxKey(prepareContext) } @OptIn(DangerousMongoApi::class) diff --git a/bson-tests/src/commonMain/kotlin/raw/MinMaxKeyTest.kt b/bson-tests/src/commonMain/kotlin/raw/MinMaxKeyTest.kt new file mode 100644 index 00000000..794f72f3 --- /dev/null +++ b/bson-tests/src/commonMain/kotlin/raw/MinMaxKeyTest.kt @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2025, 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. + */ + +@file:OptIn(LowLevelApi::class) + +package opensavvy.ktmongo.bson.raw + +import io.kotest.matchers.shouldBe +import opensavvy.ktmongo.bson.BsonContext +import opensavvy.ktmongo.dsl.LowLevelApi +import opensavvy.prepared.suite.Prepared +import opensavvy.prepared.suite.SuiteDsl + +/** + * Test MinKey and MaxKey representations. + * + * Adapted from the BSON corpus tests. + */ +fun SuiteDsl.minMaxKey(context: Prepared) = suite("MinMaxKey") { + testBson( + context, + name = "Minkey", + ) { + document { writeMinKey("a") } + expectedBinaryHex = "08000000FF610000" + expectedJson = $$"""{"a": {"$minKey": 1}}""" + verify("Read value") { read("a")?.readMinKey() shouldBe Unit } + } + + testBson( + context, + name = "Maxkey", + ) { + document { writeMaxKey("a") } + expectedBinaryHex = "080000007F610000" + expectedJson = $$"""{"a": {"$maxKey": 1}}""" + verify("Read value") { read("a")?.readMaxKey() shouldBe Unit } + } +} diff --git a/bson/src/commonMain/kotlin/BsonReader.kt b/bson/src/commonMain/kotlin/BsonReader.kt index 15abf278..ed8524c3 100644 --- a/bson/src/commonMain/kotlin/BsonReader.kt +++ b/bson/src/commonMain/kotlin/BsonReader.kt @@ -227,6 +227,14 @@ interface BsonValueReader { @Throws(BsonReaderException::class) fun readJavaScript(): String + @LowLevelApi + @Throws(BsonReaderException::class) + fun readMinKey() + + @LowLevelApi + @Throws(BsonReaderException::class) + fun readMaxKey() + @LowLevelApi @Throws(BsonReaderException::class) fun readDocument(): BsonDocumentReader diff --git a/bson/src/commonMain/kotlin/BsonWriter.kt b/bson/src/commonMain/kotlin/BsonWriter.kt index c00845ef..8018a48c 100644 --- a/bson/src/commonMain/kotlin/BsonWriter.kt +++ b/bson/src/commonMain/kotlin/BsonWriter.kt @@ -77,6 +77,10 @@ interface BsonValueWriter : AnyBsonWriter { @LowLevelApi fun writeBinaryData(type: UByte, data: ByteArray) @LowLevelApi fun writeJavaScript(code: String) + @LowLevelApi + fun writeMinKey() + @LowLevelApi + fun writeMaxKey() @LowLevelApi fun writeDocument(block: BsonFieldWriter.() -> Unit) @LowLevelApi fun writeArray(block: BsonValueWriter.() -> Unit) @@ -136,8 +140,8 @@ interface BsonValueWriter : AnyBsonWriter { writeDecimal128(bytes.readLong(0), bytes.readLong(1)) } - BsonType.MinKey -> TODO("Will be implemented in https://gitlab.com/opensavvy/ktmongo/-/work_items/64") - BsonType.MaxKey -> TODO("Will be implemented in https://gitlab.com/opensavvy/ktmongo/-/work_items/64") + BsonType.MinKey -> writeMinKey() + BsonType.MaxKey -> writeMaxKey() } } } @@ -195,6 +199,10 @@ interface BsonFieldWriter : AnyBsonWriter { @LowLevelApi fun writeBinaryData(name: String, type: UByte, data: ByteArray) @LowLevelApi fun writeJavaScript(name: String, code: String) + @LowLevelApi + fun writeMinKey(name: String) + @LowLevelApi + fun writeMaxKey(name: String) @LowLevelApi fun writeDocument(name: String, block: BsonFieldWriter.() -> Unit) @LowLevelApi fun writeArray(name: String, block: BsonValueWriter.() -> Unit) -- 2.51.2