use pact_models::matchingrules::MatchingRuleCategory;
use pact_models::path_exp::DocPath;
use std::borrow::Cow;
use std::collections::HashMap as Map;
use std::iter::FromIterator;
use super::Pattern;
#[derive(Debug)]
pub enum JsonPattern {
Json(serde_json::Value),
Array(Vec<JsonPattern>),
Object(Map<String, JsonPattern>),
Pattern(Box<dyn Pattern<Matches = serde_json::Value>>),
}
impl JsonPattern {
pub fn null() -> JsonPattern {
JsonPattern::Json(serde_json::Value::Null)
}
pub fn pattern<P>(pattern: P) -> JsonPattern
where
P: Pattern<Matches = serde_json::Value> + 'static,
{
JsonPattern::Pattern(Box::new(pattern))
}
}
impl Pattern for JsonPattern {
type Matches = serde_json::Value;
fn to_example(&self) -> serde_json::Value {
match *self {
JsonPattern::Json(ref json) => json.to_owned(),
JsonPattern::Array(ref arr) => {
serde_json::Value::Array(arr.iter().map(|v| v.to_example()).collect())
}
JsonPattern::Object(ref obj) => {
let fields = obj.iter().map(|(k, v)| (k.to_owned(), v.to_example()));
serde_json::Value::Object(serde_json::Map::from_iter(fields))
}
JsonPattern::Pattern(ref pattern) => pattern.to_example(),
}
}
fn to_example_bytes(&self) -> Vec<u8> {
let json = self.to_example();
let s = json.as_str().unwrap_or_default();
s.as_bytes().to_vec()
}
fn extract_matching_rules(&self, path: DocPath, rules_out: &mut MatchingRuleCategory) {
match *self {
JsonPattern::Json(_) => {}
JsonPattern::Array(ref arr) => {
for (i, val) in arr.iter().enumerate() {
let mut val_path = path.clone();
val_path.push_index(i);
val.extract_matching_rules(val_path, rules_out);
}
}
JsonPattern::Object(ref obj) => {
for (key, val) in obj {
let mut val_path = path.clone();
val_path.push_field(key);
val.extract_matching_rules(val_path, rules_out);
}
}
JsonPattern::Pattern(ref pattern) => {
pattern.extract_matching_rules(path, rules_out);
}
}
}
}
#[test]
fn json_pattern_is_pattern() {
use env_logger;
use maplit::*;
use pact_matching::s;
use serde_json::*;
use super::special_rules::Like;
let _ = env_logger::builder().is_test(true).try_init();
let pattern = json_pattern!({
"json": 1,
"simple": Like::new(json_pattern!("a")),
"array": [Like::new(json_pattern!("b"))],
});
let expected_json = json!({
"json": 1,
"simple": "a",
"array": ["b"],
});
assert_eq!(pattern.to_example(), expected_json);
let expected_rules = hashmap!(
s!("$.body.simple") => json!({ "match": "type" }),
s!("$.body.array[0]") => json!({ "match": "type" })
);
let mut rules = MatchingRuleCategory::empty("body");
pattern.extract_matching_rules(DocPath::root(), &mut rules);
assert_eq!(rules.to_v2_json(), expected_rules);
}
macro_rules! impl_from_for_json {
( $($t:ty),* ) => {
$(
impl From<$t> for JsonPattern {
fn from(n: $t) -> Self {
JsonPattern::Json(n.into())
}
}
)*
}
}
impl_from_for_json!(
i8, i16, i32, i64, isize, u8, u16, u32, u64, usize, f32, f64,
bool, String, serde_json::Map<String, serde_json::Value>
);
impl<'a> From<&'a str> for JsonPattern {
fn from(s: &'a str) -> Self {
JsonPattern::Json(s.into())
}
}
impl<'a> From<Cow<'a, str>> for JsonPattern {
fn from(s: Cow<'a, str>) -> Self {
JsonPattern::Json(s.into())
}
}
impl<T: Into<JsonPattern>> From<Vec<T>> for JsonPattern {
fn from(arr: Vec<T>) -> Self {
JsonPattern::Array(arr.into_iter().map(|v| v.into()).collect())
}
}
impl<'a, T: Clone + Into<JsonPattern>> From<&'a [T]> for JsonPattern {
fn from(arr: &'a [T]) -> Self {
JsonPattern::Array(arr.iter().map(|v| v.clone().into()).collect())
}
}
impl<T: Into<JsonPattern>> FromIterator<T> for JsonPattern {
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
JsonPattern::Array(iter.into_iter().map(|v| v.into()).collect())
}
}
impl From<Map<String, JsonPattern>> for JsonPattern {
fn from(m: Map<String, JsonPattern>) -> Self {
JsonPattern::Object(m)
}
}
impl From<serde_json::Value> for JsonPattern {
fn from(j: serde_json::Value) -> Self {
JsonPattern::Json(j)
}
}