use std::collections::HashMap;
use panproto_gat::{Name, SiteRename};
use serde::{Deserialize, Serialize};
use crate::schema::Edge;
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct SchemaMorphism {
pub name: String,
pub src_protocol: String,
pub tgt_protocol: String,
pub vertex_map: HashMap<Name, Name>,
#[serde(with = "crate::serde_helpers::map_as_vec")]
pub edge_map: HashMap<Edge, Edge>,
pub renames: Vec<SiteRename>,
}
impl SchemaMorphism {
#[must_use]
pub fn compose(&self, other: &Self) -> Self {
let mut vertex_map = HashMap::new();
for (src, mid) in &self.vertex_map {
if let Some(tgt) = other.vertex_map.get(mid) {
vertex_map.insert(src.clone(), tgt.clone());
} else {
vertex_map.insert(src.clone(), mid.clone());
}
}
let mut edge_map = HashMap::new();
for (src_e, mid_e) in &self.edge_map {
if let Some(tgt_e) = other.edge_map.get(mid_e) {
edge_map.insert(src_e.clone(), tgt_e.clone());
} else {
edge_map.insert(src_e.clone(), mid_e.clone());
}
}
let mut renames = self.renames.clone();
renames.extend(other.renames.iter().cloned());
Self {
name: format!("{};{}", self.name, other.name),
src_protocol: self.src_protocol.clone(),
tgt_protocol: other.tgt_protocol.clone(),
vertex_map,
edge_map,
renames,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn compose_chains_vertex_maps() {
let m1 = SchemaMorphism {
name: "m1".into(),
src_protocol: "a".into(),
tgt_protocol: "b".into(),
vertex_map: HashMap::from([(Name::from("v1"), Name::from("v2"))]),
edge_map: HashMap::new(),
renames: vec![],
};
let m2 = SchemaMorphism {
name: "m2".into(),
src_protocol: "b".into(),
tgt_protocol: "c".into(),
vertex_map: HashMap::from([(Name::from("v2"), Name::from("v3"))]),
edge_map: HashMap::new(),
renames: vec![],
};
let composed = m1.compose(&m2);
assert_eq!(composed.vertex_map.get("v1").map(AsRef::as_ref), Some("v3"));
assert_eq!(composed.src_protocol, "a");
assert_eq!(composed.tgt_protocol, "c");
}
#[test]
fn compose_preserves_renames() {
let r1 = SiteRename::new(panproto_gat::NameSite::EdgeLabel, "a", "b");
let r2 = SiteRename::new(panproto_gat::NameSite::VertexKind, "x", "y");
let m1 = SchemaMorphism {
name: "m1".into(),
src_protocol: "p".into(),
tgt_protocol: "p".into(),
vertex_map: HashMap::new(),
edge_map: HashMap::new(),
renames: vec![r1.clone()],
};
let m2 = SchemaMorphism {
name: "m2".into(),
src_protocol: "p".into(),
tgt_protocol: "p".into(),
vertex_map: HashMap::new(),
edge_map: HashMap::new(),
renames: vec![r2.clone()],
};
let composed = m1.compose(&m2);
assert_eq!(composed.renames.len(), 2);
assert_eq!(composed.renames[0], r1);
assert_eq!(composed.renames[1], r2);
}
}