use indexmap::IndexMap;
use crate::schema::{FieldType, Schema};
use super::prune::is_empty;
use super::signature::local_signature;
pub fn is_isomorphic(a: &Schema, b: &Schema) -> bool {
let (empty_a, empty_b) = (is_empty(a), is_empty(b));
if empty_a || empty_b {
return empty_a && empty_b;
}
let mut map_ab: IndexMap<String, String> = IndexMap::new();
let mut map_ba: IndexMap<String, String> = IndexMap::new();
walk(
a,
a.root().name.clone(),
b,
b.root().name.clone(),
&mut map_ab,
&mut map_ba,
)
}
fn walk(
a: &Schema,
na: String,
b: &Schema,
nb: String,
map_ab: &mut IndexMap<String, String>,
map_ba: &mut IndexMap<String, String>,
) -> bool {
if map_ab.contains_key(&na) || map_ba.contains_key(&nb) {
return map_ab.get(&na) == Some(&nb) && map_ba.get(&nb) == Some(&na);
}
map_ab.insert(na.clone(), nb.clone());
map_ba.insert(nb.clone(), na.clone());
let ra = a
.env()
.get(&na)
.expect("caller only walks names taken from a Schema's own root/Ref graph");
let rb = b
.env()
.get(&nb)
.expect("caller only walks names taken from a Schema's own root/Ref graph");
if local_signature(ra) != local_signature(rb) {
return false;
}
for fa in ra.fields() {
let fb = rb
.field(&fa.label)
.expect("equal local_signature guarantees the same label set on both sides");
if let (FieldType::Ref(ra_ref), FieldType::Ref(rb_ref)) = (&fa.ty, &fb.ty)
&& !walk(
a,
ra_ref.name.clone(),
b,
rb_ref.name.clone(),
map_ab,
map_ba,
)
{
return false;
}
}
true
}