diff options
| author | tv <tv@krebsco.de> | 2026-11-05 03:40:09 +0100 |
|---|---|---|
| committer | tv <tv@krebsco.de> | 2026-11-05 03:40:09 +0100 |
| commit | 11d098477cfc76da0bc4385635e4c4b7124a9259 (patch) | |
| tree | 941640ead4d8e80b291780f708a56f8be53a0fe1 /src/document/changes.rs | |
| parent | 9f9015a35b591043ebaf6e306f967ffc62cd07f5 (diff) | |
announce only to those behind
Diffstat (limited to 'src/document/changes.rs')
| -rw-r--r-- | src/document/changes.rs | 43 |
1 files changed, 43 insertions, 0 deletions
diff --git a/src/document/changes.rs b/src/document/changes.rs index 19d4db6..1091261 100644 --- a/src/document/changes.rs +++ b/src/document/changes.rs @@ -8,6 +8,25 @@ use anyhow::{Context, Result, anyhow, bail}; use super::{Change, ChangeHash, Document, ReadDoc}; +/// Whether the history of `theirs` (of them, those `doc` has) holds all of `ours` +/// (heads of `doc`): a peer at `theirs` has every change we have. +pub(crate) fn covers(doc: &Document, theirs: &[ChangeHash], ours: &[ChangeHash]) -> bool { + let known: Vec<ChangeHash> = theirs + .iter() + .copied() + .filter(|head| doc.get_change_by_hash(head).is_some()) + .collect(); + if known.is_empty() { + return ours.is_empty(); + } + let beyond: std::collections::HashSet<ChangeHash> = doc + .get_changes_meta(&known) + .into_iter() + .map(|change| change.hash) + .collect(); + ours.iter().all(|head| !beyond.contains(head)) +} + /// Those of `heads` that `doc` doesn't have. pub(crate) fn missing(doc: &impl ReadDoc, heads: &[ChangeHash]) -> Vec<ChangeHash> { heads @@ -176,6 +195,30 @@ mod tests { }); } + /// Heads cover ours when their history holds all of ours: as far as ours, or + /// further; not when behind or beside; heads we don't know count for nothing. + #[test] + fn heads_that_cover_ours() { + let mut doc = Document::new(); + change(&mut doc, |doc| doc.put(ROOT, "a", 1_i64).unwrap()); + let early = doc.get_heads(); + change(&mut doc, |doc| doc.put(ROOT, "b", 2_i64).unwrap()); + let ours = doc.get_heads(); + let mut further = doc.fork(); + change(&mut further, |doc| doc.put(ROOT, "c", 3_i64).unwrap()); + doc.merge(&mut further.clone()).unwrap(); + let unknown = ChangeHash([7; 32]); + + assert!(covers(&doc, &ours, &ours)); + assert!(covers(&doc, &further.get_heads(), &ours), "further"); + assert!(!covers(&doc, &early, &ours), "behind"); + assert!(!covers(&doc, &[unknown], &ours), "unknown"); + assert!( + covers(&doc, &[unknown, ours[0]], &ours), + "the known one does" + ); + } + #[test] fn no_delta_without_its_heads() { let mut doc = Document::new(); |
