cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 1 | use std::collections::{BTreeMap, BTreeSet}; |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 2 | |
80381e06a573 | atagen | 2026-10-25 03:02 UTC | 3 | use ruma::{OwnedEventId, OwnedUserId}; |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 4 | use serde_json::Value; |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 5 | |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 6 | use crate::{ |
80381e06a573 | atagen | 2026-10-25 03:02 UTC | 7 | Error, ErrorKind, EventContext, HistoryRepairPolicy, MentionCaptureState, MentionCoverage, |
80381e06a573 | atagen | 2026-10-25 03:02 UTC | 8 | MentionEntry, MentionKey, MentionsBoundary, MentionsPage, SavedMentionContext, TimelineItem, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 9 | normalise::MediaRegistration, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 10 | }; |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 11 | |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 12 | #[derive(Clone)] |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 13 | #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 14 | pub(super) struct SavedEvent { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 15 | pub item: TimelineItem, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 16 | pub media: Vec<MediaRegistration>, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 17 | pub raw: Option<Value>, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 18 | } |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 19 | |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 20 | #[derive(Clone)] |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 21 | #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 22 | pub(super) struct Candidate { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 23 | pub room_id: ruma::OwnedRoomId, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 24 | pub raw: Value, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 25 | pub attempts: u32, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 26 | pub retry_at_ms: u64, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 27 | } |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 28 | |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 29 | #[derive(Clone)] |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 30 | #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 31 | pub(super) struct Window { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 32 | pub before: Vec<String>, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 33 | pub target: Option<String>, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 34 | pub after: Vec<String>, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 35 | pub start: Option<String>, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 36 | pub end: Option<String>, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 37 | } |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 38 | |
80381e06a573 | atagen | 2026-10-25 03:02 UTC | 39 | #[derive(Clone, Debug, Default)] |
80381e06a573 | atagen | 2026-10-25 03:02 UTC | 40 | #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] |
80381e06a573 | atagen | 2026-10-25 03:02 UTC | 41 | pub(crate) struct RepairCoverage { |
80381e06a573 | atagen | 2026-10-25 03:02 UTC | 42 | pub(crate) last_confirmed_ms: Option<u64>, |
80381e06a573 | atagen | 2026-10-25 03:02 UTC | 43 | pub(crate) boundary_ms: Option<u64>, |
80381e06a573 | atagen | 2026-10-25 03:02 UTC | 44 | pub(crate) reliable: bool, |
80381e06a573 | atagen | 2026-10-25 03:02 UTC | 45 | pub(crate) unresolved: bool, |
80381e06a573 | atagen | 2026-10-25 03:02 UTC | 46 | pub(crate) latest_anchor: Option<OwnedEventId>, |
80381e06a573 | atagen | 2026-10-25 03:02 UTC | 47 | } |
80381e06a573 | atagen | 2026-10-25 03:02 UTC | 48 | |
80381e06a573 | atagen | 2026-10-25 03:02 UTC | 49 | impl RepairCoverage { |
80381e06a573 | atagen | 2026-10-25 03:02 UTC | 50 | pub(crate) fn duration_ms(&self) -> Option<u64> { |
80381e06a573 | atagen | 2026-10-25 03:02 UTC | 51 | if !self.reliable { |
80381e06a573 | atagen | 2026-10-25 03:02 UTC | 52 | return None; |
80381e06a573 | atagen | 2026-10-25 03:02 UTC | 53 | } |
80381e06a573 | atagen | 2026-10-25 03:02 UTC | 54 | self.boundary_ms?.checked_sub(self.last_confirmed_ms?) |
80381e06a573 | atagen | 2026-10-25 03:02 UTC | 55 | } |
80381e06a573 | atagen | 2026-10-25 03:02 UTC | 56 | } |
80381e06a573 | atagen | 2026-10-25 03:02 UTC | 57 | |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 58 | #[derive(Clone, Default)] |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 59 | #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 60 | pub(super) struct Data { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 61 | pub revision: u64, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 62 | pub entries: BTreeMap<String, MentionEntry>, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 63 | pub events: BTreeMap<String, SavedEvent>, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 64 | pub windows: BTreeMap<String, Window>, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 65 | pub capture_attempts: BTreeMap<String, u32>, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 66 | pub candidates: BTreeMap<String, Candidate>, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 67 | pub redacted: BTreeMap<String, u64>, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 68 | pub edits: BTreeMap<String, Value>, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 69 | pub coverage: MentionCoverage, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 70 | pub checkpoints: BTreeMap<String, String>, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 71 | pub observed_rooms: BTreeSet<ruma::OwnedRoomId>, |
80381e06a573 | atagen | 2026-10-25 03:02 UTC | 72 | #[cfg_attr(feature = "serde", serde(default))] |
80381e06a573 | atagen | 2026-10-25 03:02 UTC | 73 | pub repair_policy: HistoryRepairPolicy, |
80381e06a573 | atagen | 2026-10-25 03:02 UTC | 74 | #[cfg_attr(feature = "serde", serde(default))] |
80381e06a573 | atagen | 2026-10-25 03:02 UTC | 75 | pub repair_coverage: BTreeMap<ruma::OwnedRoomId, RepairCoverage>, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 76 | pub history_cursor: Option<String>, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 77 | pub history_exhausted: bool, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 78 | pub latest_cursor: Option<String>, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 79 | #[cfg_attr(feature = "serde", serde(default))] |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 80 | pub latest_anchor: Option<String>, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 81 | #[cfg_attr(feature = "serde", serde(default))] |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 82 | pub latest_head: Option<String>, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 83 | } |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 84 | |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 85 | pub(super) fn id(key: &MentionKey) -> String { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 86 | format!("{}\n{}", key.room_id, key.event_id) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 87 | } |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 88 | |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 89 | pub(super) fn now() -> u64 { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 90 | matrix_sdk::ruma::MilliSecondsSinceUnixEpoch::now() |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 91 | .get() |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 92 | .into() |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 93 | } |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 94 | |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 95 | pub(super) fn invalid(message: &'static str) -> Error { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 96 | Error::new(ErrorKind::Invalid, message) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 97 | } |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 98 | |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 99 | impl Data { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 100 | pub fn touch_contexts(&mut self, identity: &str) { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 101 | for (key, window) in &self.windows { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 102 | if window.target.as_deref() == Some(identity) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 103 | || window |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 104 | .before |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 105 | .iter() |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 106 | .chain(&window.after) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 107 | .any(|key| key == identity) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 108 | { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 109 | if let Some(entry) = self.entries.get_mut(key) { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 110 | entry.revision = self.revision + 1; |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 111 | } |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 112 | } |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 113 | } |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 114 | } |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 115 | |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 116 | pub fn page( |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 117 | &self, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 118 | account: &OwnedUserId, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 119 | cursor: Option<&str>, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 120 | limit: u16, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 121 | ) -> Result<MentionsPage, Error> { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 122 | let (boundary, last) = if let Some(cursor) = cursor { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 123 | let value: Value = |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 124 | serde_json::from_str(cursor).map_err(|_| invalid("invalid mentions cursor"))?; |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 125 | if value["account"].as_str() != Some(account.as_str()) { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 126 | return Err(invalid("mentions cursor belongs to a different account")); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 127 | } |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 128 | let boundary = value["revision"] |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 129 | .as_u64() |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 130 | .ok_or_else(|| invalid("invalid mentions cursor"))?; |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 131 | let timestamp = value["timestamp"] |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 132 | .as_u64() |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 133 | .ok_or_else(|| invalid("invalid mentions cursor"))?; |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 134 | let key = value["key"] |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 135 | .as_str() |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 136 | .ok_or_else(|| invalid("invalid mentions cursor"))? |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 137 | .to_owned(); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 138 | if boundary > self.revision { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 139 | return Err(invalid("mentions cursor is newer than this store")); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 140 | } |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 141 | (boundary, Some((timestamp, key))) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 142 | } else { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 143 | (self.revision, None) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 144 | }; |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 145 | let mut entries: Vec<_> = self |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 146 | .entries |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 147 | .iter() |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 148 | .filter(|(key, entry)| { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 149 | entry.discovery_revision <= boundary |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 150 | && last.as_ref().is_none_or(|last| { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 151 | (entry.timestamp_ms, key.as_str()) < (last.0, last.1.as_str()) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 152 | }) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 153 | }) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 154 | .map(|(_, entry)| entry.clone()) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 155 | .collect(); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 156 | entries.sort_by(|a, b| (b.timestamp_ms, id(&b.key)).cmp(&(a.timestamp_ms, id(&a.key)))); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 157 | let limit = usize::from(limit.clamp(1, 50)); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 158 | let end_reached = entries.len() <= limit; |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 159 | entries.truncate(limit); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 160 | let next_cursor = if end_reached { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 161 | None |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 162 | } else { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 163 | entries.last().map(|entry| serde_json::json!({ |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 164 | "account": account.as_str(), "revision": boundary, "timestamp": entry.timestamp_ms, "key": id(&entry.key) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 165 | }).to_string()) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 166 | }; |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 167 | let mut coverage = self.coverage.clone(); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 168 | coverage.unresolved_candidates = self.candidates.len(); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 169 | Ok(MentionsPage { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 170 | entries, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 171 | next_cursor, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 172 | end_reached, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 173 | coverage, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 174 | unread_count: self.entries.values().filter(|entry| !entry.read).count(), |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 175 | revision: self.revision, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 176 | boundary: MentionsBoundary { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 177 | account: account.clone(), |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 178 | revision: boundary, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 179 | }, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 180 | }) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 181 | } |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 182 | |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 183 | pub fn context( |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 184 | &self, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 185 | key: &MentionKey, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 186 | ) -> Result<(SavedMentionContext, Vec<MediaRegistration>), Error> { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 187 | let entry = self |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 188 | .entries |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 189 | .get(&id(key)) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 190 | .ok_or_else(|| Error::new(ErrorKind::EventNotFound, "mention was not found"))?; |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 191 | let mut media = Vec::new(); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 192 | let mut item = |key: &String| { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 193 | self.events.get(key).map(|event| { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 194 | media.extend(event.media.clone()); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 195 | event.item.clone() |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 196 | }) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 197 | }; |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 198 | let context = self.windows.get(&id(key)).map(|window| EventContext { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 199 | events_before: window.before.iter().filter_map(&mut item).collect(), |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 200 | event: window.target.as_ref().and_then(&mut item), |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 201 | events_after: window.after.iter().filter_map(&mut item).collect(), |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 202 | start_token: window.start.clone(), |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 203 | end_token: window.end.clone(), |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 204 | }); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 205 | Ok(( |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 206 | SavedMentionContext { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 207 | key: key.clone(), |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 208 | state: entry.capture.clone(), |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 209 | context, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 210 | revision: self.revision, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 211 | }, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 212 | media, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 213 | )) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 214 | } |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 215 | |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 216 | pub fn collect_events(&mut self) { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 217 | let mut used: BTreeSet<String> = self.entries.keys().cloned().collect(); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 218 | for window in self.windows.values() { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 219 | used.extend(window.before.iter().chain(&window.after).cloned()); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 220 | used.extend(window.target.iter().cloned()); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 221 | } |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 222 | self.events.retain(|key, _| used.contains(key)); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 223 | self.capture_attempts |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 224 | .retain(|key, _| self.entries.contains_key(key)); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 225 | } |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 226 | |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 227 | pub fn evict_one(&mut self) -> bool { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 228 | let oldest = |keys: Vec<String>, entries: &BTreeMap<String, MentionEntry>| { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 229 | keys.into_iter() |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 230 | .min_by_key(|key| entries.get(key).map_or(0, |entry| entry.timestamp_ms)) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 231 | }; |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 232 | if let Some(key) = oldest( |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 233 | self.windows |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 234 | .keys() |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 235 | .filter(|key| self.entries.get(*key).is_some_and(|entry| entry.read)) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 236 | .cloned() |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 237 | .collect(), |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 238 | &self.entries, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 239 | ) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 240 | .or_else(|| oldest(self.windows.keys().cloned().collect(), &self.entries)) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 241 | { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 242 | self.windows.remove(&key); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 243 | if let Some(entry) = self.entries.get_mut(&key) { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 244 | entry.capture = MentionCaptureState::Unavailable; |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 245 | entry.revision = self.revision; |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 246 | } |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 247 | } else if let Some(key) = self |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 248 | .entries |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 249 | .iter() |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 250 | .min_by_key(|(_, entry)| (!entry.read, entry.timestamp_ms)) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 251 | .map(|(key, _)| key.clone()) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 252 | { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 253 | self.entries.remove(&key); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 254 | } else if let Some(key) = self.candidates.keys().next().cloned() { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 255 | self.candidates.remove(&key); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 256 | } else if let Some(key) = self.edits.keys().next().cloned() { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 257 | self.edits.remove(&key); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 258 | } else if let Some(key) = self.redacted.keys().next().cloned() { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 259 | self.redacted.remove(&key); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 260 | } else { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 261 | return false; |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 262 | } |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 263 | self.coverage.truncated = true; |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 264 | self.collect_events(); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 265 | true |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 266 | } |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 267 | |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 268 | pub fn bound(&mut self, entries: usize, events: usize, candidates: usize) { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 269 | while self.entries.len() > entries { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 270 | if let Some(key) = self |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 271 | .entries |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 272 | .iter() |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 273 | .min_by_key(|(_, entry)| (!entry.read, entry.timestamp_ms)) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 274 | .map(|(key, _)| key.clone()) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 275 | { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 276 | self.entries.remove(&key); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 277 | self.windows.remove(&key); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 278 | self.coverage.truncated = true; |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 279 | } |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 280 | } |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 281 | self.collect_events(); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 282 | while self.events.len() > events && self.evict_one() {} |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 283 | while self.candidates.len() > candidates { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 284 | let key = self.candidates.keys().next().cloned().unwrap(); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 285 | self.candidates.remove(&key); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 286 | self.coverage.truncated = true; |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 287 | } |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 288 | for map in [&mut self.redacted] { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 289 | while map.len() > entries + candidates { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 290 | let key = map |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 291 | .iter() |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 292 | .min_by_key(|(_, timestamp)| *timestamp) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 293 | .map(|(key, _)| key.clone()) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 294 | .unwrap(); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 295 | map.remove(&key); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 296 | self.coverage.truncated = true; |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 297 | } |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 298 | } |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 299 | while self.edits.len() > candidates { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 300 | let key = self.edits.keys().next().cloned().unwrap(); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 301 | self.edits.remove(&key); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 302 | self.coverage.truncated = true; |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 303 | } |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 304 | self.coverage.unresolved_candidates = self.candidates.len(); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 305 | } |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 306 | } |