use std::collections::HashMap;
use panproto_gat::{Name, NameSite, SiteRename, TheoryMorphism};
use panproto_inst::CompiledMigration;
use panproto_schema::{Edge, Schema, SchemaMorphism};
#[must_use]
pub fn induce_schema_morphism(
theory_morph: &TheoryMorphism,
src_schema: &Schema,
) -> SchemaMorphism {
let renames = theory_morph.induce_schema_renames();
let vertex_map: HashMap<Name, Name> = src_schema
.vertices
.keys()
.map(|id| (id.clone(), id.clone()))
.collect();
let mut edge_map: HashMap<Edge, Edge> = HashMap::new();
for edge in src_schema.edges.keys() {
let mut new_edge = edge.clone();
if let Some(new_kind) = theory_morph.op_map.get(edge.kind.as_ref()) {
new_edge.kind = Name::from(&**new_kind);
}
edge_map.insert(edge.clone(), new_edge);
}
SchemaMorphism {
name: format!("induced_{}", theory_morph.name),
src_protocol: theory_morph.domain.to_string(),
tgt_protocol: theory_morph.codomain.to_string(),
vertex_map,
edge_map,
renames,
}
}
#[must_use]
pub fn induce_data_migration(
schema_morph: &SchemaMorphism,
tgt_schema: &Schema,
) -> CompiledMigration {
compile_schema_morphism(schema_morph, tgt_schema)
}
#[must_use]
fn compile_schema_morphism(
schema_morph: &SchemaMorphism,
tgt_schema: &Schema,
) -> CompiledMigration {
let surviving_verts: std::collections::HashSet<Name> =
schema_morph.vertex_map.values().cloned().collect();
let surviving_edges: std::collections::HashSet<Edge> =
schema_morph.edge_map.values().cloned().collect();
let mut vertex_remap = HashMap::new();
for (src, tgt) in &schema_morph.vertex_map {
if src != tgt {
vertex_remap.insert(src.clone(), tgt.clone());
}
}
let mut edge_remap = HashMap::new();
for (src_e, tgt_e) in &schema_morph.edge_map {
if src_e != tgt_e {
edge_remap.insert(src_e.clone(), tgt_e.clone());
}
}
let mut resolver = HashMap::new();
for edge in tgt_schema.edges.keys() {
if surviving_verts.contains(&edge.src) && surviving_verts.contains(&edge.tgt) {
resolver.insert((edge.src.clone(), edge.tgt.clone()), edge.clone());
}
}
CompiledMigration {
surviving_verts,
surviving_edges,
vertex_remap,
edge_remap,
resolver,
hyper_resolver: HashMap::new(),
field_transforms: HashMap::new(),
conditional_survival: HashMap::new(),
}
}
#[must_use]
pub fn induce_migration_from_theory(
theory_morph: &TheoryMorphism,
src_schema: &Schema,
tgt_schema: &Schema,
) -> (SchemaMorphism, CompiledMigration) {
let schema_morph = induce_schema_morphism(theory_morph, src_schema);
let compiled = induce_data_migration(&schema_morph, tgt_schema);
(schema_morph, compiled)
}
#[must_use]
pub fn theory_renames(theory_morph: &TheoryMorphism) -> Vec<SiteRename> {
theory_morph.induce_schema_renames()
}
#[must_use]
pub fn rename_affects_site(rename: &SiteRename, site: &NameSite) -> bool {
rename.site == *site
}
#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
use super::*;
use std::sync::Arc;
fn test_morphism() -> TheoryMorphism {
TheoryMorphism::new(
"test",
"ThGraph",
"ThRenamedGraph",
HashMap::from([
(Arc::from("Vertex"), Arc::from("Node")),
(Arc::from("Edge"), Arc::from("Arrow")),
]),
HashMap::from([
(Arc::from("src"), Arc::from("source")),
(Arc::from("tgt"), Arc::from("target")),
]),
)
}
fn simple_schema() -> Schema {
use panproto_schema::{Protocol, SchemaBuilder};
let protocol = Protocol {
name: "test".into(),
schema_theory: "ThGraph".into(),
instance_theory: "ThWType".into(),
edge_rules: vec![],
obj_kinds: vec!["record".into(), "string".into()],
constraint_sorts: vec![],
..Protocol::default()
};
SchemaBuilder::new(&protocol)
.vertex("root", "record", None::<&str>)
.unwrap()
.vertex("root.name", "string", None::<&str>)
.unwrap()
.edge("root", "root.name", "src", Some("name"))
.unwrap()
.build()
.unwrap()
}
#[test]
fn induce_schema_morphism_renames_edge_kinds() {
let morph = test_morphism();
let schema = simple_schema();
let schema_morph = induce_schema_morphism(&morph, &schema);
for (src_e, tgt_e) in &schema_morph.edge_map {
if src_e.kind == "src" {
assert_eq!(tgt_e.kind, "source");
}
}
}
#[test]
fn induce_schema_morphism_preserves_vertex_ids() {
let morph = test_morphism();
let schema = simple_schema();
let schema_morph = induce_schema_morphism(&morph, &schema);
for (src_id, tgt_id) in &schema_morph.vertex_map {
assert_eq!(src_id, tgt_id, "vertex IDs should be unchanged");
}
}
#[test]
fn induce_schema_morphism_records_renames() {
let morph = test_morphism();
let schema = simple_schema();
let schema_morph = induce_schema_morphism(&morph, &schema);
assert!(
!schema_morph.renames.is_empty(),
"renames should be non-empty"
);
let has_vertex_kind_rename = schema_morph
.renames
.iter()
.any(|r| r.site == NameSite::VertexKind);
let has_edge_kind_rename = schema_morph
.renames
.iter()
.any(|r| r.site == NameSite::EdgeKind);
assert!(has_vertex_kind_rename, "should have vertex kind renames");
assert!(has_edge_kind_rename, "should have edge kind renames");
}
#[test]
fn induce_data_migration_produces_compiled() {
let morph = test_morphism();
let schema = simple_schema();
let schema_morph = induce_schema_morphism(&morph, &schema);
let compiled = induce_data_migration(&schema_morph, &schema);
assert_eq!(compiled.surviving_verts.len(), schema.vertices.len());
}
}