use serde::{Deserialize, Serialize};
use crate::address::ContentAddress;
use crate::codec::{self, CodecError};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum GeneratorAction {
Functor {
map_object: Vec<(String, String)>,
map_morphism: Vec<(String, String)>,
},
NaturalTransformation {
components: Vec<(String, String)>,
},
Lens {
view: String,
get: String,
put: String,
},
Adjunction {
left_map_object: Vec<(String, String)>,
right_map_object: Vec<(String, String)>,
unit: Vec<(String, String)>,
counit: Vec<(String, String)>,
},
}
impl GeneratorAction {
fn canonicalize(&mut self) {
fn sort(v: &mut Vec<(String, String)>) {
v.sort();
v.dedup();
}
match self {
GeneratorAction::Functor {
map_object,
map_morphism,
} => {
sort(map_object);
sort(map_morphism);
}
GeneratorAction::NaturalTransformation { components } => sort(components),
GeneratorAction::Lens { .. } => {}
GeneratorAction::Adjunction {
left_map_object,
right_map_object,
unit,
counit,
} => {
sort(left_map_object);
sort(right_map_object);
sort(unit);
sort(counit);
}
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Connection {
pub kind: String,
pub source: String,
pub target: String,
pub action: GeneratorAction,
pub laws: Vec<String>,
}
impl Connection {
pub fn address(&self) -> Result<ContentAddress, CodecError> {
let mut canon = self.clone();
canon.action.canonicalize();
canon.laws.sort();
canon.laws.dedup();
codec::address_of(&canon)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn functor() -> Connection {
Connection {
kind: "FullyFaithful".into(),
source: "OntologyArchive".into(),
target: "PraxisKnowledgeGraph".into(),
action: GeneratorAction::Functor {
map_object: vec![("ContentAddressableNode".into(), "ConceptNode".into())],
map_morphism: vec![("Subsumption".into(), "RelationEdge".into())],
},
laws: vec!["PreservesComposition".into(), "PreservesIdentity".into()],
}
}
#[test]
fn identical_connections_share_an_address() {
assert_eq!(functor().address().unwrap(), functor().address().unwrap());
}
#[test]
fn changing_the_action_changes_the_address() {
let mut b = functor();
if let GeneratorAction::Functor { map_object, .. } = &mut b.action {
map_object.push(("MerkleEdge".into(), "RelationEdge".into()));
}
assert_ne!(functor().address().unwrap(), b.address().unwrap());
}
#[test]
fn action_table_order_does_not_affect_address() {
let mut a = functor();
let mut b = functor();
if let GeneratorAction::Functor { map_object, .. } = &mut a.action {
map_object.push(("A".into(), "X".into()));
map_object.push(("B".into(), "Y".into()));
}
if let GeneratorAction::Functor { map_object, .. } = &mut b.action {
map_object.push(("B".into(), "Y".into())); map_object.push(("A".into(), "X".into()));
}
assert_eq!(a.address().unwrap(), b.address().unwrap());
}
#[test]
fn distinct_families_get_distinct_addresses() {
let mut l = functor();
l.action = GeneratorAction::Lens {
view: "Source".into(),
get: "parse".into(),
put: "generate".into(),
};
assert_ne!(functor().address().unwrap(), l.address().unwrap());
}
#[test]
fn changing_the_kind_changes_the_address() {
let mut b = functor();
b.kind = "Faithful".into();
assert_ne!(functor().address().unwrap(), b.address().unwrap());
}
#[test]
fn every_family_addresses() {
for action in [
GeneratorAction::Functor {
map_object: vec![("a".into(), "b".into())],
map_morphism: vec![],
},
GeneratorAction::NaturalTransformation {
components: vec![("x".into(), "eta_x".into())],
},
GeneratorAction::Lens {
view: "v".into(),
get: "g".into(),
put: "p".into(),
},
GeneratorAction::Adjunction {
left_map_object: vec![],
right_map_object: vec![],
unit: vec![],
counit: vec![],
},
] {
let mut c = functor();
c.action = action;
assert!(c.address().is_ok());
}
}
}