use std::hash::Hash;
use derive_where::derive_where;
use thiserror::Error;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Error)]
#[error("Vertices are not equivalent")]
pub struct NotEquivalent;
pub trait EquivalenceResolver<N, E> {
type MergeMapping;
type DedupKey: Eq + Hash;
fn id(&self) -> String;
fn dedup_key(&self, value: &N, incoming_edges: &[&E]) -> Self::DedupKey;
fn try_merge_mapping(
&self,
a_value: &N,
a_incoming_edges: &[&E],
b_value: &N,
b_incoming_edges: &[&E],
) -> Result<Self::MergeMapping, NotEquivalent>;
fn move_edge_source(&self, mapping: &Self::MergeMapping, edge: &E) -> E;
}
#[derive_where(Debug, Clone, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(bound = ""))]
pub struct ResolverId<N, E, R> {
id: String,
#[cfg_attr(feature = "serde", serde(skip))]
_marker: std::marker::PhantomData<(N, E, R)>,
}
impl<'r, N, E, R: EquivalenceResolver<N, E>> From<&'r R> for ResolverId<N, E, R> {
fn from(value: &'r R) -> Self {
Self {
id: value.id(),
_marker: std::marker::PhantomData,
}
}
}
impl<N, E, R> From<ResolverId<N, E, R>> for String {
fn from(value: ResolverId<N, E, R>) -> Self {
value.id
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Error)]
#[error("Invalid resolver \"{0}\", expected \"{1}\"")]
pub struct InvalidResolver(pub String, pub String);
#[cfg(test)]
pub(crate) mod tests {
use super::*;
#[derive(Debug, Copy, Clone, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub(crate) struct TestResolver;
impl EquivalenceResolver<(usize, usize), usize> for TestResolver {
type DedupKey = usize;
type MergeMapping = usize;
fn id(&self) -> String {
"test_resolver".to_string()
}
fn dedup_key(&self, value: &(usize, usize), _incoming_edges: &[&usize]) -> Self::DedupKey {
value.0
}
fn try_merge_mapping(
&self,
a_value: &(usize, usize),
_a_incoming_edges: &[&usize],
b_value: &(usize, usize),
_b_incoming_edges: &[&usize],
) -> Result<Self::MergeMapping, NotEquivalent> {
if a_value.0 == b_value.0 {
Ok(b_value.1)
} else {
Err(NotEquivalent)
}
}
fn move_edge_source(&self, mapping: &Self::MergeMapping, _edge: &usize) -> usize {
*mapping
}
}
#[test]
fn test_resolver_equivalence() {
let resolver = TestResolver;
let a_value = (1, 2);
let b_value = (1, 3);
let result = resolver.try_merge_mapping(&a_value, &[], &b_value, &[]);
assert!(result.is_ok());
assert_eq!(result.unwrap(), 3);
let c_value = (2, 2);
let result = resolver.try_merge_mapping(&a_value, &[], &c_value, &[]);
assert!(result.is_err());
}
#[test]
fn test_resolver_edge_update() {
let resolver = TestResolver;
let mapping = 5;
let edge = 10;
assert_eq!(resolver.move_edge_source(&mapping, &edge), 5);
}
}