├── bson ├── bench │ ├── .gitignore │ ├── package.json │ ├── index.mjs │ ├── deno_bench.ts │ └── yarn.lock ├── .gitignore ├── src │ ├── encoder │ │ ├── mod.rs │ │ ├── value.rs │ │ ├── number.rs │ │ ├── object.rs │ │ └── extended.rs │ └── lib.rs ├── Cargo.toml ├── Makefile ├── types.ts ├── scripts │ └── generate.ts ├── mod.ts └── Cargo.lock ├── .gitignore ├── assets └── mango.png ├── tmp ├── mongo-data-dump │ ├── profiles.bson │ └── profiles.metadata.json ├── mongodb.env ├── mongo.seeder.dockerfile ├── docker-compose.yml └── app.ts ├── .editorconfig ├── deps.ts ├── DEV.md ├── mod.ts ├── protocol ├── header.ts ├── mod.ts └── op_msg.ts ├── LICENSE └── README.md /bson/bench/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /bson/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | 3 | build 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OS files 2 | .DS_Store 3 | .cache 4 | 5 | # IDE 6 | .vscode 7 | -------------------------------------------------------------------------------- /assets/mango.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denodrivers/mango/HEAD/assets/mango.png -------------------------------------------------------------------------------- /tmp/mongo-data-dump/profiles.bson: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denodrivers/mango/HEAD/tmp/mongo-data-dump/profiles.bson -------------------------------------------------------------------------------- /bson/src/encoder/mod.rs: -------------------------------------------------------------------------------- 1 | mod extended; 2 | mod number; 3 | mod object; 4 | mod value; 5 | 6 | pub use object::create_document; 7 | -------------------------------------------------------------------------------- /bson/bench/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bench", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "license": "MIT", 6 | "dependencies": { 7 | "bson": "4.1.0" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /tmp/mongo-data-dump/profiles.metadata.json: -------------------------------------------------------------------------------- 1 | {"options":{},"indexes":[{"v":{"$numberInt":"2"},"key":{"_id":{"$numberInt":"1"}},"name":"_id_","ns":"juanportal.profiles"}],"uuid":"11fd8fa3efba4eb09623636a7565a738"} -------------------------------------------------------------------------------- /tmp/mongodb.env: -------------------------------------------------------------------------------- 1 | MONGODB_ROOT_USERNAME=root 2 | MONGODB_ROOT_PASSWD=rootpassword 3 | MONGODB_ROOT_ROLE=root 4 | MONGODB_USERNAME=user 5 | MONGODB_PASSWD=userpassword 6 | MONGODB_DBNAME=juanportal 7 | MONGODB_ROLE=readWrite -------------------------------------------------------------------------------- /tmp/mongo.seeder.dockerfile: -------------------------------------------------------------------------------- 1 | FROM mongo 2 | 3 | COPY ./mongo-data-dump /mongo_data 4 | # Profiles collection 5 | CMD mongorestore --host mongodb --db juanportal /mongo_data 6 | #CMD mongorestore --host mongodb --db juanportal /mongo_data 7 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | indent_style = space 7 | indent_size = 2 8 | 9 | [*.rs] 10 | indent_size = 4 11 | 12 | [Makefile] 13 | indent_style = tab 14 | indent_size = 4 15 | -------------------------------------------------------------------------------- /deps.ts: -------------------------------------------------------------------------------- 1 | export { BufReader } from "https://deno.land/std@0.74.0/io/mod.ts"; 2 | export { deferred } from "https://deno.land/std@0.74.0/async/deferred.ts"; 3 | export type { Deferred } from "https://deno.land/std@0.74.0/async/deferred.ts"; 4 | export { 5 | assert, 6 | assertEquals, 7 | } from "https://deno.land/std@0.74.0/testing/asserts.ts"; 8 | -------------------------------------------------------------------------------- /DEV.md: -------------------------------------------------------------------------------- 1 | # Using the BSON converter 2 | 3 | `make` 4 | 5 | Or to be smart: `make && deno run --allow-read bson/mod.ts` 6 | 7 | # Using Driver Dev Environment 8 | 9 | ```shell script 10 | $ cd tmp 11 | $ docker-compose up -d 12 | $ deno run -A app.ts 13 | ``` 14 | 15 | Mongo database is seeded with a `profiles` collection, with 3 documents 16 | -------------------------------------------------------------------------------- /mod.ts: -------------------------------------------------------------------------------- 1 | import { MongoProtocol } from "./protocol/mod.ts"; 2 | 3 | const socket = await Deno.connect({ port: 27017, transport: "tcp" }); 4 | console.log("Connected"); 5 | 6 | const protocol = new MongoProtocol(socket); 7 | 8 | const res = await protocol.executeOpMsg([{ 9 | kind: 0, 10 | body: { find: "profiles", "$db": "juanportal" }, 11 | }]); 12 | console.log(res[0]); 13 | 14 | protocol.close(); 15 | console.log("Closed"); 16 | -------------------------------------------------------------------------------- /bson/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mango_bson" 3 | version = "0.1.0" 4 | authors = ["Filippo Rossi"] 5 | edition = "2018" 6 | 7 | [lib] 8 | crate-type = ["cdylib"] 9 | path = "src/lib.rs" 10 | 11 | [dependencies] 12 | chrono = "0.4" 13 | serde = { version = "1.0", features = ["derive"] } 14 | serde_json = "1.0" 15 | wasm-bindgen = { version = "0.2", features = ["serde-serialize"] } 16 | js-sys = "0.3" 17 | bson = "1.1" 18 | hex = "0.4" 19 | base64 = "0.13" 20 | 21 | [profile.release] 22 | lto = true 23 | opt-level = 3 24 | -------------------------------------------------------------------------------- /bson/Makefile: -------------------------------------------------------------------------------- 1 | BUILD=build 2 | CRATE=mango_bson 3 | 4 | WASM=wasm 5 | 6 | all: generate 7 | 8 | generate: build 9 | wasm-bindgen --target deno \ 10 | --out-dir "$(BUILD)" \ 11 | "./target/wasm32-unknown-unknown/release/$(CRATE).wasm" 12 | deno run --allow-read --allow-write scripts/generate.ts 13 | 14 | build: prepare 15 | cargo build --release --target wasm32-unknown-unknown 16 | 17 | prepare: 18 | mkdir -p $(BUILD) 19 | mkdir -p $(WASM) 20 | 21 | clean: 22 | rm -rf $(BUILD) 23 | rm -rf $(WASM) 24 | 25 | .PHONY: all generate build prepare 26 | -------------------------------------------------------------------------------- /tmp/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | 5 | mongodb: 6 | container_name: mango_mongo 7 | image: mongo 8 | ports: 9 | - "27017:27017" 10 | env_file: 11 | - ./mongodb.env 12 | networks: 13 | - mango-network 14 | 15 | mongoseeder: 16 | container_name: mango_mongoseeder 17 | build: 18 | context: . 19 | dockerfile: mongo.seeder.dockerfile 20 | depends_on: 21 | - mongodb 22 | networks: 23 | - mango-network 24 | 25 | networks: 26 | mango-network: 27 | driver: bridge 28 | -------------------------------------------------------------------------------- /bson/bench/index.mjs: -------------------------------------------------------------------------------- 1 | import bson from "bson"; 2 | import { performance } from "perf_hooks"; 3 | 4 | const date = new Date(); 5 | const data = bson.serialize( 6 | { 7 | foo: "bar", 8 | hello: 55, 9 | date, 10 | binary: Buffer.alloc(128), 11 | }, 12 | ); 13 | 14 | const start = performance.now(); 15 | 16 | for (let i = 0; i < 50000; i++) { 17 | const date = new Date(); 18 | const data = bson.serialize( 19 | { 20 | foo: "bar", 21 | hello: 55, 22 | date, 23 | binary: Buffer.alloc(128), 24 | }, 25 | ); 26 | } 27 | 28 | const stop = performance.now(); 29 | console.log(stop - start); 30 | -------------------------------------------------------------------------------- /bson/src/encoder/value.rs: -------------------------------------------------------------------------------- 1 | use bson::Bson; 2 | use wasm_bindgen::JsValue; 3 | 4 | use super::object; 5 | use crate::Result; 6 | 7 | /// Inspect a generic JsValue, taking into account default javascript values 8 | pub fn inspect(target: &JsValue) -> Result { 9 | if let Some(n) = target.as_f64() { 10 | return Ok(Bson::Double(n)); 11 | } else if target.is_string() { 12 | return Ok(Bson::String(target.as_string().unwrap())); 13 | } else if let Some(b) = target.as_bool() { 14 | return Ok(Bson::Boolean(b)); 15 | } else if target.is_null() { 16 | return Ok(Bson::Null); 17 | } else if target.is_object() { 18 | return Ok(object::inspect(target)?); 19 | } 20 | Err("type not valid in BSON spec".into()) 21 | } 22 | -------------------------------------------------------------------------------- /protocol/header.ts: -------------------------------------------------------------------------------- 1 | export interface MessageHeader { 2 | messageLength: number; 3 | requestID: number; 4 | responseTo: number; 5 | opCode: number; 6 | } 7 | 8 | export function serializeHeader(header: MessageHeader): Uint8Array { 9 | const view = new DataView(new ArrayBuffer(16)); 10 | view.setInt32(0, header.messageLength, true); 11 | view.setInt32(4, header.requestID, true); 12 | view.setInt32(8, header.responseTo, true); 13 | view.setInt32(12, header.opCode, true); 14 | return new Uint8Array(view.buffer); 15 | } 16 | 17 | export function parseHeader(buf: Uint8Array): MessageHeader { 18 | const view = new DataView(buf.buffer); 19 | return { 20 | messageLength: view.getInt32(0, true), 21 | requestID: view.getInt32(4, true), 22 | responseTo: view.getInt32(8, true), 23 | opCode: view.getInt32(12, true), 24 | }; 25 | } 26 | -------------------------------------------------------------------------------- /bson/bench/deno_bench.ts: -------------------------------------------------------------------------------- 1 | import { Binary, encode } from "../mod.ts"; 2 | import { BinarySubtype } from "../types.ts"; 3 | import { 4 | bench, 5 | runBenchmarks, 6 | } from "https://deno.land/std@0.74.0/testing/bench.ts"; 7 | 8 | bench({ 9 | name: "encode", 10 | runs: 3, 11 | func(b) { 12 | const date = new Date(); 13 | const data = encode( 14 | { 15 | foo: "bar", 16 | hello: 55, 17 | date, 18 | binary: Binary(new Uint8Array(128), BinarySubtype.Generic), 19 | }, 20 | ); 21 | b.start(); 22 | for (let i = 0; i < 50000; i++) { 23 | const data = encode( 24 | { 25 | foo: "bar", 26 | hello: 55, 27 | date, 28 | binary: Binary(new Uint8Array(128), BinarySubtype.Generic), 29 | }, 30 | ); 31 | } 32 | b.stop(); 33 | }, 34 | }); 35 | 36 | await runBenchmarks(); 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 the denodrivers team 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /bson/src/lib.rs: -------------------------------------------------------------------------------- 1 | use wasm_bindgen::prelude::*; 2 | 3 | type Result = std::result::Result; 4 | 5 | mod encoder; 6 | 7 | #[wasm_bindgen] 8 | pub fn to_bson_document(target: &JsValue) -> Result> { 9 | if !target.is_object() { 10 | return Err(js_sys::Error::new("only object can be serialized to bson documents").into()); 11 | } 12 | let document = encoder::create_document(&target)?; 13 | let mut buf: Vec = vec![]; 14 | document.to_writer(&mut buf).map_err(|err| { 15 | js_sys::Error::new(&format!("error writing document: {}", err.to_string())) 16 | })?; 17 | Ok(buf) 18 | } 19 | 20 | #[wasm_bindgen] 21 | pub fn from_bson_document(buf: Vec) -> Result { 22 | let mut x: &[u8] = &buf; 23 | let document = bson::Document::from_reader(&mut x).map_err(|err| { 24 | js_sys::Error::new(&format!("error parsing document: {}", err.to_string())) 25 | })?; 26 | Ok(JsValue::from_serde(&document).map_err(|err| { 27 | js_sys::Error::new(&format!( 28 | "error serializing document to json: {}", 29 | err.to_string() 30 | )) 31 | })?) 32 | } 33 | -------------------------------------------------------------------------------- /bson/types.ts: -------------------------------------------------------------------------------- 1 | export interface ObjectID { 2 | $oid: string; 3 | } 4 | export interface DateTime { 5 | $date: { 6 | $numberLong: string; 7 | }; 8 | } 9 | export interface Double { 10 | $numberDouble: string; 11 | } 12 | export interface Int32 { 13 | $numberInt: string; 14 | } 15 | export interface Int64 { 16 | $numberLong: string; 17 | } 18 | export interface RegularExpression { 19 | $regularExpression: { 20 | pattern: string; 21 | options: string; 22 | }; 23 | } 24 | export interface Timestamp { 25 | $timestamp: { 26 | t: number; 27 | i: number; 28 | }; 29 | } 30 | export enum BinarySubtype { 31 | Generic = "00", 32 | Function = "01", 33 | _Binary = "02", 34 | _UUID = "03", 35 | UUID = "04", 36 | MD5 = "05", 37 | Encrypted = "06", 38 | UserDefined = "80", 39 | } 40 | export interface Binary { 41 | $binary: { 42 | base64: string; 43 | subType: BinarySubtype; 44 | }; 45 | } 46 | export interface MaxKey { 47 | $maxKey: 1; 48 | } 49 | export interface MinKey { 50 | $minKey: 1; 51 | } 52 | export type BsonField = 53 | | BsonObject 54 | | ObjectID 55 | | DateTime 56 | | Double 57 | | Int32 58 | | Int64 59 | | RegularExpression 60 | | Timestamp 61 | | Binary 62 | | MaxKey 63 | | MinKey 64 | | number 65 | | Date 66 | | string; 67 | export interface BsonObject { 68 | [key: string]: BsonField; 69 | } 70 | export type Document = Uint8Array; 71 | -------------------------------------------------------------------------------- /bson/src/encoder/number.rs: -------------------------------------------------------------------------------- 1 | use wasm_bindgen::JsValue; 2 | 3 | use crate::Result; 4 | 5 | /// Safely extract a JsValue into a rust String 6 | fn extract_string(target: &JsValue) -> Result { 7 | target 8 | .as_string() 9 | .ok_or_else(|| "failed to extract string value".into()) 10 | } 11 | 12 | /// Parse a string JsValue into an i64 13 | pub(crate) fn long(target: &JsValue) -> Result { 14 | let n = extract_string(target)?; 15 | let n = n 16 | .parse::() 17 | .map_err(|err| format!("error converting $numberLong value: {}", err.to_string()))?; 18 | Ok(n) 19 | } 20 | 21 | /// Parse a string JsValue into an i32 22 | pub(crate) fn int(target: &JsValue) -> Result { 23 | let n = extract_string(target)?; 24 | let n = n 25 | .parse::() 26 | .map_err(|err| format!("error converting $numberInt value: {}", err.to_string()))?; 27 | Ok(n) 28 | } 29 | 30 | /// Parse a string JsValue into an f64 31 | pub(crate) fn double(target: &JsValue) -> Result { 32 | let n = extract_string(target)?; 33 | match n.as_str() { 34 | "Infinity" => Ok(f64::INFINITY), 35 | "-Infinity" => Ok(f64::NEG_INFINITY), 36 | _ => { 37 | let n = n.parse::().map_err(|err| { 38 | format!("error converting $numberDouble value: {}", err.to_string()) 39 | })?; 40 | Ok(n) 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /bson/bench/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | base64-js@^1.0.2: 6 | version "1.3.1" 7 | resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1" 8 | integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g== 9 | 10 | bson@4.1.0: 11 | version "4.1.0" 12 | resolved "https://registry.yarnpkg.com/bson/-/bson-4.1.0.tgz#8135dbaaf90d78d0ee4675f31600162d4c12be28" 13 | integrity sha512-xwNzRRsK2xmHvHuPESi0zVfgsm4edO47WdulNaShTriunNUMRDOAlKwpJc8zpkOXczIzbxUD7kFzhUGVYoZLDw== 14 | dependencies: 15 | buffer "^5.1.0" 16 | long "^4.0.0" 17 | 18 | buffer@^5.1.0: 19 | version "5.6.0" 20 | resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.6.0.tgz#a31749dc7d81d84db08abf937b6b8c4033f62786" 21 | integrity sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw== 22 | dependencies: 23 | base64-js "^1.0.2" 24 | ieee754 "^1.1.4" 25 | 26 | ieee754@^1.1.4: 27 | version "1.1.13" 28 | resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84" 29 | integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg== 30 | 31 | long@^4.0.0: 32 | version "4.0.0" 33 | resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" 34 | integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== 35 | -------------------------------------------------------------------------------- /bson/scripts/generate.ts: -------------------------------------------------------------------------------- 1 | /// wasm-bindgen -> deno (without --allow-read) 2 | /// Generate a single javascript file that handles wasm loading from a const var 3 | 4 | const configuration = { 5 | in: { 6 | dir: "build", 7 | wasm: "mango_bson_bg.wasm", 8 | glue: "mango_bson.js", 9 | }, 10 | out: { 11 | dir: "wasm", 12 | file: "mango_bson.js", 13 | }, 14 | }; 15 | 16 | // ... do the magic ... 17 | 18 | import { basename, join } from "https://deno.land/std@0.74.0/path/mod.ts"; 19 | import { encode } from "https://deno.land/std@0.74.0/encoding/base64.ts"; 20 | import { minify } from "https://jspm.dev/terser@5.3.8"; 21 | 22 | configuration.in.wasm = join(configuration.in.dir, configuration.in.wasm); 23 | configuration.in.glue = join(configuration.in.dir, configuration.in.glue); 24 | configuration.out.file = join(configuration.out.dir, configuration.out.file); 25 | 26 | const wasmsrc = await Deno.readFile(configuration.in.wasm); 27 | const gluesrc = await Deno.readTextFile(configuration.in.glue); 28 | 29 | const wasmencoded = encode(wasmsrc); 30 | 31 | const target = `const file = new URL(import.meta.url).pathname; 32 | const wasmFile = file.substring(0, file.lastIndexOf(Deno.build.os === 'windows' ? '\\\\' : '/') + 1) + '${ 33 | basename(configuration.in.wasm) 34 | }'; 35 | const wasmModule = new WebAssembly.Module(Deno.readFileSync(wasmFile));`; 36 | 37 | const replace = 38 | `import { decode } from "https://deno.land/std@0.74.0/encoding/base64.ts"; 39 | const encoded = "${wasmencoded}"; 40 | const source = decode(encoded); 41 | const wasmModule = new WebAssembly.Module(source);`; 42 | 43 | const data = gluesrc.replace(target, replace); 44 | 45 | const output = await minify(data, { 46 | mangle: { module: true }, 47 | output: { 48 | preamble: "//deno-fmt-ignore-file", 49 | }, 50 | }); 51 | 52 | await Deno.writeTextFile(configuration.out.file, output.code); 53 | -------------------------------------------------------------------------------- /tmp/app.ts: -------------------------------------------------------------------------------- 1 | const conn = await Deno.connect({ 2 | hostname: "0.0.0.0", // or "mango_mongo" if this script is ran inside a container 3 | port: 27017 4 | }) 5 | 6 | async function listen () { 7 | (async () => { 8 | try { 9 | for await (const chunk of Deno.iter(conn)) { 10 | console.log('got msg') 11 | const response = new TextDecoder().decode(chunk) 12 | console.log(response) 13 | } 14 | } catch (e) { 15 | console.log('got error') 16 | console.error(e.stack); 17 | } 18 | })(); 19 | } 20 | 21 | await listen() 22 | 23 | // see https://docs.mongodb.com/manual/reference/mongodb-wire-protocol/#standard-message-header 24 | type MsgHeader = { 25 | messageLength: number // total message size, including this 26 | requestID: number // Identifier for this message 27 | opCode: number // Request type, see https://docs.mongodb.com/manual/reference/mongodb-wire-protocol/#request-opcodes 28 | } 29 | type ResponseHeader = MsgHeader & { 30 | responseTo: number // requestId from the original request (used in response from db) 31 | } 32 | // Base type for OP's, to reuse between all actual OP's. Not official 33 | type OP_BASE = { 34 | header: MsgHeader, 35 | flags: number, 36 | fullCollectionName: string // "." 37 | } 38 | // TODO 39 | type document = {} 40 | // see https://docs.mongodb.com/manual/reference/mongodb-wire-protocol/#op-query 41 | type OP_QUERY = OP_BASE & { 42 | numberToSkip: number // Number of documents to skip 43 | numberToReturn: number // Number of documents to return 44 | query: document // query object, see link above 45 | returnFieldsSelector?: document // Optional. Selector indicating the fields to return. See link above for more details 46 | } 47 | // TODO :: add all other `OP_*` 48 | // ... 49 | const query: OP_QUERY = { 50 | header: { 51 | messageLength: 0, 52 | requestID: 1, 53 | opCode: 2004 54 | }, 55 | flags: 5, 56 | fullCollectionName: "juanportal.profiles", 57 | numberToSkip: 0, 58 | numberToReturn: 1, 59 | query: "" // TODO 60 | } 61 | 62 | //await conn.write(new TextEncoder().encode(JSON.stringify(query))) 63 | -------------------------------------------------------------------------------- /protocol/mod.ts: -------------------------------------------------------------------------------- 1 | import { 2 | assert, 3 | assertEquals, 4 | BufReader, 5 | Deferred, 6 | deferred, 7 | } from "../deps.ts"; 8 | import { MessageHeader, parseHeader, serializeHeader } from "./header.ts"; 9 | import { parseOpMsg, Section, serializeOpMsg } from "./op_msg.ts"; 10 | 11 | export class MongoProtocol { 12 | #reader: BufReader; 13 | #socket: Deno.Writer & Deno.Closer; 14 | #requestIDCounter = 0; 15 | #ops: Record> = {}; 16 | #polling = false; 17 | 18 | constructor(socket: Deno.Reader & Deno.Writer & Deno.Closer) { 19 | this.#reader = new BufReader(socket); 20 | this.#socket = socket; 21 | } 22 | 23 | private async send(header: MessageHeader, body: Uint8Array) { 24 | const buf = serializeHeader(header); 25 | await Deno.writeAll(this.#socket, buf); 26 | await Deno.writeAll(this.#socket, body); 27 | } 28 | 29 | private async wake() { 30 | if (this.#polling) return; 31 | await this.receiveLoop(); 32 | } 33 | 34 | private async receiveLoop() { 35 | this.#polling = true; 36 | while (Object.keys(this.#ops).length > 0) { 37 | const headerBuf = await this.#reader.readFull(new Uint8Array(16)); 38 | assert(headerBuf); 39 | const header = parseHeader(headerBuf); 40 | 41 | const buf = await this.#reader.readFull( 42 | new Uint8Array(header.messageLength - 16), 43 | ); 44 | assert(buf); 45 | 46 | this.#ops[header.responseTo].resolve([header, buf]); 47 | delete this.#ops[header.responseTo]; 48 | } 49 | this.#polling = false; 50 | } 51 | 52 | private async receive(reqID: number): Promise<[MessageHeader, Uint8Array]> { 53 | this.#ops[reqID] = deferred(); 54 | this.wake(); 55 | const resp = await this.#ops[reqID]; 56 | return resp; 57 | } 58 | 59 | private nextRequestID(): number { 60 | this.#requestIDCounter++; 61 | return this.#requestIDCounter; 62 | } 63 | 64 | async executeOpMsg(sections: Section[]): Promise { 65 | const reqID = this.nextRequestID(); 66 | const op = serializeOpMsg({ 67 | sections, 68 | }); 69 | await this.send({ 70 | messageLength: 16 + op.byteLength, 71 | opCode: 2013, 72 | requestID: reqID, 73 | responseTo: 0, 74 | }, op); 75 | const [header, buf] = await this.receive(reqID); 76 | assertEquals(header.opCode, 2013); 77 | 78 | const res = parseOpMsg(buf); 79 | 80 | return res.sections; 81 | } 82 | 83 | close() { 84 | this.#socket.close(); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /bson/src/encoder/object.rs: -------------------------------------------------------------------------------- 1 | use bson::{Bson, Document}; 2 | use chrono::prelude::*; 3 | use wasm_bindgen::{JsCast, JsValue}; 4 | 5 | use super::{extended, value}; 6 | use crate::Result; 7 | 8 | /// Inspect an object JsValue, taking into account default javascript values 9 | pub fn inspect(target: &JsValue) -> Result { 10 | if let Some(date) = target.dyn_ref::() { 11 | // Date 12 | let ms: f64 = date.get_time(); // [ms] 13 | let secs = (ms / 1e3).floor() as i64; // [s] 14 | let nsecs = ((ms % 1e3) * 1e6).ceil() as u32; // [ns] 15 | let date = chrono::Utc.timestamp(secs, nsecs); 16 | return Ok(Bson::DateTime(date)); 17 | } else if let Some(iterable) = target.dyn_ref::() { 18 | // Array 19 | let mut array = vec![]; 20 | for x in iterable.iter() { 21 | array.push(value::inspect(&x)?) 22 | } 23 | return Ok(Bson::Array(array)); 24 | } else if let Some(iterable) = target.dyn_ref::() { 25 | // Set 26 | let mut array = vec![]; 27 | for x in iterable.keys() { 28 | let x = x?; 29 | array.push(value::inspect(&x)?) 30 | } 31 | return Ok(Bson::Array(array)); 32 | } else if let Some(map) = target.dyn_ref::() { 33 | // Map 34 | let mut document = Document::default(); 35 | for key in map.keys() { 36 | let key = key?; 37 | let val = map.get(&key); 38 | let key = key 39 | .as_string() 40 | .ok_or_else(|| "only object can be serialized to bson documents")?; 41 | document.insert(key, value::inspect(&val)?); 42 | } 43 | return Ok(Bson::Document(document)); 44 | } else if let Some(ext) = extended::inspect(target)? { 45 | // { $type: ... } objects 46 | return Ok(ext); 47 | } 48 | 49 | // Plain JS object 50 | return Ok(Bson::Document(create_document(target)?)); 51 | } 52 | 53 | // Create a BSON decument from a pure javascript object 54 | pub fn create_document(target: &JsValue) -> Result { 55 | let mut document = Document::default(); 56 | let keys = js_sys::Reflect::own_keys(target)?; 57 | for key in keys.iter() { 58 | let val = js_sys::Reflect::get(target, &key)?; 59 | let key = key 60 | .as_string() 61 | .ok_or_else(|| "failed to extract object key")?; 62 | document.insert(key, value::inspect(&val)?); 63 | } 64 | Ok(document) 65 | } 66 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | Mango Logo 3 |

Mango

4 |

5 |

A MongoDB driver for Deno.

6 |

7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |

20 | 21 | --- 22 | 23 | --- 24 | 25 | > ⚠️ Work in progress. Expect breaking changes. 26 | 27 | --- 28 | 29 | ## Table of Contents 30 | - [Quick Start](#quick-start) 31 | - [Documentation](#documentation) 32 | - [Features](#features) 33 | - [Maintainers](#maintainers) 34 | - [Other](#other) 35 | - [Related](#related) 36 | - [Contribution](#contribution) 37 | - [Licence](#licence) 38 | 39 | ## Quick Start 40 | 41 | Subject to change, here as a placeholder mainly. In progress 42 | 43 | ```typescript 44 | import { Mango } from "https://deno.land/x/mango/mod.ts" 45 | 46 | const client = new Mango({ 47 | // ... configs 48 | }) 49 | 50 | const db = await client.connect() 51 | 52 | const profileCollection = db.collection("profiles") 53 | 54 | const profiles = await profileCollection.find({}) 55 | ``` 56 | 57 | ## Documentation 58 | 59 | In progress 60 | 61 | Link to doc.deno.land api documentation, or github pages, or add documentation here 62 | 63 | ## Features 64 | 65 | In progress 66 | 67 | - [x] Connecting to the database 68 | - [ ] Find documents for a given collection 69 | - [ ] ... 70 | 71 | ## Maintainers 72 | 73 | - Filippo Rossi ([@qu4k](https://github.com/qu4k)) 74 | - Edward Bebbington ([@ebebbington](https://github.com/ebebbington)) 75 | - Luca Casonato ([@lucacasonato](https://github.com/lucacasonato)) 76 | 77 | ## Other 78 | 79 | ### Related 80 | 81 | - [bson](https://github.com/mongodb/bson-rust) - rust bson crate 82 | 83 | ### Contribution 84 | 85 | Pull request, issues and feedback are very welcome. Code style is formatted with `deno fmt` and `cargo fmt` and commit messages are done following Conventional Commits spec. 86 | 87 | ### Licence 88 | 89 | Copyright 2020, the denodrivers team. All rights reserved. MIT license. 90 | -------------------------------------------------------------------------------- /bson/mod.ts: -------------------------------------------------------------------------------- 1 | import { 2 | from_bson_document as fromBsonDocument, 3 | to_bson_document as toBsonDocument, 4 | } from "./wasm/mango_bson.js"; 5 | 6 | import * as types from "./types.ts"; 7 | 8 | import { BinarySubtype } from "./types.ts"; 9 | export { BinarySubtype, types }; 10 | 11 | export function ObjectID(oid: string): types.ObjectID { 12 | return { $oid: oid }; 13 | } 14 | 15 | export function DateTime(ms: number): types.DateTime { 16 | return { $date: { $numberLong: String(ms) } }; 17 | } 18 | 19 | export function Double(val: number): types.Double { 20 | return { $numberDouble: String(val) }; 21 | } 22 | 23 | export function Int32(val: number): types.Int32 { 24 | return { $numberInt: String(val) }; 25 | } 26 | 27 | export function Int64(val: number): types.Int64 { 28 | return { $numberLong: String(val) }; 29 | } 30 | 31 | export function Regex(val: RegExp): types.RegularExpression { 32 | return { $regularExpression: { pattern: val.source, options: val.flags } }; 33 | } 34 | 35 | export function Timestamp(t: number, i: number): types.Timestamp { 36 | return { $timestamp: { t, i } }; 37 | } 38 | 39 | export function Binary( 40 | payload: Uint8Array, 41 | subType: types.BinarySubtype, 42 | ): types.Binary { 43 | const output: string = Array.from(payload) 44 | .map((val): string => String.fromCharCode(val)) 45 | .join(""); 46 | const base64 = btoa(output); 47 | return { $binary: { base64, subType } }; 48 | } 49 | 50 | export function MaxKey(): types.MaxKey { 51 | return { $maxKey: 1 }; 52 | } 53 | 54 | export function MinKey(): types.MinKey { 55 | return { $minKey: 1 }; 56 | } 57 | 58 | export function encode(object: types.BsonObject): Uint8Array { 59 | return toBsonDocument(object); 60 | } 61 | 62 | export function encodeDocuments(object: types.BsonObject[]): Uint8Array { 63 | const buffers = object.map(toBsonDocument); 64 | let total = 0; 65 | for (const buffer of buffers) { 66 | total += buffer.byteLength; 67 | } 68 | const final = new Uint8Array(total); 69 | let seek = 0; 70 | for (const buffer of buffers) { 71 | final.set(buffer, seek); 72 | seek += buffer.byteLength; 73 | } 74 | return final; 75 | } 76 | 77 | export function decode(buf: Uint8Array): types.BsonObject { 78 | // TODO(lucacasonato): rehydrate this 79 | return fromBsonDocument(buf); 80 | } 81 | 82 | /** 83 | * This function decodes all of the documents in the Uint8Array. 84 | */ 85 | export function decodeDocuments(buf: Uint8Array): types.BsonObject[] { 86 | const view = new DataView(buf.buffer); 87 | let seek = 0; 88 | const documents = []; 89 | while (seek < buf.length) { 90 | const len = view.getInt32(seek, true); 91 | documents.push(decode(buf.subarray(seek, seek + len + 1))); 92 | seek += len; 93 | } 94 | return documents; 95 | } 96 | -------------------------------------------------------------------------------- /protocol/op_msg.ts: -------------------------------------------------------------------------------- 1 | import { 2 | decode, 3 | decodeDocuments, 4 | encode, 5 | encodeDocuments, 6 | types, 7 | } from "../bson/mod.ts"; 8 | import { assertEquals } from "../deps.ts"; 9 | 10 | export type Section = Section0 | Section1; 11 | 12 | const encoder = new TextEncoder(); 13 | const decoder = new TextDecoder(); 14 | 15 | export interface OpMsg { 16 | sections: Section[]; 17 | } 18 | 19 | export function serializeOpMsg(op: OpMsg): Uint8Array { 20 | const sections = op.sections.map(serializeSection); 21 | let sectionLen = 0; 22 | for (const section of sections) { 23 | sectionLen += section.byteLength; 24 | } 25 | 26 | const len = 4 + sectionLen; 27 | 28 | const view = new DataView(new ArrayBuffer(len)); 29 | // flag bits 30 | view.setInt32(0, 0, true); 31 | let seek = 4; 32 | for (const section of sections) { 33 | new Uint8Array(view.buffer).set(section, seek); 34 | seek += section.byteLength; 35 | } 36 | return new Uint8Array(view.buffer); 37 | } 38 | 39 | function serializeSection(section: Section): Uint8Array { 40 | if (section.kind === 0) { 41 | return serializeSection0(section); 42 | } else if (section.kind === 1) { 43 | return serializeSection1(section); 44 | } 45 | throw new TypeError("Invalid section kind"); 46 | } 47 | 48 | export interface Section0 { 49 | kind: 0; 50 | body: types.BsonObject; 51 | } 52 | 53 | function serializeSection0(section: Section0): Uint8Array { 54 | const body = encode(section.body); 55 | const buf = new Uint8Array(1 + body.byteLength); 56 | buf[0] = 0; 57 | buf.set(body, 1); 58 | return buf; 59 | } 60 | 61 | export interface Section1 { 62 | kind: 1; 63 | documentSequenceIdentifier: string; 64 | objects: types.BsonObject[]; 65 | } 66 | 67 | function serializeSection1(section: Section1): Uint8Array { 68 | const documentSequenceIdentifier = encoder.encode( 69 | section.documentSequenceIdentifier, 70 | ); 71 | const objects = encodeDocuments(section.objects); 72 | const buf = new Uint8Array( 73 | 1 + 4 + documentSequenceIdentifier.byteLength + 1 + objects.byteLength, 74 | ); 75 | buf[0] = 0; 76 | new DataView(buf.buffer).setInt32( 77 | 1, 78 | 4 + documentSequenceIdentifier.byteLength + 1 + objects.byteLength, 79 | true, 80 | ); 81 | buf.set(documentSequenceIdentifier, 1 + 4); 82 | buf.set(objects, 1 + 4 + documentSequenceIdentifier.byteLength + 1); 83 | return buf; 84 | } 85 | 86 | export function parseOpMsg(buf: Uint8Array): OpMsg { 87 | const view = new DataView(buf.buffer); 88 | const flagBits = view.getInt32(0, true); 89 | assertEquals(flagBits >> 16, 0); 90 | 91 | const sections: Section[] = []; 92 | let seek = 4; 93 | while (seek < buf.byteLength) { 94 | const kind = view.getInt8(seek); 95 | seek++; 96 | if (kind === 0) { 97 | const len = view.getInt32(seek, true); 98 | sections.push({ kind: 0, body: decode(buf.slice(seek, seek + len)) }); 99 | seek += len; 100 | } else if (kind === 1) { 101 | const len = view.getInt32(seek, true); 102 | let documentSequenceIdentifierLen = 0; 103 | for (const byte of buf) { 104 | if (byte === 0) { 105 | break; 106 | } 107 | documentSequenceIdentifierLen++; 108 | } 109 | const documentSequenceIdentifier = decoder.decode( 110 | buf.subarray(4, 4 + documentSequenceIdentifierLen), 111 | ); 112 | const objects = decodeDocuments( 113 | buf.slice(4 + documentSequenceIdentifierLen + 1), 114 | ); 115 | sections.push({ kind: 1, documentSequenceIdentifier, objects }); 116 | } else { 117 | throw new Error("Invalid section kind"); 118 | } 119 | } 120 | 121 | return { sections }; 122 | } 123 | -------------------------------------------------------------------------------- /bson/src/encoder/extended.rs: -------------------------------------------------------------------------------- 1 | use bson::{oid::ObjectId, Bson}; 2 | use chrono::prelude::*; 3 | use wasm_bindgen::JsValue; 4 | 5 | use super::number; 6 | use crate::Result; 7 | 8 | /// `{“$oid”: ””}` 9 | /// : A 24-character, big-endian hexadecimal string that represents the ObjectId bytes. 10 | fn oid(target: &JsValue) -> Result { 11 | let oid = target 12 | .as_string() 13 | .ok_or_else(|| "failed to extract object id value")?; 14 | Ok(Bson::ObjectId(ObjectId::with_string(&oid).map_err( 15 | |err| format!("error in ObjectID value: {}", err.to_string()), 16 | )?)) 17 | } 18 | 19 | /// `{"$date": {"$numberLong": ""}}` 20 | /// : A 64-bit signed integer as string. The value represents milliseconds relative to the epoch. 21 | fn date(target: &JsValue) -> Result { 22 | let ms = js_sys::Reflect::get(target, &JsValue::from_str("$numberLong"))?; 23 | let ms = number::long(&ms)?; 24 | let secs = ms / 1e3 as i64; // [s] 25 | let nsecs = ((ms % 1e3 as i64) * 1e6 as i64) as u32; // [ns] 26 | let date = chrono::Utc.timestamp(secs, nsecs); 27 | Ok(Bson::DateTime(date)) 28 | } 29 | 30 | /// {"$timestamp": {"t": , "i": }} 31 | /// : A positive integer for the seconds since epoch. 32 | /// : A positive integer for the increment. 33 | fn timestamp(target: &JsValue) -> Result { 34 | let t = js_sys::Reflect::get(target, &JsValue::from_str("t"))?; 35 | let i = js_sys::Reflect::get(target, &JsValue::from_str("i"))?; 36 | let t = t.as_f64().ok_or_else(|| "invalid t in $timestamp")?; 37 | let i = i.as_f64().ok_or_else(|| "invalid i in $timestamp")?; 38 | let time = (t / 1e3) as u32; // [s] 39 | let increment = i as u32; 40 | Ok(Bson::Timestamp(bson::Timestamp { time, increment })) 41 | } 42 | 43 | /// { "$regularExpression": { "pattern": "", "options": "" } } 44 | /// : A string that corresponds to the regular expression pattern. 45 | /// The string can contain valid JSON characters and unescaped double quote (") characters, 46 | /// but may not contain unescaped forward slash (/) characters. 47 | /// : A string that specifies BSON regular expression options (‘g’, ‘i’, ‘m’ and ‘s’) or an empty string "". 48 | /// Options other than (‘g’, ‘i’, ‘m’ and ‘s’) will be dropped when converting to this representation. 49 | /// !! The options MUST be in alphabetical order. 50 | fn regex(target: &JsValue) -> Result { 51 | let pattern = js_sys::Reflect::get(target, &JsValue::from_str("pattern"))?; 52 | let options = js_sys::Reflect::get(target, &JsValue::from_str("options"))?; 53 | let pattern = pattern 54 | .as_string() 55 | .ok_or_else(|| "invalid pattern in $regularExpression")?; 56 | let options = options 57 | .as_string() 58 | .ok_or_else(|| "invalid options in $regularExpression")?; 59 | 60 | // sort options... 61 | let mut chars = options.chars().collect::>(); 62 | chars.sort_by(|a, b| a.cmp(b)); 63 | let s = chars.into_iter().collect::(); 64 | let options = String::from(s.trim()); 65 | 66 | Ok(Bson::RegularExpression(bson::Regex { pattern, options })) 67 | } 68 | 69 | /// {"$binary": {"base64": , "subType": }} 70 | /// : Base64 encoded (with padding as “=”) payload string. 71 | /// : A one- or two-character hex string that corresponds to a BSON binary subtype. 72 | fn binary(target: &JsValue) -> Result { 73 | let bytes = js_sys::Reflect::get(target, &JsValue::from_str("base64"))?; 74 | let subtype = js_sys::Reflect::get(target, &JsValue::from_str("subType"))?; 75 | let bytes = bytes 76 | .as_string() 77 | .ok_or_else(|| "invalid base64 in $binary")?; 78 | let subtype = subtype 79 | .as_string() 80 | .ok_or_else(|| "invalid subType in $binary")?; 81 | let bytes = base64::decode(bytes) 82 | .map_err(|err| format!("invalid base64 in $binary: {}", err.to_string()))?; 83 | let subtype = hex::decode(subtype) 84 | .map_err(|err| format!("invalid subType in $binary: {}", err.to_string()))?; 85 | 86 | if subtype.len() == 1 { 87 | Ok(Bson::Binary(bson::Binary { 88 | bytes, 89 | subtype: subtype[0].into(), 90 | })) 91 | } else { 92 | Err("invalid subType in $binary".into()) 93 | } 94 | } 95 | 96 | /// Inspect an extended JSON JsValue 97 | /// For reference: https://docs.mongodb.com/manual/reference/mongodb-extended-json/ 98 | pub fn inspect(target: &JsValue) -> Result> { 99 | // extended JSON check (`$`) 100 | let keys = js_sys::Reflect::own_keys(target)?; 101 | let keys = keys.to_vec(); 102 | match keys.get(0) { 103 | Some(key) => { 104 | let val = js_sys::Reflect::get(target, &key)?; 105 | let key = key 106 | .as_string() 107 | .ok_or_else(|| "failed to extract object key")?; 108 | Ok(match key.as_str() { 109 | "$oid" => Some(oid(&val)?), 110 | "$date" => Some(date(&val)?), 111 | "$numberDouble" => Some(Bson::Double(number::double(&val)?)), 112 | "$numberInt" => Some(Bson::Int32(number::int(&val)?)), 113 | "$numberLong" => Some(Bson::Int64(number::long(&val)?)), 114 | "$minKey" => Some(Bson::MinKey), 115 | "$maxKey" => Some(Bson::MaxKey), 116 | "$regularExpression" => Some(regex(&val)?), 117 | "$timestamp" => Some(timestamp(&val)?), 118 | "$binary" => Some(binary(&val)?), 119 | _ => None, 120 | }) 121 | } 122 | None => Ok(None), 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /bson/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "autocfg" 5 | version = "1.0.1" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" 8 | 9 | [[package]] 10 | name = "base64" 11 | version = "0.12.3" 12 | source = "registry+https://github.com/rust-lang/crates.io-index" 13 | checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" 14 | 15 | [[package]] 16 | name = "base64" 17 | version = "0.13.0" 18 | source = "registry+https://github.com/rust-lang/crates.io-index" 19 | checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" 20 | 21 | [[package]] 22 | name = "bson" 23 | version = "1.1.0" 24 | source = "registry+https://github.com/rust-lang/crates.io-index" 25 | checksum = "c11f16001d679cb13d14b2c93c7d0fa13bb484a87c34a6c4c39707ad936499b5" 26 | dependencies = [ 27 | "base64 0.12.3", 28 | "chrono", 29 | "hex", 30 | "lazy_static", 31 | "linked-hash-map", 32 | "rand", 33 | "serde", 34 | "serde_json", 35 | ] 36 | 37 | [[package]] 38 | name = "bumpalo" 39 | version = "3.4.0" 40 | source = "registry+https://github.com/rust-lang/crates.io-index" 41 | checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820" 42 | 43 | [[package]] 44 | name = "cfg-if" 45 | version = "0.1.10" 46 | source = "registry+https://github.com/rust-lang/crates.io-index" 47 | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 48 | 49 | [[package]] 50 | name = "chrono" 51 | version = "0.4.19" 52 | source = "registry+https://github.com/rust-lang/crates.io-index" 53 | checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" 54 | dependencies = [ 55 | "libc", 56 | "num-integer", 57 | "num-traits", 58 | "time", 59 | "winapi", 60 | ] 61 | 62 | [[package]] 63 | name = "getrandom" 64 | version = "0.1.15" 65 | source = "registry+https://github.com/rust-lang/crates.io-index" 66 | checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6" 67 | dependencies = [ 68 | "cfg-if", 69 | "libc", 70 | "wasi 0.9.0+wasi-snapshot-preview1", 71 | ] 72 | 73 | [[package]] 74 | name = "hashbrown" 75 | version = "0.9.1" 76 | source = "registry+https://github.com/rust-lang/crates.io-index" 77 | checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" 78 | 79 | [[package]] 80 | name = "hex" 81 | version = "0.4.2" 82 | source = "registry+https://github.com/rust-lang/crates.io-index" 83 | checksum = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35" 84 | 85 | [[package]] 86 | name = "indexmap" 87 | version = "1.6.0" 88 | source = "registry+https://github.com/rust-lang/crates.io-index" 89 | checksum = "55e2e4c765aa53a0424761bf9f41aa7a6ac1efa87238f59560640e27fca028f2" 90 | dependencies = [ 91 | "autocfg", 92 | "hashbrown", 93 | ] 94 | 95 | [[package]] 96 | name = "itoa" 97 | version = "0.4.6" 98 | source = "registry+https://github.com/rust-lang/crates.io-index" 99 | checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" 100 | 101 | [[package]] 102 | name = "js-sys" 103 | version = "0.3.45" 104 | source = "registry+https://github.com/rust-lang/crates.io-index" 105 | checksum = "ca059e81d9486668f12d455a4ea6daa600bd408134cd17e3d3fb5a32d1f016f8" 106 | dependencies = [ 107 | "wasm-bindgen", 108 | ] 109 | 110 | [[package]] 111 | name = "lazy_static" 112 | version = "1.4.0" 113 | source = "registry+https://github.com/rust-lang/crates.io-index" 114 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 115 | 116 | [[package]] 117 | name = "libc" 118 | version = "0.2.79" 119 | source = "registry+https://github.com/rust-lang/crates.io-index" 120 | checksum = "2448f6066e80e3bfc792e9c98bf705b4b0fc6e8ef5b43e5889aff0eaa9c58743" 121 | 122 | [[package]] 123 | name = "linked-hash-map" 124 | version = "0.5.3" 125 | source = "registry+https://github.com/rust-lang/crates.io-index" 126 | checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a" 127 | 128 | [[package]] 129 | name = "log" 130 | version = "0.4.11" 131 | source = "registry+https://github.com/rust-lang/crates.io-index" 132 | checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" 133 | dependencies = [ 134 | "cfg-if", 135 | ] 136 | 137 | [[package]] 138 | name = "mango_bson" 139 | version = "0.1.0" 140 | dependencies = [ 141 | "base64 0.13.0", 142 | "bson", 143 | "chrono", 144 | "hex", 145 | "js-sys", 146 | "serde", 147 | "serde_json", 148 | "wasm-bindgen", 149 | ] 150 | 151 | [[package]] 152 | name = "num-integer" 153 | version = "0.1.43" 154 | source = "registry+https://github.com/rust-lang/crates.io-index" 155 | checksum = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b" 156 | dependencies = [ 157 | "autocfg", 158 | "num-traits", 159 | ] 160 | 161 | [[package]] 162 | name = "num-traits" 163 | version = "0.2.12" 164 | source = "registry+https://github.com/rust-lang/crates.io-index" 165 | checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611" 166 | dependencies = [ 167 | "autocfg", 168 | ] 169 | 170 | [[package]] 171 | name = "ppv-lite86" 172 | version = "0.2.9" 173 | source = "registry+https://github.com/rust-lang/crates.io-index" 174 | checksum = "c36fa947111f5c62a733b652544dd0016a43ce89619538a8ef92724a6f501a20" 175 | 176 | [[package]] 177 | name = "proc-macro2" 178 | version = "1.0.24" 179 | source = "registry+https://github.com/rust-lang/crates.io-index" 180 | checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" 181 | dependencies = [ 182 | "unicode-xid", 183 | ] 184 | 185 | [[package]] 186 | name = "quote" 187 | version = "1.0.7" 188 | source = "registry+https://github.com/rust-lang/crates.io-index" 189 | checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" 190 | dependencies = [ 191 | "proc-macro2", 192 | ] 193 | 194 | [[package]] 195 | name = "rand" 196 | version = "0.7.3" 197 | source = "registry+https://github.com/rust-lang/crates.io-index" 198 | checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" 199 | dependencies = [ 200 | "getrandom", 201 | "libc", 202 | "rand_chacha", 203 | "rand_core", 204 | "rand_hc", 205 | ] 206 | 207 | [[package]] 208 | name = "rand_chacha" 209 | version = "0.2.2" 210 | source = "registry+https://github.com/rust-lang/crates.io-index" 211 | checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" 212 | dependencies = [ 213 | "ppv-lite86", 214 | "rand_core", 215 | ] 216 | 217 | [[package]] 218 | name = "rand_core" 219 | version = "0.5.1" 220 | source = "registry+https://github.com/rust-lang/crates.io-index" 221 | checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" 222 | dependencies = [ 223 | "getrandom", 224 | ] 225 | 226 | [[package]] 227 | name = "rand_hc" 228 | version = "0.2.0" 229 | source = "registry+https://github.com/rust-lang/crates.io-index" 230 | checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" 231 | dependencies = [ 232 | "rand_core", 233 | ] 234 | 235 | [[package]] 236 | name = "ryu" 237 | version = "1.0.5" 238 | source = "registry+https://github.com/rust-lang/crates.io-index" 239 | checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" 240 | 241 | [[package]] 242 | name = "serde" 243 | version = "1.0.117" 244 | source = "registry+https://github.com/rust-lang/crates.io-index" 245 | checksum = "b88fa983de7720629c9387e9f517353ed404164b1e482c970a90c1a4aaf7dc1a" 246 | dependencies = [ 247 | "serde_derive", 248 | ] 249 | 250 | [[package]] 251 | name = "serde_derive" 252 | version = "1.0.117" 253 | source = "registry+https://github.com/rust-lang/crates.io-index" 254 | checksum = "cbd1ae72adb44aab48f325a02444a5fc079349a8d804c1fc922aed3f7454c74e" 255 | dependencies = [ 256 | "proc-macro2", 257 | "quote", 258 | "syn", 259 | ] 260 | 261 | [[package]] 262 | name = "serde_json" 263 | version = "1.0.59" 264 | source = "registry+https://github.com/rust-lang/crates.io-index" 265 | checksum = "dcac07dbffa1c65e7f816ab9eba78eb142c6d44410f4eeba1e26e4f5dfa56b95" 266 | dependencies = [ 267 | "indexmap", 268 | "itoa", 269 | "ryu", 270 | "serde", 271 | ] 272 | 273 | [[package]] 274 | name = "syn" 275 | version = "1.0.46" 276 | source = "registry+https://github.com/rust-lang/crates.io-index" 277 | checksum = "5ad5de3220ea04da322618ded2c42233d02baca219d6f160a3e9c87cda16c942" 278 | dependencies = [ 279 | "proc-macro2", 280 | "quote", 281 | "unicode-xid", 282 | ] 283 | 284 | [[package]] 285 | name = "time" 286 | version = "0.1.44" 287 | source = "registry+https://github.com/rust-lang/crates.io-index" 288 | checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" 289 | dependencies = [ 290 | "libc", 291 | "wasi 0.10.0+wasi-snapshot-preview1", 292 | "winapi", 293 | ] 294 | 295 | [[package]] 296 | name = "unicode-xid" 297 | version = "0.2.1" 298 | source = "registry+https://github.com/rust-lang/crates.io-index" 299 | checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" 300 | 301 | [[package]] 302 | name = "wasi" 303 | version = "0.9.0+wasi-snapshot-preview1" 304 | source = "registry+https://github.com/rust-lang/crates.io-index" 305 | checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" 306 | 307 | [[package]] 308 | name = "wasi" 309 | version = "0.10.0+wasi-snapshot-preview1" 310 | source = "registry+https://github.com/rust-lang/crates.io-index" 311 | checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" 312 | 313 | [[package]] 314 | name = "wasm-bindgen" 315 | version = "0.2.68" 316 | source = "registry+https://github.com/rust-lang/crates.io-index" 317 | checksum = "1ac64ead5ea5f05873d7c12b545865ca2b8d28adfc50a49b84770a3a97265d42" 318 | dependencies = [ 319 | "cfg-if", 320 | "serde", 321 | "serde_json", 322 | "wasm-bindgen-macro", 323 | ] 324 | 325 | [[package]] 326 | name = "wasm-bindgen-backend" 327 | version = "0.2.68" 328 | source = "registry+https://github.com/rust-lang/crates.io-index" 329 | checksum = "f22b422e2a757c35a73774860af8e112bff612ce6cb604224e8e47641a9e4f68" 330 | dependencies = [ 331 | "bumpalo", 332 | "lazy_static", 333 | "log", 334 | "proc-macro2", 335 | "quote", 336 | "syn", 337 | "wasm-bindgen-shared", 338 | ] 339 | 340 | [[package]] 341 | name = "wasm-bindgen-macro" 342 | version = "0.2.68" 343 | source = "registry+https://github.com/rust-lang/crates.io-index" 344 | checksum = "6b13312a745c08c469f0b292dd2fcd6411dba5f7160f593da6ef69b64e407038" 345 | dependencies = [ 346 | "quote", 347 | "wasm-bindgen-macro-support", 348 | ] 349 | 350 | [[package]] 351 | name = "wasm-bindgen-macro-support" 352 | version = "0.2.68" 353 | source = "registry+https://github.com/rust-lang/crates.io-index" 354 | checksum = "f249f06ef7ee334cc3b8ff031bfc11ec99d00f34d86da7498396dc1e3b1498fe" 355 | dependencies = [ 356 | "proc-macro2", 357 | "quote", 358 | "syn", 359 | "wasm-bindgen-backend", 360 | "wasm-bindgen-shared", 361 | ] 362 | 363 | [[package]] 364 | name = "wasm-bindgen-shared" 365 | version = "0.2.68" 366 | source = "registry+https://github.com/rust-lang/crates.io-index" 367 | checksum = "1d649a3145108d7d3fbcde896a468d1bd636791823c9921135218ad89be08307" 368 | 369 | [[package]] 370 | name = "winapi" 371 | version = "0.3.9" 372 | source = "registry+https://github.com/rust-lang/crates.io-index" 373 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 374 | dependencies = [ 375 | "winapi-i686-pc-windows-gnu", 376 | "winapi-x86_64-pc-windows-gnu", 377 | ] 378 | 379 | [[package]] 380 | name = "winapi-i686-pc-windows-gnu" 381 | version = "0.4.0" 382 | source = "registry+https://github.com/rust-lang/crates.io-index" 383 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 384 | 385 | [[package]] 386 | name = "winapi-x86_64-pc-windows-gnu" 387 | version = "0.4.0" 388 | source = "registry+https://github.com/rust-lang/crates.io-index" 389 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 390 | --------------------------------------------------------------------------------