mongodb 0.9.1

The official MongoDB driver for Rust (currently in alpha)
Documentation
use std::{any::Any, fmt::Debug};

use bson::{Bson, Document};

use crate::bson_util;

pub trait Matchable: Sized + 'static {
    fn is_placeholder(&self) -> bool {
        false
    }

    fn content_matches(&self, expected: &Self) -> bool;

    fn matches<T: Matchable + Any>(&self, expected: &T) -> bool {
        if expected.is_placeholder() {
            return true;
        }
        if let Some(expected) = Any::downcast_ref::<Self>(expected) {
            self.content_matches(expected)
        } else {
            false
        }
    }
}

impl Matchable for Bson {
    fn is_placeholder(&self) -> bool {
        if let Bson::String(string) = self {
            string.as_str() == "42" || string.as_str() == ""
        } else {
            get_int(self) == Some(42)
        }
    }

    fn content_matches(&self, expected: &Bson) -> bool {
        match (self, expected) {
            (Bson::Document(actual_doc), Bson::Document(expected_doc)) => {
                actual_doc.matches(expected_doc)
            }
            (Bson::Array(actual_array), Bson::Array(expected_array)) => {
                for (i, expected_element) in expected_array.iter().enumerate() {
                    if actual_array.len() <= i || !actual_array[i].matches(expected_element) {
                        return false;
                    }
                }
                true
            }
            _ => match (bson_util::get_int(self), get_int(expected)) {
                (Some(actual_int), Some(expected_int)) => actual_int == expected_int,
                (None, Some(_)) => false,
                _ => self == expected,
            },
        }
    }
}

impl Matchable for Document {
    fn content_matches(&self, expected: &Document) -> bool {
        for (k, v) in expected.iter() {
            if let Some(actual_v) = self.get(k) {
                if !actual_v.matches(v) {
                    return false;
                }
            } else {
                return false;
            }
        }
        true
    }
}

pub fn assert_matches<A: Matchable + Debug, E: Matchable + Debug>(actual: &A, expected: &E) {
    assert!(
        actual.matches(expected),
        "\n{:?}\n did not MATCH \n{:?}",
        actual,
        expected
    );
}

fn parse_i64_ext_json(doc: &Document) -> Option<i64> {
    let number_string = doc.get("$numberLong").and_then(Bson::as_str)?;
    number_string.parse::<i64>().ok()
}

fn get_int(value: &Bson) -> Option<i64> {
    bson_util::get_int(value).or_else(|| value.as_document().and_then(parse_i64_ext_json))
}