matrix_sdk_base/response_processors/account_data/
global.rs1use std::{
16 collections::{BTreeMap, HashMap, HashSet},
17 mem,
18};
19
20use matrix_sdk_common::timer;
21use ruma::{
22 RoomId,
23 events::{
24 AnyGlobalAccountDataEvent, GlobalAccountDataEventType, direct::OwnedDirectUserIdentifier,
25 },
26 serde::Raw,
27};
28use tracing::{debug, instrument, trace, warn};
29
30use super::super::Context;
31use crate::{RoomInfo, StateChanges, store::BaseStateStore};
32
33pub fn global(events: &[Raw<AnyGlobalAccountDataEvent>]) -> Global {
35 Global::process(events)
36}
37
38#[must_use]
39pub struct Global {
40 parsed_events: Vec<AnyGlobalAccountDataEvent>,
41 raw_by_type: BTreeMap<GlobalAccountDataEventType, Raw<AnyGlobalAccountDataEvent>>,
42}
43
44impl Global {
45 fn process(events: &[Raw<AnyGlobalAccountDataEvent>]) -> Self {
47 let _timer = timer!(tracing::Level::TRACE, "Global::process (global account data)");
48
49 let mut raw_by_type = BTreeMap::new();
50 let mut parsed_events = Vec::new();
51
52 for raw_event in events {
53 let event = match raw_event.deserialize() {
54 Ok(e) => e,
55 Err(e) => {
56 let event_type: Option<String> = raw_event.get_field("type").ok().flatten();
57 warn!(event_type, "Failed to deserialize a global account data event: {e}");
58 continue;
59 }
60 };
61
62 raw_by_type.insert(event.event_type(), raw_event.clone());
63 parsed_events.push(event);
64 }
65
66 Self { raw_by_type, parsed_events }
67 }
68
69 pub fn push_rules(&self) -> Option<&Raw<AnyGlobalAccountDataEvent>> {
71 self.raw_by_type.get(&GlobalAccountDataEventType::PushRules)
72 }
73
74 #[instrument(skip_all)]
80 fn process_direct_rooms(
81 &self,
82 events: &[AnyGlobalAccountDataEvent],
83 state_store: &BaseStateStore,
84 state_changes: &mut StateChanges,
85 ) {
86 for event in events {
87 let AnyGlobalAccountDataEvent::Direct(direct_event) = event else { continue };
88
89 let mut new_dms = HashMap::<&RoomId, HashSet<OwnedDirectUserIdentifier>>::new();
90
91 for (user_identifier, rooms) in direct_event.content.iter() {
92 for room_id in rooms {
93 new_dms.entry(room_id).or_default().insert(user_identifier.clone());
94 }
95 }
96
97 let rooms = state_store.rooms();
98 let mut old_dms = rooms
99 .iter()
100 .filter_map(|r| {
101 let direct_targets = r.direct_targets();
102 (!direct_targets.is_empty()).then(|| (r.room_id(), direct_targets))
103 })
104 .collect::<HashMap<_, _>>();
105
106 for (room_id, new_direct_targets) in new_dms {
108 if let Some(old_direct_targets) = old_dms.remove(&room_id)
109 && old_direct_targets == new_direct_targets
110 {
111 continue;
112 }
113 trace!(?room_id, targets = ?new_direct_targets, "Marking room as direct room");
114 map_info(room_id, state_changes, state_store, |info| {
115 info.base_info.dm_targets = new_direct_targets;
116 });
117 }
118
119 for room_id in old_dms.keys() {
121 trace!(?room_id, "Unmarking room as direct room");
122 map_info(room_id, state_changes, state_store, |info| {
123 info.base_info.dm_targets.clear();
124 });
125 }
126 }
127 }
128
129 pub async fn apply(mut self, context: &mut Context, state_store: &BaseStateStore) {
131 let _timer = timer!(tracing::Level::TRACE, "Global::apply (global account data)");
132
133 mem::swap(&mut context.state_changes.account_data, &mut self.raw_by_type);
135
136 let has_new_direct_room_data = self
138 .parsed_events
139 .iter()
140 .any(|event| event.event_type() == GlobalAccountDataEventType::Direct);
141
142 if has_new_direct_room_data {
143 self.process_direct_rooms(&self.parsed_events, state_store, &mut context.state_changes);
144 } else if let Ok(Some(direct_account_data)) =
145 state_store.get_account_data_event(GlobalAccountDataEventType::Direct).await
146 {
147 debug!("Found direct room data in the Store, applying it");
148 if let Ok(direct_account_data) = direct_account_data.deserialize() {
149 self.process_direct_rooms(
150 &[direct_account_data],
151 state_store,
152 &mut context.state_changes,
153 );
154 } else {
155 warn!("Failed to deserialize direct room account data");
156 }
157 }
158 }
159}
160
161fn map_info<F: FnOnce(&mut RoomInfo)>(
164 room_id: &RoomId,
165 changes: &mut StateChanges,
166 store: &BaseStateStore,
167 f: F,
168) {
169 if let Some(info) = changes.room_infos.get_mut(room_id) {
170 f(info);
171 } else if let Some(room) = store.room(room_id) {
172 let mut info = room.clone_info();
173 f(&mut info);
174 changes.add_room(info);
175 } else if store.already_logged_missing_room.lock().insert(room_id.to_owned()) {
176 debug!(room = %room_id, "couldn't find room in state changes or store");
177 }
178}