atagen

macltok

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