import bson/md5 import gleam/int import gleam/list import bson/types import bson/custom import bson/generic import bson/object_id import gleam/bit_string type Entity { Entity(kind: types.Kind, value: BitString) } pub fn encode(doc: List(#(String, types.Value))) -> BitString { case document(doc) { Entity(kind: _, value: value) -> value } } fn document(doc: List(#(String, types.Value))) -> Entity { let doc = doc |> list.map(fn(kv) { encode_kv(kv) }) |> bit_string.concat let size = bit_string.byte_size(doc) + 5 Entity( kind: types.document, value: [<>, doc, <<0>>] |> bit_string.concat, ) } fn encode_kv(pair: #(String, types.Value)) -> BitString { let key = <> let value = case pair.1 { types.Null -> null() types.Min -> min() types.Max -> max() types.JS(value) -> js(value) types.Str(value) -> string(value) types.Array(value) -> array(value) types.Double(value) -> double(value) types.Boolean(value) -> boolean(value) types.Integer(value) -> integer(value) types.Document(value) -> document(value) types.DateTime(value) -> datetime(value) types.ObjectId(value) -> object_id(value) types.Timestamp(value) -> timestamp(value) types.Binary(types.MD5(value)) -> md5(value) types.Binary(types.Custom(value)) -> custom(value) types.Binary(types.Generic(value)) -> generic(value) } case value { Entity(kind: kind, value: value) -> [kind.code, key, value] |> bit_string.concat } } fn null() -> Entity { Entity(kind: types.null, value: <<>>) } fn min() -> Entity { Entity(kind: types.min, value: <<>>) } fn max() -> Entity { Entity(kind: types.max, value: <<>>) } fn js(value: String) -> Entity { let length = bit_string.byte_size(<>) + 1 Entity(kind: types.js, value: <>) } fn string(value: String) -> Entity { let length = bit_string.byte_size(<>) + 1 Entity(kind: types.string, value: <>) } fn array(list: List(types.Value)) -> Entity { case list |> list.index_map(fn(index, item) { #( index |> int.to_string, item, ) }) |> document { Entity(kind: _, value: value) -> Entity(kind: types.array, value: value) } } fn double(value: Float) -> Entity { Entity(kind: types.double, value: <>) } fn boolean(value: Bool) -> Entity { case value { True -> Entity(kind: types.boolean, value: <<1>>) False -> Entity(kind: types.boolean, value: <<0>>) } } fn integer(value: Int) -> Entity { case value < types.int32_max && value > types.int32_min { True -> Entity(kind: types.int32, value: <>) False -> Entity(kind: types.int64, value: <>) } } fn datetime(value: Int) -> Entity { Entity(kind: types.datetime, value: <>) } fn object_id(value: object_id.ObjectId) -> Entity { Entity( kind: types.object_id, value: value |> object_id.to_bit_string, ) } fn timestamp(value: Int) -> Entity { Entity(kind: types.timestamp, value: <>) } fn md5(value: md5.MD5) -> Entity { let value = value |> md5.to_bit_string let length = bit_string.byte_size(value) Entity( kind: types.binary, value: [<>, types.md5.code, value] |> bit_string.concat, ) } fn custom(value: custom.Custom) -> Entity { let #(code, value) = value |> custom.to_bit_string_with_code let length = bit_string.byte_size(value) Entity( kind: types.binary, value: [<>, code, value] |> bit_string.concat, ) } fn generic(value: generic.Generic) -> Entity { let value = value |> generic.to_bit_string let length = bit_string.byte_size(value) Entity( kind: types.binary, value: [<>, types.generic.code, value] |> bit_string.concat, ) }