Skip to main content

fractal/session/remote/
cache.rs

1use std::{cell::RefCell, fmt, rc::Rc};
2
3use quick_cache::unsync::Cache;
4use ruma::{OwnedRoomOrAliasId, OwnedUserId, RoomId};
5
6use super::{RemoteRoom, RemoteUser};
7use crate::{session::Session, utils::matrix::MatrixRoomIdUri};
8
9/// The data of the [`RemoteCache`].
10struct RemoteCacheData {
11    /// Remote rooms.
12    rooms: RefCell<Cache<OwnedRoomOrAliasId, RemoteRoom>>,
13    /// Remote users.
14    users: RefCell<Cache<OwnedUserId, RemoteUser>>,
15}
16
17/// An API to query remote data and cache it.
18#[derive(Clone)]
19pub(crate) struct RemoteCache {
20    session: Session,
21    data: Rc<RemoteCacheData>,
22}
23
24impl RemoteCache {
25    /// Construct a new `RemoteCache` for the given session.
26    pub(crate) fn new(session: Session) -> Self {
27        Self {
28            session,
29            data: RemoteCacheData {
30                rooms: Cache::new(30).into(),
31                users: Cache::new(30).into(),
32            }
33            .into(),
34        }
35    }
36
37    /// Get the remote room for the given URI.
38    pub(crate) fn room(&self, uri: MatrixRoomIdUri) -> RemoteRoom {
39        let mut rooms = self.data.rooms.borrow_mut();
40
41        // Check if the room is in the cache.
42        if let Some(room) = rooms.get(&uri.id) {
43            room.load_data_if_stale();
44            return room.clone();
45        }
46
47        // Check if the alias or ID matches a room in the cache, in case the URI uses
48        // another ID than the one we used as a key for the cache.
49        let mut found_id = None;
50        let id_or_alias = <&RoomId>::try_from(&*uri.id);
51
52        for (id, room) in rooms.iter() {
53            match id_or_alias {
54                Ok(room_id) => {
55                    if room.room_id().is_some_and(|id| id == room_id) {
56                        found_id = Some(id.clone());
57                        break;
58                    }
59                }
60                Err(room_alias) => {
61                    if room
62                        .canonical_alias()
63                        .is_some_and(|alias| alias == room_alias)
64                    {
65                        found_id = Some(id.clone());
66                        break;
67                    }
68                }
69            }
70        }
71
72        if let Some(id) = found_id {
73            let room = rooms.get(&id).expect("room should be in cache");
74            room.load_data_if_stale();
75            return room.clone();
76        }
77
78        // We did not find it, create the room.
79        let id = uri.id.clone();
80        let room = RemoteRoom::new(&self.session, uri);
81        rooms.insert(id, room.clone());
82
83        room
84    }
85
86    /// Get the remote user for the given ID.
87    pub(crate) fn user(&self, user_id: OwnedUserId) -> RemoteUser {
88        let mut users = self.data.users.borrow_mut();
89
90        // Check if the user is in the cache.
91        if let Some(user) = users.get(&user_id) {
92            user.load_profile_if_stale();
93            return user.clone();
94        }
95
96        // We did not find it, create the user.
97        let user = RemoteUser::new(&self.session, user_id.clone());
98        users.insert(user_id, user.clone());
99
100        user
101    }
102}
103
104impl fmt::Debug for RemoteCache {
105    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
106        f.debug_struct("RemoteCache").finish_non_exhaustive()
107    }
108}