use std::collections::HashMap;
use crate::error::ComposeError;
use crate::migration::Migration;
pub fn compose(m1: &Migration, m2: &Migration) -> Result<Migration, ComposeError> {
let mut vertex_map = HashMap::new();
for (v1, v2) in &m1.vertex_map {
if let Some(v3) = m2.vertex_map.get(v2) {
vertex_map.insert(v1.clone(), v3.clone());
}
}
let mut edge_map = HashMap::new();
for (e1, e2) in &m1.edge_map {
if let Some(e3) = m2.edge_map.get(e2) {
edge_map.insert(e1.clone(), e3.clone());
}
}
let mut hyper_edge_map = HashMap::new();
for (he1, he2) in &m1.hyper_edge_map {
if let Some(he3) = m2.hyper_edge_map.get(he2) {
hyper_edge_map.insert(he1.clone(), he3.clone());
}
}
let mut label_map = HashMap::new();
for ((he1, label1), label2) in &m1.label_map {
if let Some(he2) = m1.hyper_edge_map.get(he1) {
let key2 = (he2.clone(), label2.clone());
if let Some(label3) = m2.label_map.get(&key2) {
label_map.insert((he1.clone(), label1.clone()), label3.clone());
} else {
label_map.insert((he1.clone(), label1.clone()), label2.clone());
}
} else {
label_map.insert((he1.clone(), label1.clone()), label2.clone());
}
}
let mut resolver = HashMap::new();
for ((src, tgt), edge) in &m1.resolver {
let src3 = vertex_map.get(src).cloned().unwrap_or_else(|| src.clone());
let tgt3 = vertex_map.get(tgt).cloned().unwrap_or_else(|| tgt.clone());
if let Some(mapped_edge) = m2.edge_map.get(edge) {
resolver.insert((src3, tgt3), mapped_edge.clone());
} else {
resolver.insert((src3, tgt3), edge.clone());
}
}
for ((src, tgt), edge) in &m2.resolver {
let key = (src.clone(), tgt.clone());
resolver.entry(key).or_insert_with(|| edge.clone());
}
let mut hyper_resolver = HashMap::new();
for (key, value) in &m1.hyper_resolver {
hyper_resolver.insert(key.clone(), value.clone());
}
for ((he_id, labels), (tgt_he, label_remap)) in &m2.hyper_resolver {
let src_he_id = m1
.hyper_edge_map
.iter()
.find(|(_, v)| *v == he_id)
.map_or_else(|| he_id.clone(), |(k, _)| k.clone());
let remapped_labels: Vec<String> = labels
.iter()
.map(|l| {
m1.vertex_map
.iter()
.find(|(_, v)| *v == l)
.map_or_else(|| l.clone(), |(k, _)| k.clone())
})
.collect();
let key = (src_he_id, remapped_labels);
hyper_resolver
.entry(key)
.or_insert_with(|| (tgt_he.clone(), label_remap.clone()));
}
Ok(Migration {
vertex_map,
edge_map,
hyper_edge_map,
label_map,
resolver,
hyper_resolver,
})
}
#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
use super::*;
use panproto_schema::Edge;
#[test]
fn compose_identity_identity_is_identity() {
let edge = Edge {
src: "a".into(),
tgt: "b".into(),
kind: "prop".into(),
name: Some("x".into()),
};
let id_mig = Migration::identity(&["a".into(), "b".into()], std::slice::from_ref(&edge));
let composed = compose(&id_mig, &id_mig);
assert!(composed.is_ok());
let c = composed.unwrap_or_else(|_| panic!("compose should succeed"));
assert_eq!(c.vertex_map.get("a"), Some(&"a".to_string()));
assert_eq!(c.vertex_map.get("b"), Some(&"b".to_string()));
assert_eq!(c.edge_map.get(&edge), Some(&edge));
}
}