fn merge()

in components/webext-storage/src/sync/mod.rs [37:134]


fn merge(
    ext_id: String,
    mut other: JsonMap,
    mut ours: JsonMap,
    parent: Option<JsonMap>,
) -> IncomingAction {
    if other == ours {
        return IncomingAction::Same { ext_id };
    }
    let old_incoming = other.clone();
    // worst case is keys in each are unique.
    let mut changes = StorageChanges::with_capacity(other.len() + ours.len());
    if let Some(parent) = parent {
        // Perform 3-way merge. First, for every key in parent,
        // compare the parent value with the incoming value to compute
        // an implicit "diff".
        for (key, parent_value) in parent.into_iter() {
            if let Some(incoming_value) = other.remove(&key) {
                if incoming_value != parent_value {
                    log::trace!(
                        "merge: key {} was updated in incoming - copying value locally",
                        key
                    );
                    let old_value = ours.remove(&key);
                    let new_value = Some(incoming_value.clone());
                    if old_value != new_value {
                        changes.push(StorageValueChange {
                            key: key.clone(),
                            old_value,
                            new_value,
                        });
                    }
                    ours.insert(key, incoming_value);
                }
            } else {
                // Key was not present in incoming value.
                // Another client must have deleted it.
                log::trace!(
                    "merge: key {} no longer present in incoming - removing it locally",
                    key
                );
                if let Some(old_value) = ours.remove(&key) {
                    changes.push(StorageValueChange {
                        key,
                        old_value: Some(old_value),
                        new_value: None,
                    });
                }
            }
        }

        // Then, go through every remaining key in incoming. These are
        // the ones where a corresponding key does not exist in
        // parent, so it is a new key, and we need to add it.
        for (key, incoming_value) in other.into_iter() {
            log::trace!(
                "merge: key {} doesn't occur in parent - copying from incoming",
                key
            );
            changes.push(StorageValueChange {
                key: key.clone(),
                old_value: None,
                new_value: Some(incoming_value.clone()),
            });
            ours.insert(key, incoming_value);
        }
    } else {
        // No parent. Server wins. Overwrite every key in ours with
        // the corresponding value in other.
        log::trace!("merge: no parent - copying all keys from incoming");
        for (key, incoming_value) in other.into_iter() {
            let old_value = ours.remove(&key);
            let new_value = Some(incoming_value.clone());
            if old_value != new_value {
                changes.push(StorageValueChange {
                    key: key.clone(),
                    old_value,
                    new_value,
                });
            }
            ours.insert(key, incoming_value);
        }
    }

    if ours == old_incoming {
        IncomingAction::TakeRemote {
            ext_id,
            data: old_incoming,
            changes,
        }
    } else {
        IncomingAction::Merge {
            ext_id,
            data: ours,
            changes,
        }
    }
}