Skip to main content

matrix_sdk/client/
caches.rs

1// Copyright 2025 The Matrix.org Foundation C.I.C.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::sync::Arc;
16
17use matrix_sdk_base::store::WellKnownResponse;
18use matrix_sdk_common::{locks::Mutex, ttl::TtlValue};
19use ruma::api::{
20    SupportedVersions,
21    client::{
22        discovery::{
23            get_authorization_server_metadata::v1::AuthorizationServerMetadata,
24            get_capabilities::v3::Capabilities,
25        },
26        rtc::RtcTransport,
27    },
28};
29use tokio::sync::Mutex as AsyncMutex;
30
31use crate::HttpError;
32
33/// A collection of in-memory data that the `Client` might want to cache to
34/// avoid hitting the homeserver every time users request the data.
35pub(crate) struct ClientCaches {
36    /// The supported versions of the homeserver.
37    ///
38    /// We only want to cache:
39    ///
40    /// - The versions prefilled with `ClientBuilder::server_versions()`
41    /// - The versions fetched from an *authenticated* request to the server.
42    pub(crate) supported_versions: Cache<SupportedVersions, Arc<HttpError>>,
43    /// Well-known information.
44    pub(super) well_known: Cache<Option<WellKnownResponse>, ()>,
45    /// OAuth 2.0 server metadata.
46    pub(crate) server_metadata: Cache<AuthorizationServerMetadata, ()>,
47    /// Homeserver capabilities.
48    pub(crate) homeserver_capabilities: Cache<Capabilities, Arc<HttpError>>,
49    /// RTC transports advertised by the homeserver
50    /// ([MSC4143](https://github.com/matrix-org/matrix-spec-proposals/pull/4143)).
51    ///
52    /// `None` is cached to represent a homeserver that doesn't implement the
53    /// discovery endpoint, as distinct from one that advertises no transports
54    /// (`Some(vec![])`).
55    pub(crate) rtc_transports: Cache<Option<Vec<RtcTransport>>, Arc<HttpError>>,
56}
57
58/// A cached value that can either be set or not set, used to avoid confusion
59/// between a value that is set to `None` (because it doesn't exist) and a value
60/// that has not been cached yet.
61#[derive(Clone, Debug)]
62pub(crate) enum CachedValue<Value> {
63    /// A value has been cached.
64    Cached(Value),
65    /// Nothing has been cached yet.
66    NotSet,
67}
68
69impl<Value> CachedValue<Value> {
70    /// Takes the value out of the `CachedValue`, leaving a `NotSet` in its
71    /// place.
72    pub(super) fn take(&mut self) -> Option<Value> {
73        let prev = std::mem::replace(self, Self::NotSet);
74
75        match prev {
76            Self::Cached(value) => Some(value),
77            Self::NotSet => None,
78        }
79    }
80}
81
82/// A cache in the [`ClientCaches`].
83pub(crate) struct Cache<Value, Error> {
84    /// The value that is cached.
85    value: Mutex<CachedValue<TtlValue<Value>>>,
86    /// Lock making sure that we are only refreshing the value once at a time.
87    ///
88    /// Stores the error that happened during the last refresh, if any.
89    pub(crate) refresh_lock: AsyncMutex<Result<(), Error>>,
90}
91
92impl<Value, Error> Cache<Value, Error> {
93    /// Construct a new empty `Cache`.
94    pub(crate) fn new() -> Self {
95        Self::with_value(CachedValue::NotSet)
96    }
97
98    /// Construct a new `Cache` with the given value.
99    pub(crate) fn with_value(value: CachedValue<TtlValue<Value>>) -> Self {
100        Self { value: Mutex::new(value), refresh_lock: AsyncMutex::new(Ok(())) }
101    }
102
103    /// Set the value.
104    pub(crate) fn set_value(&self, value: TtlValue<Value>) {
105        *self.value.lock() = CachedValue::Cached(value);
106    }
107
108    /// Reset the cache by dropping the value.
109    pub(crate) fn reset(&self) {
110        self.value.lock().take();
111    }
112}
113
114impl<Value, Error> Cache<Value, Error>
115where
116    Value: Clone,
117{
118    /// Get the cached value.
119    pub(crate) fn value(&self) -> CachedValue<TtlValue<Value>> {
120        self.value.lock().clone()
121    }
122}