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::discovery::{
22 get_authorization_server_metadata::v1::AuthorizationServerMetadata,
23 get_capabilities::v3::Capabilities,
24 },
25};
26use tokio::sync::Mutex as AsyncMutex;
27
28use crate::HttpError;
29
30/// A collection of in-memory data that the `Client` might want to cache to
31/// avoid hitting the homeserver every time users request the data.
32pub(crate) struct ClientCaches {
33 /// The supported versions of the homeserver.
34 ///
35 /// We only want to cache:
36 ///
37 /// - The versions prefilled with `ClientBuilder::server_versions()`
38 /// - The versions fetched from an *authenticated* request to the server.
39 pub(crate) supported_versions: Cache<SupportedVersions, Arc<HttpError>>,
40 /// Well-known information.
41 pub(super) well_known: Cache<Option<WellKnownResponse>, ()>,
42 /// OAuth 2.0 server metadata.
43 pub(crate) server_metadata: Cache<AuthorizationServerMetadata, ()>,
44 /// Homeserver capabilities.
45 pub(crate) homeserver_capabilities: Cache<Capabilities, Arc<HttpError>>,
46}
47
48/// A cached value that can either be set or not set, used to avoid confusion
49/// between a value that is set to `None` (because it doesn't exist) and a value
50/// that has not been cached yet.
51#[derive(Clone, Debug)]
52pub(crate) enum CachedValue<Value> {
53 /// A value has been cached.
54 Cached(Value),
55 /// Nothing has been cached yet.
56 NotSet,
57}
58
59impl<Value> CachedValue<Value> {
60 /// Takes the value out of the `CachedValue`, leaving a `NotSet` in its
61 /// place.
62 pub(super) fn take(&mut self) -> Option<Value> {
63 let prev = std::mem::replace(self, Self::NotSet);
64
65 match prev {
66 Self::Cached(value) => Some(value),
67 Self::NotSet => None,
68 }
69 }
70}
71
72/// A cache in the [`ClientCaches`].
73pub(crate) struct Cache<Value, Error> {
74 /// The value that is cached.
75 value: Mutex<CachedValue<TtlValue<Value>>>,
76 /// Lock making sure that we are only refreshing the value once at a time.
77 ///
78 /// Stores the error that happened during the last refresh, if any.
79 pub(crate) refresh_lock: AsyncMutex<Result<(), Error>>,
80}
81
82impl<Value, Error> Cache<Value, Error> {
83 /// Construct a new empty `Cache`.
84 pub(crate) fn new() -> Self {
85 Self::with_value(CachedValue::NotSet)
86 }
87
88 /// Construct a new `Cache` with the given value.
89 pub(crate) fn with_value(value: CachedValue<TtlValue<Value>>) -> Self {
90 Self { value: Mutex::new(value), refresh_lock: AsyncMutex::new(Ok(())) }
91 }
92
93 /// Set the value.
94 pub(crate) fn set_value(&self, value: TtlValue<Value>) {
95 *self.value.lock() = CachedValue::Cached(value);
96 }
97
98 /// Reset the cache by dropping the value.
99 pub(crate) fn reset(&self) {
100 self.value.lock().take();
101 }
102}
103
104impl<Value, Error> Cache<Value, Error>
105where
106 Value: Clone,
107{
108 /// Get the cached value.
109 pub(crate) fn value(&self) -> CachedValue<TtlValue<Value>> {
110 self.value.lock().clone()
111 }
112}