mongodb 0.2.8

A native MongoDB driver developed under MongoDB labs.
Documentation
use json::FromValueResult;

use mongodb::common::ReadPreference;
use mongodb::topology::TopologyType;

use serde_json::{self, Value};

use std::fs::File;
use std::str::FromStr;

use super::server::Server;
use super::topology_description::TopologyDescription;

pub struct Suite {
    pub in_latency_window: Vec<Server>,
    pub write: bool,
    pub read_preference: ReadPreference,
    pub suitable_servers: Vec<Server>,
    pub topology_description: TopologyDescription,
}

fn get_server_array(vals: &[Value]) -> Result<Vec<Server>, String> {
    let mut servers = Vec::new();

    for json in vals.iter() {
        match *json {
            Value::Object(ref obj) => {
                match Server::from_json(obj) {
                    Ok(server) => servers.push(server),
                    Err(err) => return Err(err),
                }
            }
            _ => return Err(String::from("Some servers could not be parsed for topology")),
        }
    }

    Ok(servers)
}

pub trait SuiteContainer: Sized {
    fn from_file(path: &str) -> Result<Self, String>;
    fn get_suite(&self) -> Result<Suite, String>;
}

impl SuiteContainer for Value {
    fn from_file(path: &str) -> Result<Value, String> {
        let mut file = File::open(path).expect(&format!("Unable to open file: {}", path));
        Ok(serde_json::from_reader(&mut file).expect(&format!("Invalid JSON file: {}", path)))
    }

    fn get_suite(&self) -> Result<Suite, String> {
        let object = val_or_err!(*self,
                                 Value::Object(ref object) => object.clone(),
                                 "`get_suite` requires a JSON object");

        let operation = val_or_err!(object.get("operation"),
                                    Some(&Value::String(ref s)) => s.to_owned(),
                                    "suite requires an operation string.");

        let write = operation == "write";

        let read_preference = val_or_err!(object.get("read_preference"),
                                          Some(&Value::Object(ref object)) =>
                                          try!(ReadPreference::from_json(object)),
                                          "suite requires a read_preference object.");

        let in_latency_window = val_or_err!(object.get("in_latency_window"),
                                           Some(&Value::Array(ref array)) =>
                                           try!(get_server_array(array)),
                                           "suite requires an in_latency_window array.");

        let suitable_servers = val_or_err!(object.get("suitable_servers"),
                                           Some(&Value::Array(ref array)) =>
                                           try!(get_server_array(array)),
                                           "suite requires a suitable_servers array.");

        let topology_obj = val_or_err!(object.get("topology_description"),
                                       Some(&Value::Object(ref obj)) => obj,
                                       "suite requires a topology_description object.");

        let top_servers = val_or_err!(topology_obj.get("servers"),
                                      Some(&Value::Array(ref array)) =>
                                      try!(get_server_array(array)),
                                      "topology requires an array of servers.");

        let ttype = val_or_err!(topology_obj.get("type"),
                                Some(&Value::String(ref s)) => TopologyType::from_str(s).unwrap(),
                                "topology requires a type");

        Ok(Suite {
            in_latency_window: in_latency_window,
            write: write,
            read_preference: read_preference,
            suitable_servers: suitable_servers,
            topology_description: TopologyDescription::new(top_servers, ttype),
        })
    }
}