#![warn(missing_docs)]
#[macro_export]
macro_rules! s {
($e:expr) => ($e.to_string())
}
use std::collections::HashMap;
use std::iter::FromIterator;
use onig::Regex;
use lazy_static::*;
use ansi_term::*;
use ansi_term::Colour::*;
#[macro_use] pub mod models;
mod path_exp;
mod time_utils;
mod matchers;
pub mod json;
mod xml;
use crate::models::HttpPart;
use crate::models::matchingrules::*;
use crate::models::generators::*;
use crate::matchers::*;
use serde_json::*;
fn strip_whitespace<'a, T: FromIterator<&'a str>>(val: &'a String, split_by: &'a str) -> T {
val.split(split_by).map(|v| v.trim().clone() ).collect()
}
lazy_static! {
static ref BODY_MATCHERS: [(Regex, fn(expected: &Vec<u8>, actual: &Vec<u8>, config: DiffConfig,
mismatches: &mut Vec<Mismatch>, matchers: &MatchingRules)); 3] = [
(Regex::new("application/.*json").unwrap(), json::match_json),
(Regex::new("application/json.*").unwrap(), json::match_json),
(Regex::new("application/.*xml").unwrap(), xml::match_xml)
];
}
static PARAMETERISED_HEADER_TYPES: [&'static str; 2] = ["accept", "content-type"];
#[derive(Debug, Clone)]
pub enum Mismatch {
MethodMismatch {
expected: String,
actual: String
},
PathMismatch {
expected: String,
actual: String,
mismatch: String
},
StatusMismatch {
expected: u16,
actual: u16
},
QueryMismatch {
parameter: String,
expected: String,
actual: String,
mismatch: String
},
HeaderMismatch {
key: String,
expected: String,
actual: String,
mismatch: String
},
BodyTypeMismatch {
expected: String,
actual: String
},
BodyMismatch {
path: String,
expected: Option<Vec<u8>>,
actual: Option<Vec<u8>>,
mismatch: String
}
}
impl Mismatch {
pub fn to_json(&self) -> serde_json::Value {
match self {
&Mismatch::MethodMismatch { expected: ref e, actual: ref a } => {
json!({
s!("type") : json!("MethodMismatch"),
s!("expected") : json!(e),
s!("actual") : json!(a)
})
},
&Mismatch::PathMismatch { expected: ref e, actual: ref a, mismatch: ref m } => {
json!({
s!("type") : json!("PathMismatch"),
s!("expected") : json!(e),
s!("actual") : json!(a),
s!("mismatch") : json!(m)
})
},
&Mismatch::StatusMismatch { expected: ref e, actual: ref a } => {
json!({
s!("type") : json!("StatusMismatch"),
s!("expected") : json!(e),
s!("actual") : json!(a)
})
},
&Mismatch::QueryMismatch { parameter: ref p, expected: ref e, actual: ref a, mismatch: ref m } => {
json!({
s!("type") : json!("QueryMismatch"),
s!("parameter") : json!(p),
s!("expected") : json!(e),
s!("actual") : json!(a),
s!("mismatch") : json!(m)
})
},
&Mismatch::HeaderMismatch { key: ref k, expected: ref e, actual: ref a, mismatch: ref m } => {
json!({
s!("type") : json!("HeaderMismatch"),
s!("key") : json!(k),
s!("expected") : json!(e),
s!("actual") : json!(a),
s!("mismatch") : json!(m)
})
},
&Mismatch::BodyTypeMismatch { expected: ref e, actual: ref a } => {
json!({
s!("type") : json!("BodyTypeMismatch"),
s!("expected") : json!(e),
s!("actual") : json!(a)
})
},
&Mismatch::BodyMismatch { path: ref p, expected: ref e, actual: ref a, mismatch: ref m } => {
json!({
s!("type") : json!("BodyMismatch"),
s!("path") : json!(p),
s!("expected") : match e {
&Some(ref v) => json!(v),
&None => serde_json::Value::Null
},
s!("actual") : match a {
&Some(ref v) => json!(v),
&None => serde_json::Value::Null
},
s!("mismatch") : json!(m)
})
}
}
}
pub fn mismatch_type(&self) -> String {
match *self {
Mismatch::MethodMismatch { .. } => s!("MethodMismatch"),
Mismatch::PathMismatch { .. } => s!("PathMismatch"),
Mismatch::StatusMismatch { .. } => s!("StatusMismatch"),
Mismatch::QueryMismatch { .. } => s!("QueryMismatch"),
Mismatch::HeaderMismatch { .. } => s!("HeaderMismatch"),
Mismatch::BodyTypeMismatch { .. } => s!("BodyTypeMismatch"),
Mismatch::BodyMismatch { .. } => s!("BodyMismatch")
}
}
pub fn summary(&self) -> String {
match *self {
Mismatch::MethodMismatch { expected: ref e, .. } => format!("is a {} request", e),
Mismatch::PathMismatch { expected: ref e, .. } => format!("to path '{}'", e),
Mismatch::StatusMismatch { expected: ref e, .. } => format!("has status code {}", e),
Mismatch::QueryMismatch { ref parameter, expected: ref e, .. } => format!("includes parameter '{}' with value '{}'", parameter, e),
Mismatch::HeaderMismatch { ref key, expected: ref e, .. } => format!("includes header '{}' with value '{}'", key, e),
Mismatch::BodyTypeMismatch { .. } => s!("has a matching body"),
Mismatch::BodyMismatch { .. } => s!("has a matching body")
}
}
pub fn description(&self) -> String {
match *self {
Mismatch::MethodMismatch { expected: ref e, actual: ref a } => format!("expected {} but was {}", e, a),
Mismatch::PathMismatch { ref mismatch, .. } => mismatch.clone(),
Mismatch::StatusMismatch { expected: ref e, actual: ref a } => format!("expected {} but was {}", e, a),
Mismatch::QueryMismatch { ref mismatch, .. } => mismatch.clone(),
Mismatch::HeaderMismatch { ref mismatch, .. } => mismatch.clone(),
Mismatch::BodyTypeMismatch { expected: ref e, actual: ref a } => format!("expected '{}' body but was '{}'", e, a),
Mismatch::BodyMismatch { ref path, ref mismatch, .. } => format!("{} -> {}", path, mismatch)
}
}
pub fn ansi_description(&self) -> String {
match *self {
Mismatch::MethodMismatch { expected: ref e, actual: ref a } => format!("expected {} but was {}", Red.paint(e.clone()), Green.paint(a.clone())),
Mismatch::PathMismatch { expected: ref e, actual: ref a, .. } => format!("expected '{}' but was '{}'", Red.paint(e.clone()), Green.paint(a.clone())),
Mismatch::StatusMismatch { expected: ref e, actual: ref a } => format!("expected {} but was {}", Red.paint(e.to_string()), Green.paint(a.to_string())),
Mismatch::QueryMismatch { expected: ref e, actual: ref a, parameter: ref p, .. } => format!("Expected '{}' but received '{}' for query parameter '{}'",
Red.paint(e.to_string()), Green.paint(a.to_string()), Style::new().bold().paint(p.clone())),
Mismatch::HeaderMismatch { expected: ref e, actual: ref a, key: ref k, .. } => format!("Expected header '{}' to have value '{}' but was '{}'",
Style::new().bold().paint(k.clone()), Red.paint(e.to_string()), Green.paint(a.to_string())),
Mismatch::BodyTypeMismatch { expected: ref e, actual: ref a } => format!("expected '{}' body but was '{}'", Red.paint(e.clone()), Green.paint(a.clone())),
Mismatch::BodyMismatch { ref path, ref mismatch, .. } => format!("{} -> {}", Style::new().bold().paint(path.clone()), mismatch)
}
}
}
impl PartialEq for Mismatch {
fn eq(&self, other: &Mismatch) -> bool {
match (self, other) {
(&Mismatch::MethodMismatch{ expected: ref e1, actual: ref a1 },
&Mismatch::MethodMismatch{ expected: ref e2, actual: ref a2 }) => {
e1 == e2 && a1 == a2
},
(&Mismatch::PathMismatch{ expected: ref e1, actual: ref a1, mismatch: _ },
&Mismatch::PathMismatch{ expected: ref e2, actual: ref a2, mismatch: _ }) => {
e1 == e2 && a1 == a2
},
(&Mismatch::StatusMismatch{ expected: ref e1, actual: ref a1 },
&Mismatch::StatusMismatch{ expected: ref e2, actual: ref a2 }) => {
e1 == e2 && a1 == a2
},
(&Mismatch::BodyTypeMismatch{ expected: ref e1, actual: ref a1 },
&Mismatch::BodyTypeMismatch{ expected: ref e2, actual: ref a2 }) => {
e1 == e2 && a1 == a2
},
(&Mismatch::QueryMismatch{ parameter: ref p1, expected: ref e1, actual: ref a1, mismatch: _ },
&Mismatch::QueryMismatch{ parameter: ref p2, expected: ref e2, actual: ref a2, mismatch: _ }) => {
p1 == p2 && e1 == e2 && a1 == a2
},
(&Mismatch::HeaderMismatch{ key: ref p1, expected: ref e1, actual: ref a1, mismatch: _ },
&Mismatch::HeaderMismatch{ key: ref p2, expected: ref e2, actual: ref a2, mismatch: _ }) => {
p1 == p2 && e1 == e2 && a1 == a2
},
(&Mismatch::BodyMismatch{ path: ref p1, expected: ref e1, actual: ref a1, mismatch: _ },
&Mismatch::BodyMismatch{ path: ref p2, expected: ref e2, actual: ref a2, mismatch: _ }) => {
p1 == p2 && e1 == e2 && a1 == a2
},
(_, _) => false
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum DiffConfig {
AllowUnexpectedKeys,
NoUnexpectedKeys
}
pub fn match_text(expected: &Vec<u8>, actual: &Vec<u8>, mismatches: &mut Vec<Mismatch>, matchers: &MatchingRules) {
let path = vec![s!("$")];
if matchers.matcher_is_defined("body", &path) {
if let Err(messages) = match_values("body", &path, matchers.clone(), expected, actual) {
for message in messages {
mismatches.push(Mismatch::BodyMismatch {
path: s!("$"),
expected: Some(expected.clone()),
actual: Some(actual.clone()),
mismatch: message.clone()
})
}
}
} else if expected != actual {
mismatches.push(Mismatch::BodyMismatch { path: s!("$"), expected: Some(expected.clone()),
actual: Some(actual.clone()),
mismatch: format!("Expected text '{:?}' but received '{:?}'", expected, actual) });
};
}
pub fn match_method(expected: String, actual: String, mismatches: &mut Vec<Mismatch>) {
if expected.to_lowercase() != actual.to_lowercase() {
mismatches.push(Mismatch::MethodMismatch { expected: expected, actual: actual });
}
}
pub fn match_path(expected: String, actual: String, mismatches: &mut Vec<Mismatch>,
matchers: &MatchingRules) {
let path = vec![];
let matcher_result = if matchers.matcher_is_defined("path", &path) {
matchers::match_values("path", &path, matchers.clone(), &expected, &actual)
} else {
expected.matches(&actual, &MatchingRule::Equality).map_err(|err| vec![err])
};
match matcher_result {
Err(messages) => {
for message in messages {
mismatches.push(Mismatch::PathMismatch {
expected: expected.clone(),
actual: actual.clone(), mismatch: message.clone()
})
}
},
Ok(_) => ()
}
}
fn compare_query_parameter_value(key: &String, expected: &String, actual: &String, index: usize,
mismatches: &mut Vec<Mismatch>, matchers: &MatchingRules) {
let path = vec![s!("$"), key.clone(), format!("{}", index)];
let matcher_result = if matchers.matcher_is_defined("query", &path) {
matchers::match_values("query", &path, matchers.clone(), expected, actual)
} else {
expected.matches(actual, &MatchingRule::Equality).map_err(|err| vec![err])
};
match matcher_result {
Err(messages) => {
for message in messages {
mismatches.push(Mismatch::QueryMismatch {
parameter: key.clone(),
expected: expected.clone(),
actual: actual.clone(),
mismatch: message
})
}
},
Ok(_) => ()
}
}
fn compare_query_parameter_values(key: &String, expected: &Vec<String>, actual: &Vec<String>,
mismatches: &mut Vec<Mismatch>, matchers: &MatchingRules) {
for (index, val) in expected.iter().enumerate() {
if index < actual.len() {
compare_query_parameter_value(key, val, &actual[index], index, mismatches, matchers);
} else {
mismatches.push(Mismatch::QueryMismatch { parameter: key.clone(),
expected: format!("{:?}", expected),
actual: format!("{:?}", actual),
mismatch: format!("Expected query parameter '{}' value '{}' but was missing", key, val) });
}
}
}
fn match_query_values(key: &String, expected: &Vec<String>, actual: &Vec<String>,
mismatches: &mut Vec<Mismatch>, matchers: &MatchingRules) {
if expected.is_empty() && !actual.is_empty() {
mismatches.push(Mismatch::QueryMismatch { parameter: key.clone(),
expected: format!("{:?}", expected),
actual: format!("{:?}", actual),
mismatch: format!("Expected an empty parameter list for '{}' but received {:?}", key, actual) });
} else {
if expected.len() != actual.len() {
mismatches.push(Mismatch::QueryMismatch { parameter: key.clone(),
expected: format!("{:?}", expected),
actual: format!("{:?}", actual),
mismatch: format!(
"Expected query parameter '{}' with {} value(s) but received {} value(s)",
key, expected.len(), actual.len()) });
}
compare_query_parameter_values(key, expected, actual, mismatches, matchers);
}
}
fn match_query_maps(expected: HashMap<String, Vec<String>>, actual: HashMap<String, Vec<String>>,
mismatches: &mut Vec<Mismatch>, matchers: &MatchingRules) {
for (key, value) in &expected {
match actual.get(key) {
Some(actual_value) => match_query_values(key, value, actual_value, mismatches, matchers),
None => mismatches.push(Mismatch::QueryMismatch { parameter: key.clone(),
expected: format!("{:?}", value),
actual: "".to_string(),
mismatch: format!("Expected query parameter '{}' but was missing", key) })
}
}
for (key, value) in &actual {
match expected.get(key) {
Some(_) => (),
None => mismatches.push(Mismatch::QueryMismatch { parameter: key.clone(),
expected: "".to_string(),
actual: format!("{:?}", value),
mismatch: format!("Unexpected query parameter '{}' received", key) })
}
}
}
pub fn match_query(expected: Option<HashMap<String, Vec<String>>>,
actual: Option<HashMap<String, Vec<String>>>, mismatches: &mut Vec<Mismatch>,
matchers: &MatchingRules) {
match (actual, expected) {
(Some(aqm), Some(eqm)) => match_query_maps(eqm, aqm, mismatches, matchers),
(Some(aqm), None) => for (key, value) in &aqm {
mismatches.push(Mismatch::QueryMismatch { parameter: key.clone(),
expected: "".to_string(),
actual: format!("{:?}", value),
mismatch: format!("Unexpected query parameter '{}' received", key) });
},
(None, Some(eqm)) => for (key, value) in &eqm {
mismatches.push(Mismatch::QueryMismatch { parameter: key.clone(),
expected: format!("{:?}", value),
actual: "".to_string(),
mismatch: format!("Expected query parameter '{}' but was missing", key) });
},
(None, None) => (),
};
}
fn parse_charset_parameters(parameters: &[&str]) -> HashMap<String, String> {
parameters.iter().map(|v| v.split("=").map(|p| p.trim()).collect::<Vec<&str>>())
.fold(HashMap::new(), |mut map, name_value| {
map.insert(name_value[0].to_string(), name_value[1].to_string());
map
})
}
fn match_parameter_header(expected: &String, actual: &String, mismatches: &mut Vec<Mismatch>, header: &String) {
let expected_values: Vec<&str> = strip_whitespace(expected, ";");
let actual_values: Vec<&str> = strip_whitespace(actual, ";");
let expected_parameters = expected_values.as_slice().split_first().unwrap();
let actual_parameters = actual_values.as_slice().split_first().unwrap();
let header_mismatch = Mismatch::HeaderMismatch { key: header.clone(),
expected: format!("{}", expected),
actual: format!("{}", actual),
mismatch: format!("Expected header '{}' to have value '{}' but was '{}'",
header, expected, actual) };
if expected_parameters.0 == actual_parameters.0 {
let expected_parameter_map = parse_charset_parameters(expected_parameters.1);
let actual_parameter_map = parse_charset_parameters(actual_parameters.1);
for (k, v) in expected_parameter_map {
if actual_parameter_map.contains_key(&k) {
if v != *actual_parameter_map.get(&k).unwrap() {
mismatches.push(header_mismatch.clone());
}
} else {
mismatches.push(header_mismatch.clone());
}
}
} else {
mismatches.push(header_mismatch.clone());
}
}
fn match_header_value(key: &String, expected: &String, actual: &String, mismatches: &mut Vec<Mismatch>,
matchers: &MatchingRules) {
let path = vec![s!("$"), key.clone()];
let expected = strip_whitespace::<String>(expected, ",");
let actual = strip_whitespace::<String>(actual, ",");
let matcher_result = if matchers.matcher_is_defined("header", &path) {
matchers::match_values("header",&path, matchers.clone(), &expected, &actual)
} else if PARAMETERISED_HEADER_TYPES.contains(&key.to_lowercase().as_str()) {
match_parameter_header(&expected, &actual, mismatches, &key);
Ok(())
} else {
expected.matches(&actual, &MatchingRule::Equality).map_err(|err| vec![err])
};
match matcher_result {
Err(messages) => {
for message in messages {
mismatches.push(Mismatch::HeaderMismatch {
key: key.clone(),
expected: expected.clone(),
actual: actual.clone(),
mismatch: format!("Mismatch with header '{}': {}", key.clone(), message)
})
}
},
Ok(_) => ()
}
}
fn find_entry<T>(map: &HashMap<String, T>, key: &String) -> Option<(String, T)> where T: Clone {
match map.keys().find(|k| k.to_lowercase() == key.to_lowercase() ) {
Some(k) => map.get(k).map(|v| (key.clone(), v.clone()) ),
None => None
}
}
fn match_header_maps(expected: HashMap<String, Vec<String>>, actual: HashMap<String, Vec<String>>,
mismatches: &mut Vec<Mismatch>, matchers: &MatchingRules) {
for (key, value) in &expected {
match find_entry(&actual, key) {
Some((_, actual_value)) => for (index, val) in value.iter().enumerate() {
match_header_value(key, val, actual_value.get(index).unwrap_or(&s!("")), mismatches, matchers)
},
None => mismatches.push(Mismatch::HeaderMismatch { key: key.clone(),
expected: format!("{:?}", value.join(", ")),
actual: "".to_string(),
mismatch: format!("Expected header '{}' but was missing", key) })
}
}
}
pub fn match_headers(expected: Option<HashMap<String, Vec<String>>>,
actual: Option<HashMap<String, Vec<String>>>, mismatches: &mut Vec<Mismatch>,
matchers: &MatchingRules) {
match (actual, expected) {
(Some(aqm), Some(eqm)) => match_header_maps(eqm, aqm, mismatches, matchers),
(Some(_), None) => (),
(None, Some(eqm)) => for (key, value) in &eqm {
mismatches.push(Mismatch::HeaderMismatch { key: key.clone(),
expected: format!("{:?}", value.join(", ")),
actual: "".to_string(),
mismatch: format!("Expected header '{}' but was missing", key) });
},
(None, None) => (),
};
}
fn compare_bodies(mimetype: String, expected: &Vec<u8>, actual: &Vec<u8>, config: DiffConfig,
mismatches: &mut Vec<Mismatch>, matchers: &MatchingRules) {
match BODY_MATCHERS.iter().find(|mt| mt.0.is_match(&mimetype)) {
Some(ref match_fn) => match_fn.1(expected, actual, config, mismatches, matchers),
None => match_text(expected, actual, mismatches, matchers)
}
}
fn match_body_content(content_type: String, expected: &models::OptionalBody, actual: &models::OptionalBody,
config: DiffConfig, mismatches: &mut Vec<Mismatch>, matchers: &MatchingRules) {
match (expected, actual) {
(&models::OptionalBody::Missing, _) => (),
(&models::OptionalBody::Null, &models::OptionalBody::Present(ref b)) => {
mismatches.push(Mismatch::BodyMismatch { expected: None, actual: Some(b.clone()),
mismatch: format!("Expected empty body but received '{:?}'", b.clone()),
path: s!("/")});
},
(&models::OptionalBody::Empty, &models::OptionalBody::Present(ref b)) => {
mismatches.push(Mismatch::BodyMismatch { expected: None, actual: Some(b.clone()),
mismatch: format!("Expected empty body but received '{:?}'", b.clone()),
path: s!("/")});
},
(&models::OptionalBody::Null, _) => (),
(&models::OptionalBody::Empty, _) => (),
(e, &models::OptionalBody::Missing) => {
mismatches.push(Mismatch::BodyMismatch { expected: Some(e.value()), actual: None,
mismatch: format!("Expected body '{:?}' but was missing", e.value()),
path: s!("/")});
},
(_, _) => {
compare_bodies(content_type, &expected.value(), &actual.value(),
config, mismatches, matchers);
}
}
}
pub fn match_body(expected: &dyn models::HttpPart, actual: &dyn models::HttpPart, config: DiffConfig,
mismatches: &mut Vec<Mismatch>, matchers: &MatchingRules) {
log::debug!("expected content type = '{}', actual content type = '{}'", expected.content_type(),
actual.content_type());
if expected.content_type() == actual.content_type() {
match_body_content(expected.content_type(), expected.body(), actual.body(), config, mismatches, matchers)
} else if expected.body().is_present() {
mismatches.push(Mismatch::BodyTypeMismatch { expected: expected.content_type(),
actual: actual.content_type() });
}
}
pub fn match_request(expected: models::Request, actual: models::Request) -> Vec<Mismatch> {
let mut mismatches = vec![];
log::info!("comparing to expected {}", expected);
log::debug!(" body: '{}'", expected.body.str_value());
log::debug!(" matching_rules: {:?}", expected.matching_rules);
log::debug!(" generators: {:?}", expected.generators);
match_method(expected.method.clone(), actual.method.clone(), &mut mismatches);
match_path(expected.path.clone(), actual.path.clone(), &mut mismatches, &expected.matching_rules);
match_body(&expected, &actual, DiffConfig::NoUnexpectedKeys, &mut mismatches, &expected.matching_rules);
match_query(expected.query, actual.query, &mut mismatches, &expected.matching_rules);
match_headers(expected.headers, actual.headers, &mut mismatches, &expected.matching_rules);
log::debug!("--> Mismatches: {:?}", mismatches);
mismatches
}
pub fn match_status(expected: u16, actual: u16, mismatches: &mut Vec<Mismatch>) {
if expected != actual {
mismatches.push(Mismatch::StatusMismatch { expected: expected, actual: actual });
}
}
pub fn match_response(expected: models::Response, actual: models::Response) -> Vec<Mismatch> {
let mut mismatches = vec![];
log::info!("comparing to expected response: {:?}", expected);
match_body(&expected, &actual, DiffConfig::AllowUnexpectedKeys, &mut mismatches, &expected.matching_rules);
match_status(expected.status, actual.status, &mut mismatches);
match_headers(expected.headers, actual.headers, &mut mismatches, &expected.matching_rules);
mismatches
}
pub fn match_message_contents(expected: &models::message::Message, actual: &models::message::Message, config: DiffConfig,
mismatches: &mut Vec<Mismatch>, matchers: &MatchingRules) {
if expected.mimetype() == actual.mimetype() {
match_body_content(expected.mimetype(), &expected.contents, &actual.contents, config, mismatches, matchers)
} else if expected.contents.is_present() {
mismatches.push(Mismatch::BodyTypeMismatch { expected: expected.mimetype(),
actual: actual.mimetype() });
}
}
pub fn match_message(expected: models::message::Message, actual: models::message::Message) -> Vec<Mismatch> {
let mut mismatches = vec![];
log::info!("comparing to expected message: {:?}", expected);
match_message_contents(&expected, &actual, DiffConfig::AllowUnexpectedKeys, &mut mismatches, &expected.matching_rules);
mismatches
}
pub fn generate_request(request: &models::Request, context: &HashMap<String, Value>) -> models::Request {
let generators = request.generators.clone();
let mut request = request.clone();
generators.apply_generator(&GeneratorCategory::PATH, |_, generator| {
match generator.generate_value(&request.path, context) {
Some(v) => request.path = v,
None => ()
}
});
generators.apply_generator(&GeneratorCategory::HEADER, |key, generator| {
match request.headers {
Some(ref mut headers) => if headers.contains_key(key) {
match generator.generate_value(&headers.get(key).unwrap().clone(), context) {
Some(v) => headers.insert(key.clone(), v),
None => None
};
},
None => ()
}
});
generators.apply_generator(&GeneratorCategory::QUERY, |key, generator| {
match request.query {
Some(ref mut parameters) => match parameters.get_mut(key) {
Some(parameter) => {
let mut generated = parameter.clone();
for (index, val) in parameter.iter().enumerate() {
match generator.generate_value(val, context) {
Some(v) => generated[index] = v,
None => ()
};
}
*parameter = generated;
},
None => ()
},
None => ()
}
});
request.body = generators.apply_body_generators(&request.body, request.content_type_enum(),
context);
request
}
pub fn generate_response(response: &models::Response, context: &HashMap<String, Value>) -> models::Response {
let generators = response.generators.clone();
let mut response = response.clone();
generators.apply_generator(&GeneratorCategory::STATUS, |_, generator| {
match generator.generate_value(&response.status, context) {
Some(v) => response.status = v,
None => ()
}
});
generators.apply_generator(&GeneratorCategory::HEADER, |key, generator| {
match response.headers {
Some(ref mut headers) => if headers.contains_key(key) {
match generator.generate_value(&headers.get(key).unwrap().clone(), context) {
Some(v) => headers.insert(key.clone(), v),
None => None
};
},
None => ()
}
});
response.body = generators.apply_body_generators(&response.body, response.content_type_enum(),
context);
response
}
#[cfg(test)]
mod tests;
#[cfg(test)]
mod generator_tests;