A BSON encoding and decoding library
moon add ZSeanYves/MoonbitBSONimport {
"ZSeanYves/MoonbitBSON",
}let user = @MoonbitBSON.Document::new()
.set("name", @MoonbitBSON.Bson::String("Ada"))
.set("age", @MoonbitBSON.Bson::Int32(37))
.set("active", @MoonbitBSON.Bson::Boolean(true))
.set(
"scores",
@MoonbitBSON.Bson::Array([
@MoonbitBSON.Bson::Double(9.5),
@MoonbitBSON.Bson::Double(10.0),
]),
)
let bytes = user.to_bytes()
let decoded = @MoonbitBSON.Document::from_bytes(bytes)
assert_eq(decoded.require_string("name"), "Ada")
assert_eq(decoded.require_int32("age"), 37)node tools/bson-codegen.mjs codegen/example.schema.json src/codegen_generated_test.mbtnode tools/bson-derive.mjs src/derive_types_test.mbt src/derive_generated_test.mbtmoon fmt --check src
moon check --target all --deny-warn --warn-list +73
moon test --target all --deny-warn --warn-list +73
moon test --release --target all --deny-warn --warn-list +73
moon bench --target native --release
moon coverage analyze -- -f summary
node tools/bson-codegen.mjs --check codegen/example.schema.json src/codegen_generated_test.mbt
node tools/bson-derive.mjs --check src/derive_types_test.mbt src/derive_generated_test.mbt
node tools/decimal128-differential.mjs
moon info --target all
moon package --list///|
test "build and roundtrip a document" {
let user = Document::new()
.set("name", String("Ada"))
.set("age", Int32(37))
.set("active", Boolean(true))
.set("scores", Array([Double(9.5), Double(10.0)]))
let encoded = user.to_bytes()
let decoded = Document::from_bytes(encoded)
assert_eq(decoded.require_string("name"), "Ada")
assert_eq(decoded.require_int32("age"), 37)
}///|
test "decode a framed document" {
let encoded = Document::new().set("x", Int64(42L)).to_bytes()
let framed = encoded + b"remaining"
let (document, consumed) = decode_prefix(framed[:])
assert_eq(document.require_int64("x"), 42L)
assert_eq(consumed, encoded.length())
}///|
test "canonical Extended JSON" {
let document = Document::new()
.set("count", Int64(9007199254740993L))
.set("decimal", Decimal128(Decimal128::from_string("1.2500")))
let text = document.to_extended_json_string()
let decoded = Document::from_extended_json_string(text)
assert_eq(decoded, document)
}///|
test "typed and raw APIs" {
let uuid = Uuid::from_string("00112233-4455-6677-8899-aabbccddeeff")
let document = Document::new()
.set("created", DateTime(DateTime::from_millis(0L)))
.set("request_id", Binary(uuid.to_binary()))
let raw = RawDocument::from_bytes(serialize_to_bytes(document))
assert_eq(raw.get_element("created").unwrap().type_code(), 0x09)
let decoded : Document = deserialize_from_bytes(raw.bytes())
assert_eq(decoded.require_datetime("created").to_millis(), 0L)
assert_eq(decoded.require_uuid("request_id"), uuid)
}///|
test "borrowed view and split stream" {
let bytes = Document::new().set("ok", Boolean(true)).to_bytes()
let view = RawDocumentView::from_bytes(bytes[:])
assert_eq(view.iter().next().unwrap().key(), "ok")
let stream = BsonStreamDecoder::new()
assert_eq(stream.push(bytes[:2]).length(), 0)
assert_eq(stream.push(bytes[2:])[0].require_bool("ok"), true)
stream.finish()
}///|
test "raw stream view" {
let first = Document::new().set("ok", Boolean(true)).to_bytes()
let second = Document::new().set("count", Int32(2)).to_bytes()
let stream = BsonStreamRawDecoder::new()
let views = stream.push(first + second)
assert_eq(views.length(), 2)
assert_eq(views[0].get("ok").unwrap().to_bson(), Boolean(true))
assert_eq(views[1].get("count").unwrap().to_bson(), Int32(2))
stream.finish()
}type SecureEntropyProvider = (Int) -> Bytes?pub(all) enum Bson {
Double(Double)
String(String)
Document(Document)
Array(Array[Bson])
Binary(Binary)
Undefined
ObjectId(ObjectId)
Boolean(Bool)
DateTime(DateTime)
Null
Regex(Regex)
DbPointer(DbPointer)
JavaScript(String)
Symbol(String)
JavaScriptWithScope(JavaScriptWithScope)
Int32(Int)
Timestamp(Timestamp)
Int64(Int64)
Decimal128(Decimal128)
MinKey
MaxKey
} derive(Eq, Debug)pub struct BsonErrorInfo {
kind : BsonErrorKind
offset : Int
path : String
message : String
} derive(Eq, Debug)pub(all) enum BsonErrorKind {
UnexpectedEnd
InvalidLength
InvalidUtf8
InvalidCString
InvalidBoolean
InvalidArrayIndex
InvalidBinary
InvalidObjectId
InvalidUuid
InvalidDateTime
InvalidDecimal128
InvalidRegex
UnsupportedType
TrailingData
DepthLimit
SizeLimit
InvalidExtendedJson
TypeMismatch
UnsupportedEntropy
} derive(Eq, Debug)fn BsonStreamDecoder::push(self : BsonStreamDecoder, chunk : BytesView) -> Array[Document] raise BsonErrorfn BsonStreamRawDecoder::push(self : BsonStreamRawDecoder, chunk : BytesView) -> Array[RawDocumentView] raise BsonErrorimpl FromBson for Decimal128impl ToBson for Decimal128fn DecodeOptions::new(max_depth? : Int, max_size? : Int, require_canonical_array_keys? : Bool) -> DecodeOptionspub(all) enum RawBsonRef {
Double(Double)
String(RawStringRef)
Document(RawDocumentView)
Array(RawArrayView)
Binary(RawBinaryRef)
Undefined
ObjectId(BytesView)
Boolean(Bool)
DateTime(Int64)
Null
Regex(RawRegexRef)
DbPointer(RawDbPointerRef)
JavaScript(RawStringRef)
Symbol(RawStringRef)
JavaScriptWithScope(RawJavaScriptWithScopeRef)
Int32(Int)
Timestamp(Timestamp)
Int64(Int64)
Decimal128(BytesView)
MinKey
MaxKey
} derive(Eq, Debug)pub struct RawElement {
document : RawDocument
key : String
type_code : Byte
value_start : Int
value_end : Int
} derive(Eq, Debug)pub struct RawElementView {
document : RawDocumentView
key_start : Int
key_end : Int
type_code : Byte
value_start : Int
value_end : Int
} derive(Eq, Debug)fn RawElementView::as_binary_bytes(self : RawElementView) -> (BinarySubtype, BytesView)? raise BsonErrorpub struct RawJavaScriptWithScopeRef {
code : RawStringRef
scope : RawDocumentView
} derive(Eq, Debug)fn decode_prefix_with_options(data : BytesView, options : DecodeOptions) -> (Document, Int) raise BsonErrorfn encode_to_with_options(document : Document, buffer : Buffer, options : EncodeOptions) -> Unit raise BsonErrorfn install_secure_entropy_provider(provider : (Int) -> Bytes?) -> UnitA BSON encoding and decoding library