use alloc::{borrow::Cow, vec::Vec};
use crate::{param::VcardParam, prop::VcardProp, tree::merge::instance::Instance};
pub(super) struct Matching {
pub(super) pairs: Vec<(usize, usize)>,
pub(super) added: Vec<usize>,
pub(super) removed: Vec<usize>,
}
impl Matching {
pub(super) fn new(base: &[Instance<'_>], side: &[Instance<'_>]) -> Self {
let mut keys: Vec<&str> = Vec::new();
for instance in base.iter().chain(side) {
if !keys.contains(&instance.key.as_str()) {
keys.push(&instance.key);
}
}
let mut matching = Self {
pairs: Vec::new(),
added: Vec::new(),
removed: Vec::new(),
};
for key in keys {
let mut pairing = Pairing::new(base, side, key);
pairing.pair_by(|b, s| {
base[b].prop.shares_pid(&side[s].prop) && base[b].prop_eq(&side[s])
});
pairing.pair_by(|b, s| base[b].prop.shares_pid(&side[s].prop));
pairing
.pair_by(|b, s| base[b].identity.is_some() && base[b].identity == side[s].identity);
pairing.pair_by(|b, s| base[b].line_eq(&side[s]));
pairing.pair_by(|b, s| base[b].prop_eq(&side[s]));
pairing.pair_by_position();
matching.pairs.append(&mut pairing.pairs);
matching.removed.append(&mut pairing.base_free);
matching.added.append(&mut pairing.side_free);
}
matching
}
}
struct Pairing<'i, 'a> {
base: &'i [Instance<'a>],
side: &'i [Instance<'a>],
base_free: Vec<usize>,
side_free: Vec<usize>,
pairs: Vec<(usize, usize)>,
}
impl<'i, 'a> Pairing<'i, 'a> {
fn new(base: &'i [Instance<'a>], side: &'i [Instance<'a>], key: &str) -> Self {
let indices = |instances: &[Instance<'a>]| -> Vec<usize> {
instances
.iter()
.enumerate()
.filter(|(_, instance)| instance.key == key)
.map(|(at, _)| at)
.collect()
};
Self {
base,
side,
base_free: indices(base),
side_free: indices(side),
pairs: Vec::new(),
}
}
fn pair_by(&mut self, matches: impl Fn(usize, usize) -> bool) {
let mut b = 0;
while b < self.base_free.len() {
let base = self.base_free[b];
match self.side_free.iter().position(|&s| matches(base, s)) {
Some(s) => self
.pairs
.push((self.base_free.remove(b), self.side_free.remove(s))),
None => b += 1,
}
}
}
fn pair_by_position(&mut self) {
let (base, side) = (self.base, self.side);
let mut b = 0;
while b < self.base_free.len() {
if base[self.base_free[b]].identity.is_some() {
b += 1;
continue;
}
match self
.side_free
.iter()
.position(|&s| side[s].identity.is_none())
{
Some(s) => self
.pairs
.push((self.base_free.remove(b), self.side_free.remove(s))),
None => break,
}
}
}
}
impl<'a> VcardProp<'a> {
pub(super) fn shares_pid(&self, other: &Self) -> bool {
match (self.pids(), other.pids()) {
(Some(ours), Some(theirs)) => ours.iter().any(|pid| theirs.contains(pid)),
_ => false,
}
}
fn pids(&self) -> Option<&[Cow<'a, str>]> {
self.params.iter().find_map(|param| match param {
VcardParam::Pid(values) => Some(values.as_slice()),
_ => None,
})
}
}