use crate::Lens;
use crate::asymmetric::{Complement, get, put};
use crate::error::LawViolation;
use panproto_inst::WInstance;
pub fn check_laws(lens: &Lens, instance: &WInstance) -> Result<(), LawViolation> {
let (view, complement) = get(lens, instance).map_err(LawViolation::Error)?;
let restored = put(lens, &view, &complement).map_err(LawViolation::Error)?;
if !instances_equivalent(instance, &restored) {
return Err(LawViolation::GetPut {
detail: format!(
"original has {} nodes and {} arcs, restored has {} nodes and {} arcs",
instance.node_count(),
instance.arc_count(),
restored.node_count(),
restored.arc_count(),
),
});
}
check_put_get_with_view(lens, &view, &complement)?;
let modified_view = modify_leaf_values(&view);
if !instances_equivalent(&view, &modified_view) {
check_put_get_with_view(lens, &modified_view, &complement)?;
}
Ok(())
}
fn instances_equivalent(a: &WInstance, b: &WInstance) -> bool {
if a.root != b.root || a.schema_root != b.schema_root {
return false;
}
if a.node_count() != b.node_count() || a.arc_count() != b.arc_count() {
return false;
}
for (&id, node_a) in &a.nodes {
match b.nodes.get(&id) {
Some(node_b) => {
if node_a.anchor != node_b.anchor {
return false;
}
if node_a.value != node_b.value {
return false;
}
}
None => return false,
}
}
true
}
pub fn check_get_put(lens: &Lens, instance: &WInstance) -> Result<(), LawViolation> {
let (view, complement) = get(lens, instance).map_err(LawViolation::Error)?;
let restored = put(lens, &view, &complement).map_err(LawViolation::Error)?;
if !instances_equivalent(instance, &restored) {
return Err(LawViolation::GetPut {
detail: format!(
"original has {} nodes, restored has {} nodes",
instance.node_count(),
restored.node_count(),
),
});
}
Ok(())
}
pub fn check_put_get(lens: &Lens, instance: &WInstance) -> Result<(), LawViolation> {
let (view, complement) = get(lens, instance).map_err(LawViolation::Error)?;
check_put_get_with_view(lens, &view, &complement)?;
let modified_view = modify_leaf_values(&view);
if !instances_equivalent(&view, &modified_view) {
check_put_get_with_view(lens, &modified_view, &complement)?;
}
Ok(())
}
fn check_put_get_with_view(
lens: &Lens,
view: &WInstance,
complement: &Complement,
) -> Result<(), LawViolation> {
let restored = put(lens, view, complement).map_err(LawViolation::Error)?;
let (view2, _) = get(lens, &restored).map_err(LawViolation::Error)?;
if !instances_equivalent(view, &view2) {
return Err(LawViolation::PutGet {
detail: format!(
"view has {} nodes, re-get has {} nodes",
view.node_count(),
view2.node_count(),
),
});
}
Ok(())
}
fn modify_leaf_values(instance: &WInstance) -> WInstance {
use panproto_inst::value::{FieldPresence, Value};
let mut modified = instance.clone();
for node in modified.nodes.values_mut() {
if let Some(FieldPresence::Present(Value::Str(ref mut s))) = node.value {
s.push_str("_modified");
}
}
modified
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tests::{identity_lens, three_node_instance, three_node_schema};
#[test]
fn identity_lens_satisfies_laws() {
let schema = three_node_schema();
let lens = identity_lens(&schema);
let instance = three_node_instance();
let result = check_laws(&lens, &instance);
assert!(
result.is_ok(),
"identity lens should satisfy all laws: {result:?}"
);
}
#[test]
fn identity_lens_satisfies_get_put() {
let schema = three_node_schema();
let lens = identity_lens(&schema);
let instance = three_node_instance();
let result = check_get_put(&lens, &instance);
assert!(result.is_ok(), "identity lens should satisfy GetPut");
}
#[test]
fn identity_lens_satisfies_put_get() {
let schema = three_node_schema();
let lens = identity_lens(&schema);
let instance = three_node_instance();
let result = check_put_get(&lens, &instance);
assert!(result.is_ok(), "identity lens should satisfy PutGet");
}
}