use std::{
collections::{hash_map::Entry, HashMap, HashSet, VecDeque},
fmt, mem,
};
use crate::driver::{AbortSignal, DefaultAbortSignal, DefaultDriver, Driver};
use crate::error::{ErrorKind, Result};
use crate::guid::{Guid, IsValidGuid, TAGS_GUID};
use crate::tree::{Content, MergeState, MergedNode, Node, Tree, Validity};
#[derive(Eq, PartialEq)]
enum StructureChange {
Unchanged,
Moved,
Deleted,
}
#[derive(Clone, Copy, Default, Debug, Eq, Hash, PartialEq)]
pub struct StructureCounts {
pub remote_revives: usize,
pub local_deletes: usize,
pub local_revives: usize,
pub remote_deletes: usize,
pub dupes: usize,
pub merged_nodes: usize,
pub merged_deletions: usize,
}
type MatchingDupes<'t> = (HashMap<Guid, Node<'t>>, HashMap<Guid, Node<'t>>);
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct Deletion<'t> {
pub guid: &'t Guid,
pub local_level: i64,
pub should_upload_tombstone: bool,
}
#[derive(Clone, Copy, Debug)]
enum ConflictResolution {
Local,
Remote,
Unchanged,
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
enum DupeKey<'a> {
WithoutPosition(&'a Content),
WithPosition(&'a Content, usize),
}
pub struct Merger<'t, D = DefaultDriver, A = DefaultAbortSignal> {
driver: &'t D,
signal: &'t A,
local_tree: &'t Tree,
remote_tree: &'t Tree,
matching_dupes_by_local_parent_guid: HashMap<Guid, MatchingDupes<'t>>,
merged_guids: HashSet<Guid>,
delete_locally: HashSet<Guid>,
delete_remotely: HashSet<Guid>,
structure_counts: StructureCounts,
}
impl<'t> Merger<'t, DefaultDriver, DefaultAbortSignal> {
pub fn new(local_tree: &'t Tree, remote_tree: &'t Tree) -> Merger<'t> {
Merger {
driver: &DefaultDriver,
signal: &DefaultAbortSignal,
local_tree,
remote_tree,
matching_dupes_by_local_parent_guid: HashMap::new(),
merged_guids: HashSet::new(),
delete_locally: HashSet::new(),
delete_remotely: HashSet::new(),
structure_counts: StructureCounts::default(),
}
}
}
impl<'t, D: Driver, A: AbortSignal> Merger<'t, D, A> {
pub fn with_driver(
driver: &'t D,
signal: &'t A,
local_tree: &'t Tree,
remote_tree: &'t Tree,
) -> Merger<'t, D, A> {
Merger {
driver,
signal,
local_tree,
remote_tree,
matching_dupes_by_local_parent_guid: HashMap::new(),
merged_guids: HashSet::new(),
delete_locally: HashSet::new(),
delete_remotely: HashSet::new(),
structure_counts: StructureCounts::default(),
}
}
pub fn merge(mut self) -> Result<MergedRoot<'t>> {
let merged_root_node = {
let local_root_node = self.local_tree.root();
let remote_root_node = self.remote_tree.root();
self.two_way_merge(local_root_node, remote_root_node)?
};
for guid in self.local_tree.deletions() {
self.signal.err_if_aborted()?;
if !self.mentions(guid) {
self.delete_remotely.insert(guid.clone());
self.structure_counts.merged_deletions += 1;
}
}
for guid in self.remote_tree.deletions() {
self.signal.err_if_aborted()?;
if !self.mentions(guid) {
self.delete_locally.insert(guid.clone());
self.structure_counts.merged_deletions += 1;
}
}
for guid in self.local_tree.guids() {
self.signal.err_if_aborted()?;
if !self.mentions(guid) {
return Err(ErrorKind::UnmergedLocalItems.into());
}
}
for guid in self.remote_tree.guids() {
self.signal.err_if_aborted()?;
if !self.mentions(guid) {
return Err(ErrorKind::UnmergedRemoteItems.into());
}
}
Ok(MergedRoot {
local_tree: self.local_tree,
remote_tree: self.remote_tree,
node: merged_root_node,
merged_guids: self.merged_guids,
delete_locally: self.delete_locally,
delete_remotely: self.delete_remotely,
structure_counts: self.structure_counts,
})
}
#[inline]
fn mentions(&self, guid: &Guid) -> bool {
self.merged_guids.contains(guid)
|| self.delete_locally.contains(guid)
|| self.delete_remotely.contains(guid)
}
fn merge_local_only_node(&mut self, local_node: Node<'t>) -> Result<MergedNode<'t>> {
trace!(self.driver, "Item {} only exists locally", local_node);
self.merged_guids.insert(local_node.guid.clone());
let merged_guid = if local_node.guid.is_valid_guid() {
local_node.guid.clone()
} else {
warn!(
self.driver,
"Generating new GUID for local node {}", local_node
);
self.signal.err_if_aborted()?;
let new_guid = self.driver.generate_new_guid(&local_node.guid)?;
if new_guid != local_node.guid {
if self.merged_guids.contains(&new_guid) {
return Err(ErrorKind::DuplicateItem(new_guid).into());
}
self.merged_guids.insert(new_guid.clone());
}
new_guid
};
let mut merged_node = MergedNode::new(merged_guid, MergeState::LocalOnly(local_node));
for local_child_node in local_node.children() {
self.signal.err_if_aborted()?;
self.merge_local_child_into_merged_node(
&mut merged_node,
local_node,
None,
local_child_node,
)?;
}
if local_node.diverged() {
merged_node.merge_state = merged_node.merge_state.with_new_local_structure();
}
Ok(merged_node)
}
fn merge_remote_only_node(&mut self, remote_node: Node<'t>) -> Result<MergedNode<'t>> {
trace!(self.driver, "Item {} only exists remotely", remote_node);
self.merged_guids.insert(remote_node.guid.clone());
let merged_guid = if remote_node.guid.is_valid_guid() {
remote_node.guid.clone()
} else {
warn!(
self.driver,
"Generating new GUID for remote node {}", remote_node
);
self.signal.err_if_aborted()?;
let new_guid = self.driver.generate_new_guid(&remote_node.guid)?;
if new_guid != remote_node.guid {
if self.merged_guids.contains(&new_guid) {
return Err(ErrorKind::DuplicateItem(new_guid).into());
}
self.merged_guids.insert(new_guid.clone());
self.delete_remotely.insert(remote_node.guid.clone());
self.structure_counts.merged_deletions += 1;
}
new_guid
};
let mut merged_node = MergedNode::new(merged_guid, MergeState::RemoteOnly(remote_node));
for remote_child_node in remote_node.children() {
self.signal.err_if_aborted()?;
self.merge_remote_child_into_merged_node(
&mut merged_node,
None,
remote_node,
remote_child_node,
)?;
}
if remote_node.diverged()
|| merged_node.remote_guid_changed()
|| remote_node.validity != Validity::Valid
{
merged_node.merge_state = merged_node.merge_state.with_new_remote_structure();
}
Ok(merged_node)
}
fn two_way_merge(
&mut self,
local_node: Node<'t>,
remote_node: Node<'t>,
) -> Result<MergedNode<'t>> {
trace!(
self.driver,
"Item exists locally as {} and remotely as {}",
local_node,
remote_node
);
if !local_node.has_compatible_kind(&remote_node) {
error!(
self.driver,
"Merging local {} and remote {} with different kinds", local_node, remote_node
);
return Err(ErrorKind::MismatchedItemKind(local_node.kind, remote_node.kind).into());
}
self.merged_guids.insert(local_node.guid.clone());
self.merged_guids.insert(remote_node.guid.clone());
let merged_guid = if remote_node.guid.is_valid_guid() {
remote_node.guid.clone()
} else {
warn!(
self.driver,
"Generating new valid GUID for node {}", remote_node
);
self.signal.err_if_aborted()?;
let new_guid = self.driver.generate_new_guid(&remote_node.guid)?;
if new_guid != remote_node.guid {
if self.merged_guids.contains(&new_guid) {
return Err(ErrorKind::DuplicateItem(new_guid).into());
}
self.merged_guids.insert(new_guid.clone());
self.delete_remotely.insert(remote_node.guid.clone());
self.structure_counts.merged_deletions += 1;
}
new_guid
};
let (item, children) = self.resolve_value_conflict(local_node, remote_node);
let mut merged_node = MergedNode::new(
merged_guid,
match item {
ConflictResolution::Local => MergeState::Local {
local_node,
remote_node,
},
ConflictResolution::Remote => MergeState::Remote {
local_node,
remote_node,
},
ConflictResolution::Unchanged => MergeState::Unchanged {
local_node,
remote_node,
},
},
);
match children {
ConflictResolution::Local => {
for local_child_node in local_node.children() {
self.signal.err_if_aborted()?;
self.merge_local_child_into_merged_node(
&mut merged_node,
local_node,
Some(remote_node),
local_child_node,
)?;
}
for remote_child_node in remote_node.children() {
self.signal.err_if_aborted()?;
self.merge_remote_child_into_merged_node(
&mut merged_node,
Some(local_node),
remote_node,
remote_child_node,
)?;
}
}
ConflictResolution::Remote => {
for remote_child_node in remote_node.children() {
self.signal.err_if_aborted()?;
self.merge_remote_child_into_merged_node(
&mut merged_node,
Some(local_node),
remote_node,
remote_child_node,
)?;
}
for local_child_node in local_node.children() {
self.signal.err_if_aborted()?;
self.merge_local_child_into_merged_node(
&mut merged_node,
local_node,
Some(remote_node),
local_child_node,
)?;
}
}
ConflictResolution::Unchanged => {
for (local_child_node, remote_child_node) in
local_node.children().zip(remote_node.children())
{
self.signal.err_if_aborted()?;
self.merge_unchanged_child_into_merged_node(
&mut merged_node,
local_node,
local_child_node,
remote_node,
remote_child_node,
)?;
}
}
}
if local_node.diverged() {
merged_node.merge_state = merged_node.merge_state.with_new_local_structure();
}
if remote_node.diverged() || remote_node.validity != Validity::Valid {
merged_node.merge_state = merged_node.merge_state.with_new_remote_structure();
}
Ok(merged_node)
}
fn merge_unchanged_child_into_merged_node(
&mut self,
merged_node: &mut MergedNode<'t>,
local_parent_node: Node<'t>,
local_child_node: Node<'t>,
remote_parent_node: Node<'t>,
remote_child_node: Node<'t>,
) -> Result<()> {
assert!(
!self.merged_guids.contains(&local_child_node.guid),
"Unchanged local child shouldn't have been merged"
);
assert!(
!self.merged_guids.contains(&remote_child_node.guid),
"Unchanged remote child shouldn't have been merged"
);
let local_structure_change = self.check_for_local_structure_change_of_remote_node(
merged_node,
remote_parent_node,
remote_child_node,
)?;
let remote_structure_change = self.check_for_remote_structure_change_of_local_node(
merged_node,
local_parent_node,
local_child_node,
)?;
match (local_structure_change, remote_structure_change) {
(StructureChange::Deleted, StructureChange::Deleted) => {
merged_node.merge_state = merged_node
.merge_state
.with_new_local_structure()
.with_new_remote_structure();
}
(StructureChange::Deleted, _) => {
merged_node.merge_state = merged_node.merge_state.with_new_remote_structure();
}
(_, StructureChange::Deleted) => {
merged_node.merge_state = merged_node.merge_state.with_new_local_structure();
}
(_, _) => {
let mut merged_child_node =
self.two_way_merge(local_child_node, remote_child_node)?;
if merged_child_node.local_guid_changed() {
merged_child_node.merge_state =
merged_child_node.merge_state.with_new_local_structure();
}
if merged_node.remote_guid_changed() {
merged_child_node.merge_state =
merged_child_node.merge_state.with_new_remote_structure();
}
if merged_child_node.remote_guid_changed() {
merged_node.merge_state = merged_node.merge_state.with_new_remote_structure();
}
merged_node.merged_children.push(merged_child_node);
self.structure_counts.merged_nodes += 1;
}
}
Ok(())
}
fn merge_remote_child_into_merged_node(
&mut self,
merged_node: &mut MergedNode<'t>,
local_parent_node: Option<Node<'t>>,
remote_parent_node: Node<'t>,
remote_child_node: Node<'t>,
) -> Result<()> {
if self.merged_guids.contains(&remote_child_node.guid) {
trace!(
self.driver,
"Remote child {} already seen in another folder and merged",
remote_child_node
);
merged_node.merge_state = merged_node.merge_state.with_new_remote_structure();
return Ok(());
}
trace!(
self.driver,
"Merging remote child {} of {} into {}",
remote_child_node,
remote_parent_node,
merged_node
);
if self.check_for_local_structure_change_of_remote_node(
merged_node,
remote_parent_node,
remote_child_node,
)? == StructureChange::Deleted
{
merged_node.merge_state = merged_node.merge_state.with_new_remote_structure();
return Ok(());
}
if let Some(local_child_node) = self.local_tree.node_for_guid(&remote_child_node.guid) {
let local_parent_node = local_child_node
.parent()
.expect("Can't merge existing remote child without local parent");
trace!(
self.driver,
"Remote child {} exists locally in {} and remotely in {}",
remote_child_node,
local_parent_node,
remote_parent_node
);
if self.remote_tree.is_deleted(&local_parent_node.guid) {
trace!(
self.driver,
"Unconditionally taking remote move for {} to {} because local parent {} is \
deleted remotely",
remote_child_node,
remote_parent_node,
local_parent_node
);
let mut merged_child_node =
self.two_way_merge(local_child_node, remote_child_node)?;
merged_child_node.merge_state =
merged_child_node.merge_state.with_new_local_structure();
if merged_node.remote_guid_changed() {
merged_child_node.merge_state =
merged_child_node.merge_state.with_new_remote_structure();
}
if merged_child_node.remote_guid_changed() {
merged_node.merge_state = merged_node.merge_state.with_new_remote_structure();
}
merged_node.merge_state = merged_node.merge_state.with_new_local_structure();
merged_node.merged_children.push(merged_child_node);
self.structure_counts.merged_nodes += 1;
return Ok(());
}
match self.resolve_structure_conflict(
local_parent_node,
local_child_node,
remote_parent_node,
remote_child_node,
) {
ConflictResolution::Local => {
trace!(
self.driver,
"Remote child {} moved locally to {} and remotely to {}; \
keeping child in newer local parent and position",
remote_child_node,
local_parent_node,
remote_parent_node
);
merged_node.merge_state = merged_node.merge_state.with_new_remote_structure();
}
ConflictResolution::Remote | ConflictResolution::Unchanged => {
let mut merged_child_node = if local_parent_node.guid != remote_parent_node.guid
{
trace!(
self.driver,
"Remote child {} reparented locally to {} and remotely to {}; \
keeping child in newer remote parent",
remote_child_node,
local_parent_node,
remote_parent_node
);
let mut merged_child_node =
self.two_way_merge(local_child_node, remote_child_node)?;
merged_child_node.merge_state =
merged_child_node.merge_state.with_new_local_structure();
merged_child_node
} else {
trace!(
self.driver,
"Remote child {} repositioned locally in {} and remotely in {}; \
keeping child in newer remote position",
remote_child_node,
local_parent_node,
remote_parent_node
);
self.two_way_merge(local_child_node, remote_child_node)?
};
if merged_child_node.local_guid_changed() {
merged_child_node.merge_state =
merged_child_node.merge_state.with_new_local_structure();
}
if merged_node.remote_guid_changed() {
merged_child_node.merge_state =
merged_child_node.merge_state.with_new_remote_structure();
}
if merged_child_node.remote_guid_changed() {
merged_node.merge_state =
merged_node.merge_state.with_new_remote_structure();
}
merged_node.merge_state = merged_node.merge_state.with_new_local_structure();
merged_node.merged_children.push(merged_child_node);
self.structure_counts.merged_nodes += 1;
}
}
return Ok(());
}
trace!(
self.driver,
"Remote child {} doesn't exist locally; looking for local content match",
remote_child_node
);
let mut merged_child_node = if let Some(local_child_node_by_content) = self
.find_local_node_matching_remote_node(
merged_node,
local_parent_node,
remote_parent_node,
remote_child_node,
)? {
self.two_way_merge(local_child_node_by_content, remote_child_node)
} else {
self.merge_remote_only_node(remote_child_node)
}?;
if merged_child_node.local_guid_changed() {
merged_child_node.merge_state =
merged_child_node.merge_state.with_new_local_structure();
}
if merged_node.remote_guid_changed() {
merged_child_node.merge_state =
merged_child_node.merge_state.with_new_remote_structure();
}
if merged_child_node.remote_guid_changed() {
merged_node.merge_state = merged_node.merge_state.with_new_remote_structure();
}
merged_node.merge_state = merged_node.merge_state.with_new_local_structure();
merged_node.merged_children.push(merged_child_node);
self.structure_counts.merged_nodes += 1;
Ok(())
}
fn merge_local_child_into_merged_node(
&mut self,
merged_node: &mut MergedNode<'t>,
local_parent_node: Node<'t>,
remote_parent_node: Option<Node<'t>>,
local_child_node: Node<'t>,
) -> Result<()> {
if self.merged_guids.contains(&local_child_node.guid) {
trace!(
self.driver,
"Local child {} already seen in another folder and merged",
local_child_node
);
merged_node.merge_state = merged_node.merge_state.with_new_local_structure();
return Ok(());
}
trace!(
self.driver,
"Merging local child {} of {} into {}",
local_child_node,
local_parent_node,
merged_node
);
if self.check_for_remote_structure_change_of_local_node(
merged_node,
local_parent_node,
local_child_node,
)? == StructureChange::Deleted
{
merged_node.merge_state = merged_node.merge_state.with_new_local_structure();
return Ok(());
}
if let Some(remote_child_node) = self.remote_tree.node_for_guid(&local_child_node.guid) {
let remote_parent_node = remote_child_node
.parent()
.expect("Can't merge existing local child without remote parent");
trace!(
self.driver,
"Local child {} exists locally in {} and remotely in {}",
local_child_node,
local_parent_node,
remote_parent_node
);
if self.local_tree.is_deleted(&remote_parent_node.guid) {
trace!(
self.driver,
"Unconditionally taking local move for {} to {} because remote parent {} is \
deleted locally",
local_child_node,
local_parent_node,
remote_parent_node
);
let mut merged_child_node =
self.two_way_merge(local_child_node, remote_child_node)?;
if merged_child_node.local_guid_changed() {
merged_child_node.merge_state =
merged_child_node.merge_state.with_new_local_structure();
}
merged_node.merge_state = merged_node.merge_state.with_new_remote_structure();
merged_child_node.merge_state =
merged_child_node.merge_state.with_new_remote_structure();
merged_node.merged_children.push(merged_child_node);
self.structure_counts.merged_nodes += 1;
return Ok(());
}
match self.resolve_structure_conflict(
local_parent_node,
local_child_node,
remote_parent_node,
remote_child_node,
) {
ConflictResolution::Local => {
if local_parent_node.guid != remote_parent_node.guid {
trace!(
self.driver,
"Local child {} reparented locally to {} and remotely to {}; \
keeping child in newer local parent",
local_child_node,
local_parent_node,
remote_parent_node
);
let mut merged_child_node =
self.two_way_merge(local_child_node, remote_child_node)?;
if merged_child_node.local_guid_changed() {
merged_child_node.merge_state =
merged_child_node.merge_state.with_new_local_structure();
}
merged_node.merge_state =
merged_node.merge_state.with_new_remote_structure();
merged_child_node.merge_state =
merged_child_node.merge_state.with_new_remote_structure();
merged_node.merged_children.push(merged_child_node);
self.structure_counts.merged_nodes += 1;
} else {
trace!(
self.driver,
"Local child {} repositioned locally in {} and remotely in {}; \
keeping child in newer local position",
local_child_node,
local_parent_node,
remote_parent_node
);
let mut merged_child_node =
self.two_way_merge(local_child_node, remote_child_node)?;
if merged_child_node.local_guid_changed() {
merged_child_node.merge_state =
merged_child_node.merge_state.with_new_local_structure();
}
merged_node.merge_state =
merged_node.merge_state.with_new_remote_structure();
if merged_node.remote_guid_changed() {
merged_child_node.merge_state =
merged_child_node.merge_state.with_new_remote_structure();
}
merged_node.merged_children.push(merged_child_node);
self.structure_counts.merged_nodes += 1;
}
}
ConflictResolution::Remote | ConflictResolution::Unchanged => {
if local_parent_node.guid != remote_parent_node.guid {
trace!(
self.driver,
"Local child {} reparented locally to {} and remotely to {}; \
keeping child in newer remote parent",
local_child_node,
local_parent_node,
remote_parent_node
);
} else {
trace!(
self.driver,
"Local child {} repositioned locally in {} and remotely in {}; \
keeping child in newer remote position",
local_child_node,
local_parent_node,
remote_parent_node
);
}
merged_node.merge_state = merged_node.merge_state.with_new_local_structure();
}
}
return Ok(());
}
trace!(
self.driver,
"Local child {} doesn't exist remotely; looking for remote content match",
local_child_node
);
let merged_child_node = if let Some(remote_child_node_by_content) = self
.find_remote_node_matching_local_node(
merged_node,
local_parent_node,
remote_parent_node,
local_child_node,
)? {
let mut merged_child_node =
self.two_way_merge(local_child_node, remote_child_node_by_content)?;
if merged_child_node.local_guid_changed() {
merged_child_node.merge_state =
merged_child_node.merge_state.with_new_local_structure();
}
if merged_node.remote_guid_changed() {
merged_child_node.merge_state =
merged_child_node.merge_state.with_new_remote_structure();
}
if merged_child_node.remote_guid_changed() {
merged_node.merge_state = merged_node.merge_state.with_new_remote_structure();
}
merged_node.merge_state = merged_node.merge_state.with_new_local_structure();
merged_child_node
} else {
let mut merged_child_node = self.merge_local_only_node(local_child_node)?;
if merged_child_node.local_guid_changed() {
merged_child_node.merge_state =
merged_child_node.merge_state.with_new_local_structure();
}
merged_node.merge_state = merged_node.merge_state.with_new_remote_structure();
merged_child_node.merge_state =
merged_child_node.merge_state.with_new_remote_structure();
merged_child_node
};
merged_node.merged_children.push(merged_child_node);
self.structure_counts.merged_nodes += 1;
Ok(())
}
fn resolve_value_conflict(
&self,
local_node: Node<'t>,
remote_node: Node<'t>,
) -> (ConflictResolution, ConflictResolution) {
if remote_node.is_root() {
return (ConflictResolution::Unchanged, ConflictResolution::Local);
}
match (local_node.needs_merge, remote_node.needs_merge) {
(true, true) => {
let item = if local_node.is_built_in_root() {
ConflictResolution::Local
} else {
match (local_node.validity, remote_node.validity) {
(Validity::Replace, Validity::Replace) => ConflictResolution::Unchanged,
(Validity::Replace, _) => ConflictResolution::Remote,
(_, Validity::Replace) => ConflictResolution::Local,
(_, _) => {
if local_node.age < remote_node.age {
ConflictResolution::Local
} else {
ConflictResolution::Remote
}
}
}
};
let children = if local_node.has_matching_children(remote_node) {
ConflictResolution::Unchanged
} else if local_node.age < remote_node.age {
ConflictResolution::Local
} else {
ConflictResolution::Remote
};
(item, children)
}
(true, false) => {
let item = match local_node.validity {
Validity::Valid | Validity::Reupload => ConflictResolution::Local,
Validity::Replace => ConflictResolution::Remote,
};
let children = if local_node.has_matching_children(remote_node) {
ConflictResolution::Unchanged
} else {
ConflictResolution::Local
};
(item, children)
}
(false, true) => {
let item = if local_node.is_built_in_root() {
ConflictResolution::Unchanged
} else {
match remote_node.validity {
Validity::Valid | Validity::Reupload => ConflictResolution::Remote,
Validity::Replace => ConflictResolution::Local,
}
};
let children = if local_node.has_matching_children(remote_node) {
ConflictResolution::Unchanged
} else {
ConflictResolution::Remote
};
(item, children)
}
(false, false) => {
let item = match (local_node.validity, remote_node.validity) {
(Validity::Replace, Validity::Replace) => ConflictResolution::Unchanged,
(_, Validity::Replace) => ConflictResolution::Local,
(Validity::Replace, _) => ConflictResolution::Remote,
(_, _) => ConflictResolution::Unchanged,
};
let children = if local_node.has_matching_children(remote_node) {
ConflictResolution::Unchanged
} else if local_node.age < remote_node.age {
ConflictResolution::Local
} else {
ConflictResolution::Remote
};
(item, children)
}
}
}
fn resolve_structure_conflict(
&self,
local_parent_node: Node<'t>,
local_child_node: Node<'t>,
remote_parent_node: Node<'t>,
remote_child_node: Node<'t>,
) -> ConflictResolution {
if remote_child_node.is_built_in_root() {
return ConflictResolution::Local;
}
match (
local_parent_node.needs_merge,
remote_parent_node.needs_merge,
) {
(true, true) => {
let latest_local_age = local_child_node.age.min(local_parent_node.age);
let latest_remote_age = remote_child_node.age.min(remote_parent_node.age);
if latest_local_age < latest_remote_age {
ConflictResolution::Local
} else {
ConflictResolution::Remote
}
}
(true, false) => ConflictResolution::Local,
(false, true) => ConflictResolution::Remote,
(false, false) => ConflictResolution::Unchanged,
}
}
fn check_for_local_structure_change_of_remote_node(
&mut self,
merged_node: &mut MergedNode<'t>,
remote_parent_node: Node<'t>,
remote_node: Node<'t>,
) -> Result<StructureChange> {
if !remote_node.is_syncable() {
trace!(
self.driver,
"Deleting non-syncable remote node {}",
remote_node
);
return self.delete_remote_node(merged_node, remote_node);
}
if !self.local_tree.is_deleted(&remote_node.guid) {
if let Some(local_node) = self.local_tree.node_for_guid(&remote_node.guid) {
if !local_node.is_syncable() {
trace!(
self.driver,
"Remote node {} is syncable, but local node {} isn't; deleting",
remote_node,
local_node
);
return self.delete_remote_node(merged_node, remote_node);
}
if local_node.validity == Validity::Replace
&& remote_node.validity == Validity::Replace
{
return self.delete_remote_node(merged_node, remote_node);
}
let local_parent_node = local_node
.parent()
.expect("Can't check for structure changes without local parent");
if local_parent_node.guid != remote_parent_node.guid {
return Ok(StructureChange::Moved);
}
return Ok(StructureChange::Unchanged);
}
if remote_node.validity == Validity::Replace {
return self.delete_remote_node(merged_node, remote_node);
}
return Ok(StructureChange::Unchanged);
}
if remote_node.validity == Validity::Replace {
return self.delete_remote_node(merged_node, remote_node);
}
if remote_node.is_built_in_root() {
return Ok(StructureChange::Unchanged);
}
if remote_node.needs_merge {
if !remote_node.is_folder() {
trace!(
self.driver,
"Remote non-folder {} deleted locally and changed remotely; \
taking remote change",
remote_node
);
self.structure_counts.remote_revives += 1;
return Ok(StructureChange::Unchanged);
}
trace!(
self.driver,
"Remote folder {} deleted locally and changed remotely; \
taking local deletion",
remote_node
);
self.structure_counts.local_deletes += 1;
} else {
trace!(
self.driver,
"Remote node {} deleted locally and not changed remotely; \
taking local deletion",
remote_node
);
}
self.delete_remote_node(merged_node, remote_node)
}
fn check_for_remote_structure_change_of_local_node(
&mut self,
merged_node: &mut MergedNode<'t>,
local_parent_node: Node<'t>,
local_node: Node<'t>,
) -> Result<StructureChange> {
if !local_node.is_syncable() {
trace!(
self.driver,
"Deleting non-syncable local node {}",
local_node
);
return self.delete_local_node(merged_node, local_node);
}
if !self.remote_tree.is_deleted(&local_node.guid) {
if let Some(remote_node) = self.remote_tree.node_for_guid(&local_node.guid) {
if !remote_node.is_syncable() {
trace!(
self.driver,
"Local node {} is syncable, but remote node {} isn't; deleting",
local_node,
remote_node
);
return self.delete_local_node(merged_node, local_node);
}
if remote_node.validity == Validity::Replace
&& local_node.validity == Validity::Replace
{
return self.delete_local_node(merged_node, local_node);
}
let remote_parent_node = remote_node
.parent()
.expect("Can't check for structure changes without remote parent");
if remote_parent_node.guid != local_parent_node.guid {
return Ok(StructureChange::Moved);
}
return Ok(StructureChange::Unchanged);
}
if local_node.validity == Validity::Replace {
return self.delete_local_node(merged_node, local_node);
}
return Ok(StructureChange::Unchanged);
}
if local_node.validity == Validity::Replace {
return self.delete_local_node(merged_node, local_node);
}
if local_node.is_built_in_root() {
return Ok(StructureChange::Unchanged);
}
if local_node.needs_merge {
if !local_node.is_folder() {
trace!(
self.driver,
"Local non-folder {} deleted remotely and changed locally; taking local change",
local_node
);
self.structure_counts.local_revives += 1;
return Ok(StructureChange::Unchanged);
}
trace!(
self.driver,
"Local folder {} deleted remotely and changed locally; taking remote deletion",
local_node
);
self.structure_counts.remote_deletes += 1;
} else {
trace!(
self.driver,
"Local node {} deleted remotely and not changed locally; taking remote deletion",
local_node
);
}
self.delete_local_node(merged_node, local_node)
}
fn delete_remote_node(
&mut self,
merged_node: &mut MergedNode<'t>,
remote_node: Node<'t>,
) -> Result<StructureChange> {
self.delete_remotely.insert(remote_node.guid.clone());
for remote_child_node in remote_node.children() {
self.signal.err_if_aborted()?;
if self.merged_guids.contains(&remote_child_node.guid) {
trace!(
self.driver,
"Remote child {} can't be an orphan; already merged",
remote_child_node
);
continue;
}
match self.check_for_local_structure_change_of_remote_node(
merged_node,
remote_node,
remote_child_node,
)? {
StructureChange::Moved | StructureChange::Deleted => {
continue;
}
StructureChange::Unchanged => {
trace!(
self.driver,
"Relocating remote orphan {} to {}",
remote_child_node,
merged_node
);
let mut merged_orphan_node = if let Some(local_child_node) =
self.local_tree.node_for_guid(&remote_child_node.guid)
{
self.two_way_merge(local_child_node, remote_child_node)
} else {
self.merge_remote_only_node(remote_child_node)
}?;
merged_node.merge_state = merged_node
.merge_state
.with_new_local_structure()
.with_new_remote_structure();
merged_orphan_node.merge_state = merged_orphan_node
.merge_state
.with_new_local_structure()
.with_new_remote_structure();
merged_node.merged_children.push(merged_orphan_node);
self.structure_counts.merged_nodes += 1;
}
}
}
self.structure_counts.merged_deletions += 1;
Ok(StructureChange::Deleted)
}
fn delete_local_node(
&mut self,
merged_node: &mut MergedNode<'t>,
local_node: Node<'t>,
) -> Result<StructureChange> {
self.delete_locally.insert(local_node.guid.clone());
for local_child_node in local_node.children() {
self.signal.err_if_aborted()?;
if self.merged_guids.contains(&local_child_node.guid) {
trace!(
self.driver,
"Local child {} can't be an orphan; already merged",
local_child_node
);
continue;
}
match self.check_for_remote_structure_change_of_local_node(
merged_node,
local_node,
local_child_node,
)? {
StructureChange::Moved | StructureChange::Deleted => {
continue;
}
StructureChange::Unchanged => {
trace!(
self.driver,
"Relocating local orphan {} to {}",
local_child_node,
merged_node
);
let mut merged_orphan_node = if let Some(remote_child_node) =
self.remote_tree.node_for_guid(&local_child_node.guid)
{
self.two_way_merge(local_child_node, remote_child_node)
} else {
self.merge_local_only_node(local_child_node)
}?;
merged_node.merge_state = merged_node
.merge_state
.with_new_local_structure()
.with_new_remote_structure();
merged_orphan_node.merge_state = merged_orphan_node
.merge_state
.with_new_local_structure()
.with_new_remote_structure();
merged_node.merged_children.push(merged_orphan_node);
self.structure_counts.merged_nodes += 1;
}
}
}
self.structure_counts.merged_deletions += 1;
Ok(StructureChange::Deleted)
}
fn find_all_matching_dupes_in_folders(
&self,
local_parent_node: Node<'t>,
remote_parent_node: Node<'t>,
) -> Result<MatchingDupes<'t>> {
let mut dupe_key_to_local_nodes: HashMap<DupeKey<'_>, VecDeque<_>> = HashMap::new();
for (local_position, local_child_node) in local_parent_node.children().enumerate() {
self.signal.err_if_aborted()?;
if local_child_node.is_built_in_root() {
continue;
}
if let Some(local_child_content) = local_child_node.content() {
if let Some(remote_child_node) =
self.remote_tree.node_for_guid(&local_child_node.guid)
{
trace!(
self.driver,
"Not deduping local child {}; already exists remotely as {}",
local_child_node,
remote_child_node
);
continue;
}
if self.remote_tree.is_deleted(&local_child_node.guid) {
trace!(
self.driver,
"Not deduping local child {}; deleted remotely",
local_child_node
);
continue;
}
let dupe_key = match local_child_content {
Content::Bookmark { .. } | Content::Folder { .. } => {
DupeKey::WithoutPosition(local_child_content)
}
Content::Separator => {
DupeKey::WithPosition(local_child_content, local_position)
}
};
let local_nodes_for_key = dupe_key_to_local_nodes.entry(dupe_key).or_default();
local_nodes_for_key.push_back(local_child_node);
} else {
trace!(
self.driver,
"Not deduping local child {}; already uploaded",
local_child_node
);
}
}
let mut local_to_remote = HashMap::new();
let mut remote_to_local = HashMap::new();
for (remote_position, remote_child_node) in remote_parent_node.children().enumerate() {
self.signal.err_if_aborted()?;
if remote_to_local.contains_key(&remote_child_node.guid) {
trace!(
self.driver,
"Not deduping remote child {}; already deduped",
remote_child_node
);
continue;
}
if let Some(remote_child_content) = remote_child_node.content() {
let dupe_key = match remote_child_content {
Content::Bookmark { .. } | Content::Folder { .. } => {
DupeKey::WithoutPosition(remote_child_content)
}
Content::Separator => {
DupeKey::WithPosition(remote_child_content, remote_position)
}
};
if let Some(local_nodes_for_key) = dupe_key_to_local_nodes.get_mut(&dupe_key) {
if let Some(local_child_node) = local_nodes_for_key.pop_front() {
trace!(
self.driver,
"Deduping local child {} to remote child {}",
local_child_node,
remote_child_node
);
local_to_remote.insert(local_child_node.guid.clone(), remote_child_node);
remote_to_local.insert(remote_child_node.guid.clone(), local_child_node);
} else {
trace!(
self.driver,
"Not deduping remote child {}; no remaining local content matches",
remote_child_node
);
continue;
}
} else {
trace!(
self.driver,
"Not deduping remote child {}; no local content matches",
remote_child_node
);
continue;
}
} else {
trace!(
self.driver,
"Not deduping remote child {}; already merged",
remote_child_node
);
}
}
Ok((local_to_remote, remote_to_local))
}
fn find_remote_node_matching_local_node(
&mut self,
merged_node: &MergedNode<'t>,
local_parent_node: Node<'t>,
remote_parent_node: Option<Node<'t>>,
local_child_node: Node<'t>,
) -> Result<Option<Node<'t>>> {
if let Some(remote_parent_node) = remote_parent_node {
let mut matching_dupes_by_local_parent_guid = mem::replace(
&mut self.matching_dupes_by_local_parent_guid,
HashMap::new(),
);
let new_remote_node = {
let (local_to_remote, _) = match matching_dupes_by_local_parent_guid
.entry(local_parent_node.guid.clone())
{
Entry::Occupied(entry) => entry.into_mut(),
Entry::Vacant(entry) => {
trace!(
self.driver,
"First local child {} doesn't exist remotely; \
finding all matching dupes in local {} and remote {}",
local_child_node,
local_parent_node,
remote_parent_node
);
let matching_dupes = self.find_all_matching_dupes_in_folders(
local_parent_node,
remote_parent_node,
)?;
entry.insert(matching_dupes)
}
};
let new_remote_node = local_to_remote.get(&local_child_node.guid);
new_remote_node.map(|node| {
self.structure_counts.dupes += 1;
*node
})
};
mem::replace(
&mut self.matching_dupes_by_local_parent_guid,
matching_dupes_by_local_parent_guid,
);
Ok(new_remote_node)
} else {
trace!(
self.driver,
"Merged node {} doesn't exist remotely; no potential dupes for local child {}",
merged_node,
local_child_node
);
Ok(None)
}
}
fn find_local_node_matching_remote_node(
&mut self,
merged_node: &MergedNode<'t>,
local_parent_node: Option<Node<'t>>,
remote_parent_node: Node<'t>,
remote_child_node: Node<'t>,
) -> Result<Option<Node<'t>>> {
if let Some(local_parent_node) = local_parent_node {
let mut matching_dupes_by_local_parent_guid = mem::replace(
&mut self.matching_dupes_by_local_parent_guid,
HashMap::new(),
);
let new_local_node = {
let (_, remote_to_local) = match matching_dupes_by_local_parent_guid
.entry(local_parent_node.guid.clone())
{
Entry::Occupied(entry) => entry.into_mut(),
Entry::Vacant(entry) => {
trace!(
self.driver,
"First remote child {} doesn't exist locally; \
finding all matching dupes in local {} and remote {}",
remote_child_node,
local_parent_node,
remote_parent_node
);
let matching_dupes = self.find_all_matching_dupes_in_folders(
local_parent_node,
remote_parent_node,
)?;
entry.insert(matching_dupes)
}
};
let new_local_node = remote_to_local.get(&remote_child_node.guid);
new_local_node.map(|node| {
self.structure_counts.dupes += 1;
*node
})
};
mem::replace(
&mut self.matching_dupes_by_local_parent_guid,
matching_dupes_by_local_parent_guid,
);
Ok(new_local_node)
} else {
trace!(
self.driver,
"Merged node {} doesn't exist locally; no potential dupes for remote child {}",
merged_node,
remote_child_node
);
Ok(None)
}
}
}
#[derive(Debug)]
pub struct MergedRoot<'t> {
local_tree: &'t Tree,
remote_tree: &'t Tree,
node: MergedNode<'t>,
merged_guids: HashSet<Guid>,
delete_locally: HashSet<Guid>,
delete_remotely: HashSet<Guid>,
structure_counts: StructureCounts,
}
impl<'t> MergedRoot<'t> {
#[inline]
pub fn node(&self) -> &MergedNode<'_> {
&self.node
}
pub fn completion_ops(&self) -> CompletionOps<'_> {
let mut ops = CompletionOps::default();
accumulate(&mut ops, self.node(), 1, false);
for guid in self.delete_locally.iter() {
if self.local_tree.mentions(guid) && self.remote_tree.mentions(guid) {
let flag_as_merged = FlagAsMerged(guid);
ops.flag_as_merged.push(flag_as_merged);
}
}
ops
}
#[inline]
pub fn deletions(&self) -> impl Iterator<Item = Deletion<'_>> {
self.local_deletions().chain(self.remote_deletions())
}
pub fn local_deletions(&self) -> impl Iterator<Item = Deletion<'_>> {
self.delete_locally.iter().filter_map(move |guid| {
if self.delete_remotely.contains(guid) {
None
} else {
let local_level = self
.local_tree
.node_for_guid(guid)
.map_or(-1, |node| node.level());
Some(Deletion {
guid,
local_level,
should_upload_tombstone: false,
})
}
})
}
pub fn remote_deletions(&self) -> impl Iterator<Item = Deletion<'_>> {
self.delete_remotely.iter().map(move |guid| {
let local_level = self
.local_tree
.node_for_guid(guid)
.map_or(-1, |node| node.level());
Deletion {
guid,
local_level,
should_upload_tombstone: true,
}
})
}
#[inline]
pub fn counts(&self) -> &StructureCounts {
&self.structure_counts
}
}
#[derive(Clone, Debug, Default)]
pub struct CompletionOps<'t> {
pub change_guids: Vec<ChangeGuid<'t>>,
pub apply_remote_items: Vec<ApplyRemoteItem<'t>>,
pub apply_new_local_structure: Vec<ApplyNewLocalStructure<'t>>,
pub flag_for_upload: Vec<FlagForUpload<'t>>,
pub skip_upload: Vec<SkipUpload<'t>>,
pub flag_as_merged: Vec<FlagAsMerged<'t>>,
pub upload: Vec<Upload<'t>>,
}
impl<'t> CompletionOps<'t> {
#[inline]
pub fn is_empty(&self) -> bool {
self.change_guids.is_empty()
&& self.apply_remote_items.is_empty()
&& self.apply_new_local_structure.is_empty()
&& self.flag_for_upload.is_empty()
&& self.skip_upload.is_empty()
&& self.flag_as_merged.is_empty()
&& self.upload.is_empty()
}
}
#[derive(Clone, Copy, Debug)]
pub struct ChangeGuid<'t> {
pub merged_node: &'t MergedNode<'t>,
pub level: usize,
}
impl<'t> ChangeGuid<'t> {
#[inline]
pub fn local_node(&self) -> &'t Node<'t> {
self.merged_node
.merge_state
.local_node()
.expect("Can't change local GUID without local node")
}
}
impl<'t> fmt::Display for ChangeGuid<'t> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Change {} to {}",
self.local_node().guid,
self.merged_node.guid
)
}
}
#[derive(Clone, Copy, Debug)]
pub struct ApplyRemoteItem<'t> {
pub merged_node: &'t MergedNode<'t>,
pub level: usize,
}
impl<'t> ApplyRemoteItem<'t> {
#[inline]
pub fn remote_node(&self) -> &'t Node<'t> {
self.merged_node
.merge_state
.remote_node()
.expect("Can't apply remote item without remote node")
}
}
impl<'t> fmt::Display for ApplyRemoteItem<'t> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.merged_node.remote_guid_changed() {
write!(
f,
"Apply remote {} as {}",
self.remote_node().guid,
self.merged_node.guid
)
} else {
write!(f, "Apply remote {}", self.merged_node.guid)
}
}
}
#[derive(Clone, Copy, Debug)]
pub struct ApplyNewLocalStructure<'t> {
pub merged_node: &'t MergedNode<'t>,
pub merged_parent_node: &'t MergedNode<'t>,
pub position: usize,
pub level: usize,
}
impl<'t> fmt::Display for ApplyNewLocalStructure<'t> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Move {} into {} at {}",
self.merged_node.guid, self.merged_parent_node.guid, self.position
)
}
}
#[derive(Clone, Copy, Debug)]
pub struct FlagForUpload<'t> {
pub merged_node: &'t MergedNode<'t>,
}
impl<'t> fmt::Display for FlagForUpload<'t> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Flag {} for upload", self.merged_node.guid)
}
}
#[derive(Clone, Copy, Debug)]
pub struct SkipUpload<'t> {
pub merged_node: &'t MergedNode<'t>,
}
impl<'t> fmt::Display for SkipUpload<'t> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Don't upload {}", self.merged_node.guid)
}
}
#[derive(Clone, Copy, Debug)]
pub struct Upload<'t> {
pub merged_node: &'t MergedNode<'t>,
}
impl<'t> fmt::Display for Upload<'t> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Upload {}", self.merged_node.guid)
}
}
#[derive(Clone, Copy, Debug)]
pub struct FlagAsMerged<'t>(&'t Guid);
impl<'t> FlagAsMerged<'t> {
#[inline]
pub fn guid(self) -> &'t Guid {
self.0
}
}
impl<'t> fmt::Display for FlagAsMerged<'t> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Flag {} as merged", self.guid())
}
}
fn accumulate<'t>(
ops: &mut CompletionOps<'t>,
merged_node: &'t MergedNode<'t>,
level: usize,
is_tagging: bool,
) {
for (position, merged_child_node) in merged_node.merged_children.iter().enumerate() {
let is_tagging = if merged_child_node.guid == TAGS_GUID {
true
} else {
is_tagging
};
if merged_child_node.merge_state.should_apply_item() {
let apply_remote_item = ApplyRemoteItem {
merged_node: merged_child_node,
level,
};
ops.apply_remote_items.push(apply_remote_item);
}
if merged_child_node.local_guid_changed() {
let change_guid = ChangeGuid {
merged_node: merged_child_node,
level,
};
ops.change_guids.push(change_guid);
}
let local_child_node = merged_node
.merge_state
.local_node()
.and_then(|local_parent_node| local_parent_node.child(position));
let merged_local_child_node = merged_child_node.merge_state.local_node();
if local_child_node
.and_then(|m| merged_local_child_node.map(|n| m.guid != n.guid))
.unwrap_or(true)
{
let apply_new_local_structure = ApplyNewLocalStructure {
merged_node: merged_child_node,
merged_parent_node: merged_node,
position,
level,
};
ops.apply_new_local_structure
.push(apply_new_local_structure);
}
let local_needs_merge = merged_child_node
.merge_state
.local_node()
.map(|node| node.needs_merge)
.unwrap_or(false);
let should_upload = merged_child_node.merge_state.should_upload();
match (local_needs_merge, should_upload) {
(false, true) => {
let flag_for_upload = FlagForUpload {
merged_node: merged_child_node,
};
ops.flag_for_upload.push(flag_for_upload);
}
(true, false) => {
let skip_upload = SkipUpload {
merged_node: merged_child_node,
};
ops.skip_upload.push(skip_upload);
}
_ => {}
}
if should_upload && !is_tagging {
ops.upload.push(Upload {
merged_node: merged_child_node,
});
}
if let Some(remote_child_node) = merged_child_node.merge_state.remote_node() {
if remote_child_node.needs_merge && !should_upload {
let flag_as_merged = FlagAsMerged(&remote_child_node.guid);
ops.flag_as_merged.push(flag_as_merged);
}
}
accumulate(ops, merged_child_node, level + 1, is_tagging);
}
}