use super::order::{Anchor, PositionConflict};
use super::same::holds;
use crate::codec::schema::field;
use crate::codec::{ConflictRec, Rec, Val};
use crate::identity::Key;
pub(super) struct Host<'a> {
pub(super) prefix: String,
pub(super) conflicts: &'a mut Vec<ConflictRec>,
}
impl<'a> Host<'a> {
pub(super) const fn root(conflicts: &'a mut Vec<ConflictRec>) -> Self {
Host {
prefix: String::new(),
conflicts,
}
}
pub(super) fn path(&self, name: &str) -> String {
format!("{}{name}", self.prefix)
}
pub(super) fn nest(&mut self, name: &str) -> Host<'_> {
Host {
prefix: format!("{}{name}.", self.prefix),
conflicts: self.conflicts,
}
}
pub(super) fn conflict(&mut self, name: &str, sides: [Option<&Val>; 3]) {
let path = self.path(name);
self.conflicts.push(conflict(&path, sides));
}
}
pub(super) fn conflict(path: &str, sides: [Option<&Val>; 3]) -> ConflictRec {
ConflictRec::new(path, sides.map(|side| super::same::present(side).cloned()))
}
#[derive(Debug, Clone, Copy)]
pub(super) enum Side {
Ours,
Theirs,
}
impl Side {
pub(super) const fn other(self) -> Self {
match self {
Self::Ours => Self::Theirs,
Self::Theirs => Self::Ours,
}
}
}
pub(super) fn record_conflict(record: &Rec, deleter: Side) -> ConflictRec {
let key = record.get(field::KEY);
let null = Val::Null {
position: record.position,
};
let sides = match deleter {
Side::Ours => [key, Some(&null), key],
Side::Theirs => [key, key, Some(&null)],
};
conflict(field::RECORD, sides)
}
pub(super) fn parent_conflict(sides: [Option<Val>; 3]) -> ConflictRec {
ConflictRec::new(field::PARENT, sides)
}
pub(super) fn position_conflict(position: &PositionConflict<Key>) -> ConflictRec {
let anchor = |anchor: &Option<Anchor<Key>>| {
anchor.as_ref().map(|anchor| match anchor {
Anchor::First => Val::scalar("first"),
Anchor::After(key) => Val::scalar(key),
})
};
let sides = [&position.base, &position.ours, &position.theirs].map(anchor);
ConflictRec::new(field::POSITION, sides)
}
pub(super) fn hold(record: &mut Rec, conflict: ConflictRec) {
if !holds(record, &conflict) {
record.conflicts.push(conflict);
}
}