Data formats

BSON

BSON, MongoDB's binary format: documents to BSON and back as Extended JSON, and ObjectIds made and read.

Version 1.0.0 · 4 functions · 2 crates

#mongodb#binary#ids#import

Functions

What use bson.qwer gives a program, with the types Quillo sees. To take only some, or under other names: use bson_encode, bson_decode from bson (Quillo 0.6).

bson_encode(document_map: any) → stror an error

document (a map) as BSON, in base64. Extended JSON in it is read: { "$oid": "..." } is an ObjectId, { "$date": "2026-11-02T09:00:00Z" } a date, { "$numberDecimal": "12.50" } a decimal.

bson_decode(base64_text: str) → anyor an error

The document in base64 BSON, as relaxed Extended JSON: ObjectIds as { "$oid": ... }, dates as { "$date": ... }, binary as { "$binary": ... }; ints and floats as numbers.

bson_object_id() → str

A new ObjectId, as its 24 hex characters: unique without asking a database, and sorted by the time it was made.

bson_object_id_info(id: str) → any

What an ObjectId says: { valid, time, ms }, time when it was made (UTC, to the second) and ms the same in milliseconds since 1970. valid is false (and the rest null) for text that is not 24 hex characters.

Get it

$ quillo install bson               # bson.qwer into this folder$ quillo install bson --example     # and its example, bson.qwe

Or with curl:

$ curl -O https://quillolang.org/plugins/files/bson.qwer$ curl -O https://quillolang.org/plugins/files/bson.qwe

Then, in your program, all of it or only what you call:

main.qwe
use bson.qweruse bson_encode, bson_decode from bson      // Quillo 0.6: only these

It uses these crates, which cargo fetches the first time the plugin is built:

[dependencies]
bson = "2"base64 = "0.22"

Example

bson.qwe
// Documents for MongoDB written as BSON, read back, and the time inside an ObjectId.////   quillo run bson.qwe      then open http://localhost:8080/roundtripuse "bson.qwer"route GET /roundtrip {    let id = bson_object_id()    let doc = { _id: { "$oid": id }, name: "Ada", signed_up: { "$date": "2026-11-02T09:00:00Z" }, orders: 3, balance: 12.5, tags: ["beta"] }    let packed = bson_encode(doc)?    let back = bson_decode(packed)?    { packed, back, same_id: back._id["$oid"] == id }}route GET /id ? { id: str = "6716a3c0e4b0a1b2c3d4e5f6" } = bson_object_id_info(id)route GET /bad = { not_hex: bson_object_id_info("hello").valid, not_a_map: bson_encode([1, 2]).error }
$ quillo run bson.qwe$ curl 'localhost:8080/roundtrip'$ curl 'localhost:8080/id'

Source

bson.qwer
//! BSON, MongoDB's binary format: documents to BSON as base64 and back (with ObjectIds and dates as Extended JSON), and ObjectIds made and read.//! version: 1.0.0//!//! [dependencies]//! bson = "2"//! base64 = "0.22"use base64::engine::general_purpose::STANDARD;use base64::Engine;use bson::oid::ObjectId;use bson::{Bson, Document};use serde_json::{json, Value};/// BSON documents are at most 16 MiB, as in MongoDB.const MAX: usize = 16 * 1024 * 1024;fn document(value: Value) -> Result<Document, String> {    match Bson::try_from(value).map_err(|e| format!("not a document: {}", e))? {        Bson::Document(d) => Ok(d),        _ => Err("a BSON document is a map".into()),    }}/// `document` (a map) as BSON, in base64. Extended JSON in it is read: `{ "$oid": "..." }` is an ObjectId,/// `{ "$date": "2026-11-02T09:00:00Z" }` a date, `{ "$numberDecimal": "12.50" }` a decimal.pub fn bson_encode(document_map: serde_json::Value) -> Result<String, String> {    let d = document(document_map)?;    let mut out = Vec::new();    d.to_writer(&mut out).map_err(|e| e.to_string())?;    if out.len() > MAX {        return Err("a BSON document is at most 16 MiB".into());    }    Ok(STANDARD.encode(out))}/// The document in base64 BSON, as relaxed Extended JSON: ObjectIds as `{ "$oid": ... }`, dates as `{ "$date": ... }`,/// binary as `{ "$binary": ... }`; ints and floats as numbers.pub fn bson_decode(base64_text: &str) -> Result<Value, String> {    let bytes = STANDARD.decode(base64_text.trim()).map_err(|_| "not base64".to_string())?;    if bytes.len() > MAX {        return Err("a BSON document is at most 16 MiB".into());    }    let d = Document::from_reader(&mut bytes.as_slice()).map_err(|e| format!("not BSON: {}", e))?;    Ok(Bson::Document(d).into_relaxed_extjson())}/// A new ObjectId, as its 24 hex characters: unique without asking a database, and sorted by the time it was made.pub fn bson_object_id() -> String {    ObjectId::new().to_hex()}/// What an ObjectId says: `{ valid, time, ms }`, `time` when it was made (UTC, to the second) and `ms` the same in/// milliseconds since 1970. `valid` is false (and the rest null) for text that is not 24 hex characters.pub fn bson_object_id_info(id: &str) -> Value {    match ObjectId::parse_str(id.trim()) {        Ok(oid) => {            let t = oid.timestamp();            json!({ "valid": true, "time": t.try_to_rfc3339_string().ok(), "ms": t.timestamp_millis() })        }        Err(_) => json!({ "valid": false, "time": null, "ms": null }),    }}
CBOR cbor.qwer · 1.0.0CBOR (RFC 8949), the binary format of passkeys, COSE and IoT devices: values to CBOR and back, as base64 or files.binarypasskeysiotimport7 views · 7 downloadsProtobuf wire format protobuf_wire.qwer · 1.0.0Protocol Buffers without generated code: any message decoded field by field with no schema, decoded and encoded by name with the field numbers given, and length-delimited files split.binaryapiimportintegration7 views · 8 downloadsAmazon Ion amazon_ion.qwer · 1.0.0Amazon Ion 1.0 text and binary read and written, with a value form that keeps every Ion type: annotations, symbols and local symbol tables, typed nulls, decimals with their exact digits, timestamps at each precision and offset, integers of any size, blobs, clobs, sexps, long strings. Plain JSON both ways, and lengths past the end or nesting too deep refused.ionbinaryserializationaws6 views · 8 downloadsApache Arrow IPC arrow_ipc.qwer · 1.0.0Apache Arrow IPC stream and file formats read and written, through the official Rust crates: schemas, record batches split by size, rows to columns and back with nulls, lists, structs, dictionaries, decimals, dates and timestamps, LZ4 compression, and a schema inferred from rows. Values that do not fit their type name the row, and damaged data is an error.arrowcolumnaranalyticsbinary4 views · 8 downloadsApache Thrift thrift.qwer · 1.0.0Apache Thrift with no generated code: the binary and compact protocols encoded and decoded from a schema-less value form, field deltas, zigzag ints, bools in field headers and little-endian doubles included; TMessage framing, the old unversioned form too; and calls to a framed server with results, declared exceptions and TApplicationException read.rpcbinaryserializationservices5 views · 7 downloadsASN.1 DER and BER asn1_der.qwer · 1.0.0ASN.1 read to a tree and written back, with DER's rules enforced and BER's looseness when asked: tags of any size, OIDs with a table of names, UTCTime and GeneralizedTime, integers of any size, every string type, SETs sorted, PEM, a dump in the manner of `openssl asn1parse`. Indefinite lengths are refused in DER, and bad lengths, tags and values are errors naming their offset, never panics.asn1certificatesbinaryx5094 views · 8 downloads
Thread
66Useful? Like it, or add to its thread below.

Thread 0 posts

On BSON: questions, tips, corrections, and code that uses it.

No posts yet. The first one opens the thread.