atagen

macltok

public
Star0
Watch0
cbb715e119632f60c9b3ec1bc8803f1f33e39dce
3
ssh://brewery@pls.lobotomise.me:33/atagen/macltok.git

Blame: src/mentions/state.rs

cbb715e11963atagen2026-10-24 23:24 UTC1
use std::collections::{BTreeMap, BTreeSet};
cbb715e11963atagen2026-10-24 23:24 UTC2
cbb715e11963atagen2026-10-24 23:24 UTC3
use ruma::OwnedUserId;
cbb715e11963atagen2026-10-24 23:24 UTC4
use serde_json::Value;
cbb715e11963atagen2026-10-24 23:24 UTC5
cbb715e11963atagen2026-10-24 23:24 UTC6
use crate::{
cbb715e11963atagen2026-10-24 23:24 UTC7
    Error, ErrorKind, EventContext, MentionCaptureState, MentionCoverage, MentionEntry, MentionKey,
cbb715e11963atagen2026-10-24 23:24 UTC8
    MentionsBoundary, MentionsPage, SavedMentionContext, TimelineItem,
cbb715e11963atagen2026-10-24 23:24 UTC9
    normalise::MediaRegistration,
cbb715e11963atagen2026-10-24 23:24 UTC10
};
cbb715e11963atagen2026-10-24 23:24 UTC11
cbb715e11963atagen2026-10-24 23:24 UTC12
#[derive(Clone)]
cbb715e11963atagen2026-10-24 23:24 UTC13
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
cbb715e11963atagen2026-10-24 23:24 UTC14
pub(super) struct SavedEvent {
cbb715e11963atagen2026-10-24 23:24 UTC15
    pub item: TimelineItem,
cbb715e11963atagen2026-10-24 23:24 UTC16
    pub media: Vec<MediaRegistration>,
cbb715e11963atagen2026-10-24 23:24 UTC17
    pub raw: Option<Value>,
cbb715e11963atagen2026-10-24 23:24 UTC18
}
cbb715e11963atagen2026-10-24 23:24 UTC19
cbb715e11963atagen2026-10-24 23:24 UTC20
#[derive(Clone)]
cbb715e11963atagen2026-10-24 23:24 UTC21
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
cbb715e11963atagen2026-10-24 23:24 UTC22
pub(super) struct Candidate {
cbb715e11963atagen2026-10-24 23:24 UTC23
    pub room_id: ruma::OwnedRoomId,
cbb715e11963atagen2026-10-24 23:24 UTC24
    pub raw: Value,
cbb715e11963atagen2026-10-24 23:24 UTC25
    pub attempts: u32,
cbb715e11963atagen2026-10-24 23:24 UTC26
    pub retry_at_ms: u64,
cbb715e11963atagen2026-10-24 23:24 UTC27
}
cbb715e11963atagen2026-10-24 23:24 UTC28
cbb715e11963atagen2026-10-24 23:24 UTC29
#[derive(Clone)]
cbb715e11963atagen2026-10-24 23:24 UTC30
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
cbb715e11963atagen2026-10-24 23:24 UTC31
pub(super) struct Window {
cbb715e11963atagen2026-10-24 23:24 UTC32
    pub before: Vec<String>,
cbb715e11963atagen2026-10-24 23:24 UTC33
    pub target: Option<String>,
cbb715e11963atagen2026-10-24 23:24 UTC34
    pub after: Vec<String>,
cbb715e11963atagen2026-10-24 23:24 UTC35
    pub start: Option<String>,
cbb715e11963atagen2026-10-24 23:24 UTC36
    pub end: Option<String>,
cbb715e11963atagen2026-10-24 23:24 UTC37
}
cbb715e11963atagen2026-10-24 23:24 UTC38
cbb715e11963atagen2026-10-24 23:24 UTC39
#[derive(Clone, Default)]
cbb715e11963atagen2026-10-24 23:24 UTC40
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
cbb715e11963atagen2026-10-24 23:24 UTC41
pub(super) struct Data {
cbb715e11963atagen2026-10-24 23:24 UTC42
    pub revision: u64,
cbb715e11963atagen2026-10-24 23:24 UTC43
    pub entries: BTreeMap<String, MentionEntry>,
cbb715e11963atagen2026-10-24 23:24 UTC44
    pub events: BTreeMap<String, SavedEvent>,
cbb715e11963atagen2026-10-24 23:24 UTC45
    pub windows: BTreeMap<String, Window>,
cbb715e11963atagen2026-10-24 23:24 UTC46
    pub capture_attempts: BTreeMap<String, u32>,
cbb715e11963atagen2026-10-24 23:24 UTC47
    pub candidates: BTreeMap<String, Candidate>,
cbb715e11963atagen2026-10-24 23:24 UTC48
    pub redacted: BTreeMap<String, u64>,
cbb715e11963atagen2026-10-24 23:24 UTC49
    pub edits: BTreeMap<String, Value>,
cbb715e11963atagen2026-10-24 23:24 UTC50
    pub coverage: MentionCoverage,
cbb715e11963atagen2026-10-24 23:24 UTC51
    pub checkpoints: BTreeMap<String, String>,
cbb715e11963atagen2026-10-24 23:24 UTC52
    pub observed_rooms: BTreeSet<ruma::OwnedRoomId>,
cbb715e11963atagen2026-10-24 23:24 UTC53
    pub history_cursor: Option<String>,
cbb715e11963atagen2026-10-24 23:24 UTC54
    pub history_exhausted: bool,
cbb715e11963atagen2026-10-24 23:24 UTC55
    pub latest_cursor: Option<String>,
cbb715e11963atagen2026-10-24 23:24 UTC56
    #[cfg_attr(feature = "serde", serde(default))]
cbb715e11963atagen2026-10-24 23:24 UTC57
    pub latest_anchor: Option<String>,
cbb715e11963atagen2026-10-24 23:24 UTC58
    #[cfg_attr(feature = "serde", serde(default))]
cbb715e11963atagen2026-10-24 23:24 UTC59
    pub latest_head: Option<String>,
cbb715e11963atagen2026-10-24 23:24 UTC60
}
cbb715e11963atagen2026-10-24 23:24 UTC61
cbb715e11963atagen2026-10-24 23:24 UTC62
pub(super) fn id(key: &MentionKey) -> String {
cbb715e11963atagen2026-10-24 23:24 UTC63
    format!("{}\n{}", key.room_id, key.event_id)
cbb715e11963atagen2026-10-24 23:24 UTC64
}
cbb715e11963atagen2026-10-24 23:24 UTC65
cbb715e11963atagen2026-10-24 23:24 UTC66
pub(super) fn now() -> u64 {
cbb715e11963atagen2026-10-24 23:24 UTC67
    matrix_sdk::ruma::MilliSecondsSinceUnixEpoch::now()
cbb715e11963atagen2026-10-24 23:24 UTC68
        .get()
cbb715e11963atagen2026-10-24 23:24 UTC69
        .into()
cbb715e11963atagen2026-10-24 23:24 UTC70
}
cbb715e11963atagen2026-10-24 23:24 UTC71
cbb715e11963atagen2026-10-24 23:24 UTC72
pub(super) fn invalid(message: &'static str) -> Error {
cbb715e11963atagen2026-10-24 23:24 UTC73
    Error::new(ErrorKind::Invalid, message)
cbb715e11963atagen2026-10-24 23:24 UTC74
}
cbb715e11963atagen2026-10-24 23:24 UTC75
cbb715e11963atagen2026-10-24 23:24 UTC76
impl Data {
cbb715e11963atagen2026-10-24 23:24 UTC77
    pub fn touch_contexts(&mut self, identity: &str) {
cbb715e11963atagen2026-10-24 23:24 UTC78
        for (key, window) in &self.windows {
cbb715e11963atagen2026-10-24 23:24 UTC79
            if window.target.as_deref() == Some(identity)
cbb715e11963atagen2026-10-24 23:24 UTC80
                || window
cbb715e11963atagen2026-10-24 23:24 UTC81
                    .before
cbb715e11963atagen2026-10-24 23:24 UTC82
                    .iter()
cbb715e11963atagen2026-10-24 23:24 UTC83
                    .chain(&window.after)
cbb715e11963atagen2026-10-24 23:24 UTC84
                    .any(|key| key == identity)
cbb715e11963atagen2026-10-24 23:24 UTC85
            {
cbb715e11963atagen2026-10-24 23:24 UTC86
                if let Some(entry) = self.entries.get_mut(key) {
cbb715e11963atagen2026-10-24 23:24 UTC87
                    entry.revision = self.revision + 1;
cbb715e11963atagen2026-10-24 23:24 UTC88
                }
cbb715e11963atagen2026-10-24 23:24 UTC89
            }
cbb715e11963atagen2026-10-24 23:24 UTC90
        }
cbb715e11963atagen2026-10-24 23:24 UTC91
    }
cbb715e11963atagen2026-10-24 23:24 UTC92
cbb715e11963atagen2026-10-24 23:24 UTC93
    pub fn page(
cbb715e11963atagen2026-10-24 23:24 UTC94
        &self,
cbb715e11963atagen2026-10-24 23:24 UTC95
        account: &OwnedUserId,
cbb715e11963atagen2026-10-24 23:24 UTC96
        cursor: Option<&str>,
cbb715e11963atagen2026-10-24 23:24 UTC97
        limit: u16,
cbb715e11963atagen2026-10-24 23:24 UTC98
    ) -> Result<MentionsPage, Error> {
cbb715e11963atagen2026-10-24 23:24 UTC99
        let (boundary, last) = if let Some(cursor) = cursor {
cbb715e11963atagen2026-10-24 23:24 UTC100
            let value: Value =
cbb715e11963atagen2026-10-24 23:24 UTC101
                serde_json::from_str(cursor).map_err(|_| invalid("invalid mentions cursor"))?;
cbb715e11963atagen2026-10-24 23:24 UTC102
            if value["account"].as_str() != Some(account.as_str()) {
cbb715e11963atagen2026-10-24 23:24 UTC103
                return Err(invalid("mentions cursor belongs to a different account"));
cbb715e11963atagen2026-10-24 23:24 UTC104
            }
cbb715e11963atagen2026-10-24 23:24 UTC105
            let boundary = value["revision"]
cbb715e11963atagen2026-10-24 23:24 UTC106
                .as_u64()
cbb715e11963atagen2026-10-24 23:24 UTC107
                .ok_or_else(|| invalid("invalid mentions cursor"))?;
cbb715e11963atagen2026-10-24 23:24 UTC108
            let timestamp = value["timestamp"]
cbb715e11963atagen2026-10-24 23:24 UTC109
                .as_u64()
cbb715e11963atagen2026-10-24 23:24 UTC110
                .ok_or_else(|| invalid("invalid mentions cursor"))?;
cbb715e11963atagen2026-10-24 23:24 UTC111
            let key = value["key"]
cbb715e11963atagen2026-10-24 23:24 UTC112
                .as_str()
cbb715e11963atagen2026-10-24 23:24 UTC113
                .ok_or_else(|| invalid("invalid mentions cursor"))?
cbb715e11963atagen2026-10-24 23:24 UTC114
                .to_owned();
cbb715e11963atagen2026-10-24 23:24 UTC115
            if boundary > self.revision {
cbb715e11963atagen2026-10-24 23:24 UTC116
                return Err(invalid("mentions cursor is newer than this store"));
cbb715e11963atagen2026-10-24 23:24 UTC117
            }
cbb715e11963atagen2026-10-24 23:24 UTC118
            (boundary, Some((timestamp, key)))
cbb715e11963atagen2026-10-24 23:24 UTC119
        } else {
cbb715e11963atagen2026-10-24 23:24 UTC120
            (self.revision, None)
cbb715e11963atagen2026-10-24 23:24 UTC121
        };
cbb715e11963atagen2026-10-24 23:24 UTC122
        let mut entries: Vec<_> = self
cbb715e11963atagen2026-10-24 23:24 UTC123
            .entries
cbb715e11963atagen2026-10-24 23:24 UTC124
            .iter()
cbb715e11963atagen2026-10-24 23:24 UTC125
            .filter(|(key, entry)| {
cbb715e11963atagen2026-10-24 23:24 UTC126
                entry.discovery_revision <= boundary
cbb715e11963atagen2026-10-24 23:24 UTC127
                    && last.as_ref().is_none_or(|last| {
cbb715e11963atagen2026-10-24 23:24 UTC128
                        (entry.timestamp_ms, key.as_str()) < (last.0, last.1.as_str())
cbb715e11963atagen2026-10-24 23:24 UTC129
                    })
cbb715e11963atagen2026-10-24 23:24 UTC130
            })
cbb715e11963atagen2026-10-24 23:24 UTC131
            .map(|(_, entry)| entry.clone())
cbb715e11963atagen2026-10-24 23:24 UTC132
            .collect();
cbb715e11963atagen2026-10-24 23:24 UTC133
        entries.sort_by(|a, b| (b.timestamp_ms, id(&b.key)).cmp(&(a.timestamp_ms, id(&a.key))));
cbb715e11963atagen2026-10-24 23:24 UTC134
        let limit = usize::from(limit.clamp(1, 50));
cbb715e11963atagen2026-10-24 23:24 UTC135
        let end_reached = entries.len() <= limit;
cbb715e11963atagen2026-10-24 23:24 UTC136
        entries.truncate(limit);
cbb715e11963atagen2026-10-24 23:24 UTC137
        let next_cursor = if end_reached {
cbb715e11963atagen2026-10-24 23:24 UTC138
            None
cbb715e11963atagen2026-10-24 23:24 UTC139
        } else {
cbb715e11963atagen2026-10-24 23:24 UTC140
            entries.last().map(|entry| serde_json::json!({
cbb715e11963atagen2026-10-24 23:24 UTC141
            "account": account.as_str(), "revision": boundary, "timestamp": entry.timestamp_ms, "key": id(&entry.key)
cbb715e11963atagen2026-10-24 23:24 UTC142
        }).to_string())
cbb715e11963atagen2026-10-24 23:24 UTC143
        };
cbb715e11963atagen2026-10-24 23:24 UTC144
        let mut coverage = self.coverage.clone();
cbb715e11963atagen2026-10-24 23:24 UTC145
        coverage.unresolved_candidates = self.candidates.len();
cbb715e11963atagen2026-10-24 23:24 UTC146
        Ok(MentionsPage {
cbb715e11963atagen2026-10-24 23:24 UTC147
            entries,
cbb715e11963atagen2026-10-24 23:24 UTC148
            next_cursor,
cbb715e11963atagen2026-10-24 23:24 UTC149
            end_reached,
cbb715e11963atagen2026-10-24 23:24 UTC150
            coverage,
cbb715e11963atagen2026-10-24 23:24 UTC151
            unread_count: self.entries.values().filter(|entry| !entry.read).count(),
cbb715e11963atagen2026-10-24 23:24 UTC152
            revision: self.revision,
cbb715e11963atagen2026-10-24 23:24 UTC153
            boundary: MentionsBoundary {
cbb715e11963atagen2026-10-24 23:24 UTC154
                account: account.clone(),
cbb715e11963atagen2026-10-24 23:24 UTC155
                revision: boundary,
cbb715e11963atagen2026-10-24 23:24 UTC156
            },
cbb715e11963atagen2026-10-24 23:24 UTC157
        })
cbb715e11963atagen2026-10-24 23:24 UTC158
    }
cbb715e11963atagen2026-10-24 23:24 UTC159
cbb715e11963atagen2026-10-24 23:24 UTC160
    pub fn context(
cbb715e11963atagen2026-10-24 23:24 UTC161
        &self,
cbb715e11963atagen2026-10-24 23:24 UTC162
        key: &MentionKey,
cbb715e11963atagen2026-10-24 23:24 UTC163
    ) -> Result<(SavedMentionContext, Vec<MediaRegistration>), Error> {
cbb715e11963atagen2026-10-24 23:24 UTC164
        let entry = self
cbb715e11963atagen2026-10-24 23:24 UTC165
            .entries
cbb715e11963atagen2026-10-24 23:24 UTC166
            .get(&id(key))
cbb715e11963atagen2026-10-24 23:24 UTC167
            .ok_or_else(|| Error::new(ErrorKind::EventNotFound, "mention was not found"))?;
cbb715e11963atagen2026-10-24 23:24 UTC168
        let mut media = Vec::new();
cbb715e11963atagen2026-10-24 23:24 UTC169
        let mut item = |key: &String| {
cbb715e11963atagen2026-10-24 23:24 UTC170
            self.events.get(key).map(|event| {
cbb715e11963atagen2026-10-24 23:24 UTC171
                media.extend(event.media.clone());
cbb715e11963atagen2026-10-24 23:24 UTC172
                event.item.clone()
cbb715e11963atagen2026-10-24 23:24 UTC173
            })
cbb715e11963atagen2026-10-24 23:24 UTC174
        };
cbb715e11963atagen2026-10-24 23:24 UTC175
        let context = self.windows.get(&id(key)).map(|window| EventContext {
cbb715e11963atagen2026-10-24 23:24 UTC176
            events_before: window.before.iter().filter_map(&mut item).collect(),
cbb715e11963atagen2026-10-24 23:24 UTC177
            event: window.target.as_ref().and_then(&mut item),
cbb715e11963atagen2026-10-24 23:24 UTC178
            events_after: window.after.iter().filter_map(&mut item).collect(),
cbb715e11963atagen2026-10-24 23:24 UTC179
            start_token: window.start.clone(),
cbb715e11963atagen2026-10-24 23:24 UTC180
            end_token: window.end.clone(),
cbb715e11963atagen2026-10-24 23:24 UTC181
        });
cbb715e11963atagen2026-10-24 23:24 UTC182
        Ok((
cbb715e11963atagen2026-10-24 23:24 UTC183
            SavedMentionContext {
cbb715e11963atagen2026-10-24 23:24 UTC184
                key: key.clone(),
cbb715e11963atagen2026-10-24 23:24 UTC185
                state: entry.capture.clone(),
cbb715e11963atagen2026-10-24 23:24 UTC186
                context,
cbb715e11963atagen2026-10-24 23:24 UTC187
                revision: self.revision,
cbb715e11963atagen2026-10-24 23:24 UTC188
            },
cbb715e11963atagen2026-10-24 23:24 UTC189
            media,
cbb715e11963atagen2026-10-24 23:24 UTC190
        ))
cbb715e11963atagen2026-10-24 23:24 UTC191
    }
cbb715e11963atagen2026-10-24 23:24 UTC192
cbb715e11963atagen2026-10-24 23:24 UTC193
    pub fn collect_events(&mut self) {
cbb715e11963atagen2026-10-24 23:24 UTC194
        let mut used: BTreeSet<String> = self.entries.keys().cloned().collect();
cbb715e11963atagen2026-10-24 23:24 UTC195
        for window in self.windows.values() {
cbb715e11963atagen2026-10-24 23:24 UTC196
            used.extend(window.before.iter().chain(&window.after).cloned());
cbb715e11963atagen2026-10-24 23:24 UTC197
            used.extend(window.target.iter().cloned());
cbb715e11963atagen2026-10-24 23:24 UTC198
        }
cbb715e11963atagen2026-10-24 23:24 UTC199
        self.events.retain(|key, _| used.contains(key));
cbb715e11963atagen2026-10-24 23:24 UTC200
        self.capture_attempts
cbb715e11963atagen2026-10-24 23:24 UTC201
            .retain(|key, _| self.entries.contains_key(key));
cbb715e11963atagen2026-10-24 23:24 UTC202
    }
cbb715e11963atagen2026-10-24 23:24 UTC203
cbb715e11963atagen2026-10-24 23:24 UTC204
    pub fn evict_one(&mut self) -> bool {
cbb715e11963atagen2026-10-24 23:24 UTC205
        let oldest = |keys: Vec<String>, entries: &BTreeMap<String, MentionEntry>| {
cbb715e11963atagen2026-10-24 23:24 UTC206
            keys.into_iter()
cbb715e11963atagen2026-10-24 23:24 UTC207
                .min_by_key(|key| entries.get(key).map_or(0, |entry| entry.timestamp_ms))
cbb715e11963atagen2026-10-24 23:24 UTC208
        };
cbb715e11963atagen2026-10-24 23:24 UTC209
        if let Some(key) = oldest(
cbb715e11963atagen2026-10-24 23:24 UTC210
            self.windows
cbb715e11963atagen2026-10-24 23:24 UTC211
                .keys()
cbb715e11963atagen2026-10-24 23:24 UTC212
                .filter(|key| self.entries.get(*key).is_some_and(|entry| entry.read))
cbb715e11963atagen2026-10-24 23:24 UTC213
                .cloned()
cbb715e11963atagen2026-10-24 23:24 UTC214
                .collect(),
cbb715e11963atagen2026-10-24 23:24 UTC215
            &self.entries,
cbb715e11963atagen2026-10-24 23:24 UTC216
        )
cbb715e11963atagen2026-10-24 23:24 UTC217
        .or_else(|| oldest(self.windows.keys().cloned().collect(), &self.entries))
cbb715e11963atagen2026-10-24 23:24 UTC218
        {
cbb715e11963atagen2026-10-24 23:24 UTC219
            self.windows.remove(&key);
cbb715e11963atagen2026-10-24 23:24 UTC220
            if let Some(entry) = self.entries.get_mut(&key) {
cbb715e11963atagen2026-10-24 23:24 UTC221
                entry.capture = MentionCaptureState::Unavailable;
cbb715e11963atagen2026-10-24 23:24 UTC222
                entry.revision = self.revision;
cbb715e11963atagen2026-10-24 23:24 UTC223
            }
cbb715e11963atagen2026-10-24 23:24 UTC224
        } else if let Some(key) = self
cbb715e11963atagen2026-10-24 23:24 UTC225
            .entries
cbb715e11963atagen2026-10-24 23:24 UTC226
            .iter()
cbb715e11963atagen2026-10-24 23:24 UTC227
            .min_by_key(|(_, entry)| (!entry.read, entry.timestamp_ms))
cbb715e11963atagen2026-10-24 23:24 UTC228
            .map(|(key, _)| key.clone())
cbb715e11963atagen2026-10-24 23:24 UTC229
        {
cbb715e11963atagen2026-10-24 23:24 UTC230
            self.entries.remove(&key);
cbb715e11963atagen2026-10-24 23:24 UTC231
        } else if let Some(key) = self.candidates.keys().next().cloned() {
cbb715e11963atagen2026-10-24 23:24 UTC232
            self.candidates.remove(&key);
cbb715e11963atagen2026-10-24 23:24 UTC233
        } else if let Some(key) = self.edits.keys().next().cloned() {
cbb715e11963atagen2026-10-24 23:24 UTC234
            self.edits.remove(&key);
cbb715e11963atagen2026-10-24 23:24 UTC235
        } else if let Some(key) = self.redacted.keys().next().cloned() {
cbb715e11963atagen2026-10-24 23:24 UTC236
            self.redacted.remove(&key);
cbb715e11963atagen2026-10-24 23:24 UTC237
        } else {
cbb715e11963atagen2026-10-24 23:24 UTC238
            return false;
cbb715e11963atagen2026-10-24 23:24 UTC239
        }
cbb715e11963atagen2026-10-24 23:24 UTC240
        self.coverage.truncated = true;
cbb715e11963atagen2026-10-24 23:24 UTC241
        self.collect_events();
cbb715e11963atagen2026-10-24 23:24 UTC242
        true
cbb715e11963atagen2026-10-24 23:24 UTC243
    }
cbb715e11963atagen2026-10-24 23:24 UTC244
cbb715e11963atagen2026-10-24 23:24 UTC245
    pub fn bound(&mut self, entries: usize, events: usize, candidates: usize) {
cbb715e11963atagen2026-10-24 23:24 UTC246
        while self.entries.len() > entries {
cbb715e11963atagen2026-10-24 23:24 UTC247
            if let Some(key) = self
cbb715e11963atagen2026-10-24 23:24 UTC248
                .entries
cbb715e11963atagen2026-10-24 23:24 UTC249
                .iter()
cbb715e11963atagen2026-10-24 23:24 UTC250
                .min_by_key(|(_, entry)| (!entry.read, entry.timestamp_ms))
cbb715e11963atagen2026-10-24 23:24 UTC251
                .map(|(key, _)| key.clone())
cbb715e11963atagen2026-10-24 23:24 UTC252
            {
cbb715e11963atagen2026-10-24 23:24 UTC253
                self.entries.remove(&key);
cbb715e11963atagen2026-10-24 23:24 UTC254
                self.windows.remove(&key);
cbb715e11963atagen2026-10-24 23:24 UTC255
                self.coverage.truncated = true;
cbb715e11963atagen2026-10-24 23:24 UTC256
            }
cbb715e11963atagen2026-10-24 23:24 UTC257
        }
cbb715e11963atagen2026-10-24 23:24 UTC258
        self.collect_events();
cbb715e11963atagen2026-10-24 23:24 UTC259
        while self.events.len() > events && self.evict_one() {}
cbb715e11963atagen2026-10-24 23:24 UTC260
        while self.candidates.len() > candidates {
cbb715e11963atagen2026-10-24 23:24 UTC261
            let key = self.candidates.keys().next().cloned().unwrap();
cbb715e11963atagen2026-10-24 23:24 UTC262
            self.candidates.remove(&key);
cbb715e11963atagen2026-10-24 23:24 UTC263
            self.coverage.truncated = true;
cbb715e11963atagen2026-10-24 23:24 UTC264
        }
cbb715e11963atagen2026-10-24 23:24 UTC265
        for map in [&mut self.redacted] {
cbb715e11963atagen2026-10-24 23:24 UTC266
            while map.len() > entries + candidates {
cbb715e11963atagen2026-10-24 23:24 UTC267
                let key = map
cbb715e11963atagen2026-10-24 23:24 UTC268
                    .iter()
cbb715e11963atagen2026-10-24 23:24 UTC269
                    .min_by_key(|(_, timestamp)| *timestamp)
cbb715e11963atagen2026-10-24 23:24 UTC270
                    .map(|(key, _)| key.clone())
cbb715e11963atagen2026-10-24 23:24 UTC271
                    .unwrap();
cbb715e11963atagen2026-10-24 23:24 UTC272
                map.remove(&key);
cbb715e11963atagen2026-10-24 23:24 UTC273
                self.coverage.truncated = true;
cbb715e11963atagen2026-10-24 23:24 UTC274
            }
cbb715e11963atagen2026-10-24 23:24 UTC275
        }
cbb715e11963atagen2026-10-24 23:24 UTC276
        while self.edits.len() > candidates {
cbb715e11963atagen2026-10-24 23:24 UTC277
            let key = self.edits.keys().next().cloned().unwrap();
cbb715e11963atagen2026-10-24 23:24 UTC278
            self.edits.remove(&key);
cbb715e11963atagen2026-10-24 23:24 UTC279
            self.coverage.truncated = true;
cbb715e11963atagen2026-10-24 23:24 UTC280
        }
cbb715e11963atagen2026-10-24 23:24 UTC281
        self.coverage.unresolved_candidates = self.candidates.len();
cbb715e11963atagen2026-10-24 23:24 UTC282
    }
cbb715e11963atagen2026-10-24 23:24 UTC283
}