use bson::{Bson, Document};
use serde_json::{self, Map, Value};
use std::fs::File;
use super::arguments::Arguments;
use super::outcome::Outcome;
pub struct Test {
pub operation: Arguments,
pub outcome: Outcome,
}
impl Test {
fn from_json(object: &Map<String, Value>) -> Result<Test, String> {
macro_rules! res_or_err {
($exp:expr) => { match $exp {
Ok(a) => a,
Err(s) => return Err(s)
}};
}
let op = val_or_err!(object.get("operation"),
Some(&Value::Object(ref obj)) => obj.clone(),
"`operation` must be an object");
let args_obj = val_or_err!(op.get("arguments"),
Some(&Value::Object(ref obj)) => obj.clone(),
"`arguments` must be an object");
let name = val_or_err!(op.get("name"),
Some(&Value::String(ref s)) => s,
"`name` must be a string");
let args = match name.as_ref() {
"aggregate" => res_or_err!(Arguments::aggregate_from_json(&args_obj)),
"count" => Arguments::count_from_json(&args_obj),
"deleteMany" => res_or_err!(Arguments::delete_from_json(&args_obj, true)),
"deleteOne" => res_or_err!(Arguments::delete_from_json(&args_obj, false)),
"distinct" => res_or_err!(Arguments::distinct_from_json(&args_obj)),
"find" => Arguments::find_from_json(&args_obj),
"findOneAndDelete" => res_or_err!(Arguments::find_one_and_delete_from_json(&args_obj)),
"findOneAndReplace" => {
res_or_err!(Arguments::find_one_and_replace_from_json(&args_obj))
}
"findOneAndUpdate" => res_or_err!(Arguments::find_one_and_update_from_json(&args_obj)),
"insertMany" => res_or_err!(Arguments::insert_many_from_json(&args_obj)),
"insertOne" => res_or_err!(Arguments::insert_one_from_json(&args_obj)),
"replaceOne" => res_or_err!(Arguments::replace_one_from_json(&args_obj)),
"updateMany" => res_or_err!(Arguments::update_from_json(&args_obj, true)),
"updateOne" => res_or_err!(Arguments::update_from_json(&args_obj, false)),
_ => return Err(String::from("Invalid operation name")),
};
let outcome_obj = val_or_err!(object.get("outcome"),
Some(&Value::Object(ref obj)) => obj.clone(),
"`outcome` must be an object");
let outcome = match Outcome::from_json(&outcome_obj) {
Ok(outcome) => outcome,
Err(s) => return Err(s),
};
Ok(Test {
operation: args,
outcome: outcome,
})
}
}
pub struct Suite {
pub data: Vec<Document>,
pub tests: Vec<Test>,
}
fn get_data(object: &Map<String, Value>) -> Result<Vec<Document>, String> {
let array = val_or_err!(object.get("data"),
Some(&Value::Array(ref arr)) => arr.clone(),
"No `data` array found");
let mut data = vec![];
for json in array {
match Bson::from(json) {
Bson::Document(doc) => data.push(doc),
_ => return Err(String::from("`data` array must contain only objects")),
}
}
Ok(data)
}
fn get_tests(object: &Map<String, Value>) -> Result<Vec<Test>, String> {
let array = val_or_err!(object.get("tests"),
Some(&Value::Array(ref array)) => array.clone(),
"No `tests` array found");
let mut tests = vec![];
for json in array {
let obj = val_or_err!(json,
Value::Object(ref obj) => obj.clone(),
"`tests` array must only contain objects");
let test = match Test::from_json(&obj) {
Ok(test) => test,
Err(s) => return Err(s),
};
tests.push(test);
}
Ok(tests)
}
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 data = try!(get_data(&object));
let tests = try!(get_tests(&object));
Ok(Suite {
data: data,
tests: tests,
})
}
}