use super::*;
#[derive(Clone, Copy)]
struct Bnd {
idx: usize,
first: usize,
}
struct Preserved {
anchor: String,
uuids: Vec<String>,
}
pub(crate) fn apply<'a>(b: &mut Builder<'a>) -> Option<&'a str> {
let (newest, with_meta) = boundaries(b);
let (Some(newest), Some(meta)) = (newest, with_meta) else {
return None;
};
let preserved = if meta.first == newest.first {
match resolve(b, meta.idx) {
Some(p) => Some(p),
None => return None, }
} else {
None
};
let preserved = preserved.filter(|p| !p.uuids.is_empty());
if let Some(p) = &preserved {
if p.uuids.iter().any(|u| b.resolve(u).is_none()) {
return None; }
}
cut(b, newest.first, preserved.as_ref());
let mut anchor = None;
if let Some(p) = &preserved {
rechain(b, p);
anchor = keyed(b, &p.uuids[p.uuids.len() - 1]).map(|(k, _)| k);
reparent_orphans(b, p);
}
anchor
}
fn reparent_orphans(b: &mut Builder<'_>, p: &Preserved) {
let Some(last_uuid) = keyed(b, &p.uuids[p.uuids.len() - 1]).map(|(k, _)| k) else {
return;
};
for i in 0..b.len() {
if b.removed[i] || !b.is_conv(i) {
continue;
}
let orphaned =
b.parent[i].is_some_and(|pu| b.map.get(pu).is_some_and(|slot| b.removed[slot.last]));
if orphaned {
b.parent[i] = Some(last_uuid);
}
}
}
fn boundaries(b: &Builder<'_>) -> (Option<Bnd>, Option<Bnd>) {
let mut newest = None;
let mut with_meta = None;
for idx in 0..b.len() {
if !b.admit[idx] || !b.is_boundary(idx) {
continue;
}
let slot = b.uuid(idx).and_then(|u| b.map.get(u));
let bnd = Bnd {
idx,
first: slot.map_or(idx, |s| s.first),
};
newest = Some(bnd);
let last = slot.map_or(idx, |s| s.last);
if b.recs[last].compact_metadata.as_ref().is_some_and(|m| {
m.get("preservedMessages").is_some() || m.get("preservedSegment").is_some()
}) {
with_meta = Some(bnd);
}
}
(newest, with_meta)
}
fn resolve(b: &Builder<'_>, idx: usize) -> Option<Preserved> {
let last = b
.uuid(idx)
.and_then(|u| b.map.get(u))
.map_or(idx, |s| s.last);
let meta = b.recs[last].compact_metadata.as_ref()?;
if let Some(list) = meta.get("preservedMessages") {
let anchor = list.get("anchorUuid")?.as_str()?.to_string();
let uuids = list
.get("uuids")?
.as_array()?
.iter()
.filter_map(|v| v.as_str().map(str::to_string))
.collect();
return Some(Preserved { anchor, uuids });
}
let seg = meta.get("preservedSegment")?;
let anchor = seg.get("anchorUuid")?.as_str()?.to_string();
let head = seg.get("headUuid")?.as_str()?;
let tail = seg.get("tailUuid")?.as_str()?;
let mut seen: HashSet<String> = HashSet::new();
let mut out: Vec<String> = Vec::new();
let mut cur = b.resolve(tail);
while let Some(i) = cur {
let u = b.uuid(i)?.to_string();
if !seen.insert(u.clone()) {
return None;
}
out.push(u);
if b.uuid(i) == Some(head) {
out.reverse();
return Some(Preserved { anchor, uuids: out });
}
cur = b.parent[i].and_then(|p| b.resolve(p));
}
None
}
fn keyed<'a>(b: &Builder<'a>, uuid: &str) -> Option<(&'a str, usize)> {
b.map.get_key_value(uuid).map(|(k, s)| (*k, s.last))
}
fn rechain<'a>(b: &mut Builder<'a>, p: &Preserved) {
let first_uuid = keyed(b, &p.uuids[0]).map(|(k, _)| k);
let last_uuid = keyed(b, &p.uuids[p.uuids.len() - 1]).map(|(k, _)| k);
let anchor = keyed(b, &p.anchor).map(|(k, _)| k);
let mut prev: Option<&'a str> = anchor;
for u in &p.uuids {
let Some((key, last)) = keyed(b, u) else {
continue;
};
b.parent[last] = prev;
prev = Some(key);
}
let (Some(anchor), Some(last_uuid)) = (anchor, last_uuid) else {
return;
};
for i in 0..b.len() {
if !b.removed[i] && b.parent[i] == Some(anchor) && b.uuid(i) != first_uuid {
b.parent[i] = Some(last_uuid);
}
}
}
fn cut(b: &mut Builder<'_>, newest_first: usize, preserved: Option<&Preserved>) {
let keep: HashSet<&str> = preserved
.map(|p| p.uuids.iter().map(String::as_str).collect())
.unwrap_or_default();
let dropped: HashSet<usize> = b
.map
.iter()
.filter(|(u, slot)| slot.first < newest_first && !keep.contains(*u))
.map(|(_, slot)| slot.last)
.collect();
if dropped.is_empty() {
return;
}
for &i in &dropped {
b.removed[i] = true;
}
let copies: Vec<usize> = b
.replay_of
.iter()
.filter(|(_, &s)| b.removed[s])
.map(|(&i, _)| i)
.collect();
for i in copies {
b.removed[i] = true;
}
}