use crate::merge::array::merge_array;
use crate::merge::object::merge_object;
use crate::model::{AnyNode, NodeType, SchemaHypothesis};
use maplit::btreeset;
mod any;
mod array;
mod object;
mod object_property;
#[must_use]
pub fn merge_hypothesis(a: SchemaHypothesis, b: SchemaHypothesis) -> SchemaHypothesis {
let root = merge_node_type(a.root, b.root);
SchemaHypothesis { root }
}
pub fn merge_node_type(a: NodeType, b: NodeType) -> NodeType {
match (a, b) {
(a, b) if a == b => a,
(NodeType::Object(a), NodeType::Object(b)) => merge_object(a, b).into(),
(NodeType::Array(a), NodeType::Array(b)) => merge_array(a, b).into(),
(NodeType::Any(xs), NodeType::Any(ys)) => any::merge_any(&xs, ys),
(a @ NodeType::Any(_), b) | (b, a @ NodeType::Any(_)) => {
merge_node_type(a, AnyNode::new(btreeset![b]).into())
}
(a, b) => merge_node_type(
AnyNode::new(btreeset![a]).into(),
AnyNode::new(btreeset![b]).into(),
),
}
}
#[cfg(test)]
mod test {
use maplit::{btreemap, btreeset};
use crate::merge::{merge_hypothesis, merge_node_type};
use crate::model::{
AnyNode, ArrayNode, IntegerNode, NodeType, ObjectNode, ObjectProperty, SchemaHypothesis,
StringNode,
};
#[test]
fn test_merge_string() {
let a = SchemaHypothesis::new(StringNode::new());
let b = SchemaHypothesis::new(StringNode::new());
let actual = merge_hypothesis(a, b);
assert_eq!(actual, SchemaHypothesis::new(StringNode::new()));
}
#[test]
fn test_merge_array_without_types() {
let a = ArrayNode::new_untyped();
let b = ArrayNode::new_untyped();
assert_eq!(
merge_node_type(a.into(), b.into()),
ArrayNode::new_untyped().into()
);
}
#[test]
fn test_merge_array_with_same_types() {
let a = ArrayNode::new_many(btreeset!(IntegerNode::new().into()));
let b = ArrayNode::new_many(btreeset!(IntegerNode::new().into()));
assert_eq!(
merge_node_type(a.into(), b.into()),
ArrayNode::new_many(btreeset!(IntegerNode::new().into())).into()
);
}
#[test]
fn test_merge_array_with_one_empty_one_given() {
let a = ArrayNode::new_untyped();
let b = ArrayNode::new_many(btreeset!(IntegerNode::new().into()));
assert_eq!(
merge_node_type(a.into(), b.into()),
ArrayNode::new_many(btreeset!(IntegerNode::new().into())).into()
);
}
#[test]
fn test_merge_array_with_different_types() {
let a = ArrayNode::new_many(btreeset![
IntegerNode::new().into(),
StringNode::new().into()
])
.into();
let b = ArrayNode::new_many(btreeset![IntegerNode::new().into(), NodeType::Boolean]).into();
assert_eq!(
merge_node_type(a, b),
ArrayNode::new_many(btreeset![
IntegerNode::new().into(),
StringNode::new().into(),
NodeType::Boolean
])
.into()
);
}
#[test]
fn test_merge_array_with_objects() {
let a = ArrayNode::new_many(btreeset![ObjectNode::new(btreemap! {
"id".to_string() => ObjectProperty {
node_type: IntegerNode::new().into(),
required: true
}
})
.into()]);
let b = ArrayNode::new_many(btreeset![ObjectNode::new(btreemap! {
"name".to_string() => ObjectProperty {
node_type: StringNode::new().into(),
required: true
}
})
.into()]);
assert_eq!(
merge_node_type(a.into(), b.into()),
ArrayNode::new_many(btreeset![ObjectNode::new(btreemap! {
"id".to_string() => ObjectProperty {
node_type: IntegerNode::new().into(),
required: false
},
"name".to_string() => ObjectProperty {
node_type: StringNode::new().into(),
required: false
}
})
.into()])
.into()
);
}
#[test]
fn test_merge_object_additional_property_b() {
let a = SchemaHypothesis::new(ObjectNode::new(btreemap! {
String::from("id") => ObjectProperty::new(StringNode::new())
}));
let b = SchemaHypothesis::new(ObjectNode::new(btreemap! {
String::from("id") => ObjectProperty::new(StringNode::new()),
String::from("name") => ObjectProperty::new(StringNode::new())
}));
let actual = merge_hypothesis(a, b);
let expected = SchemaHypothesis::new(ObjectNode::new(btreemap! {
String::from("id") => ObjectProperty::new(StringNode::new()),
String::from("name") => ObjectProperty::new(StringNode::new()).optional()
}));
assert_eq!(actual, expected);
}
#[test]
fn test_merge_object_property_missing_in_b() {
let a = SchemaHypothesis::new(ObjectNode::new(btreemap! {
String::from("id") => ObjectProperty::new(StringNode::new()),
String::from("name") => ObjectProperty::new(StringNode::new())
}));
let b = SchemaHypothesis::new(ObjectNode::new(btreemap! {
String::from("id") => ObjectProperty::new(StringNode::new()),
}));
let actual = merge_hypothesis(a, b);
let expected = SchemaHypothesis::new(ObjectNode::new(btreemap! {
String::from("id") => ObjectProperty::new(StringNode::new()),
String::from("name") => ObjectProperty::new(StringNode::new()).optional()
}));
assert_eq!(actual, expected);
}
#[test]
fn test_merge_different_types() {
let a = StringNode::new().into();
let b = IntegerNode::new().into();
let actual = merge_node_type(a, b);
assert_eq!(
actual,
AnyNode::new(btreeset![
StringNode::new().into(),
IntegerNode::new().into()
])
.into()
);
}
#[test]
fn test_merge_any_and_type() {
let a = AnyNode::new(btreeset![IntegerNode::new().into()]).into();
let b = StringNode::new().into();
let actual = merge_node_type(a, b);
assert_eq!(
actual,
AnyNode::new(btreeset![
IntegerNode::new().into(),
StringNode::new().into()
])
.into()
);
}
#[test]
fn test_merge_type_and_any() {
let a = StringNode::new().into();
let b = AnyNode::new(btreeset![IntegerNode::new().into()]).into();
let actual = merge_node_type(a, b);
assert_eq!(
actual,
AnyNode::new(btreeset![
IntegerNode::new().into(),
StringNode::new().into()
])
.into()
);
}
#[test]
fn test_merge_existing_type_and_any() {
let a = AnyNode::new(btreeset![
StringNode::new().into(),
IntegerNode::new().into()
])
.into();
let b = StringNode::new().into();
let actual = merge_node_type(a, b);
assert_eq!(
actual,
AnyNode::new(btreeset![
StringNode::new().into(),
IntegerNode::new().into()
])
.into()
);
}
}