mas_storage/lib.rs
1// Copyright 2024 New Vector Ltd.
2// Copyright 2021-2024 The Matrix.org Foundation C.I.C.
3//
4// SPDX-License-Identifier: AGPL-3.0-only
5// Please see LICENSE in the repository root for full details.
6
7//! Interactions with the storage backend
8//!
9//! This crate provides a set of traits that can be implemented to interact with
10//! the storage backend. Those traits are called repositories and are grouped by
11//! the type of data they manage.
12//!
13//! Each of those reposotories can be accessed via the [`RepositoryAccess`]
14//! trait. This trait can be wrapped in a [`BoxRepository`] to allow using it
15//! without caring about the underlying storage backend, and without carrying
16//! around the generic type parameter.
17//!
18//! This crate also defines a [`Clock`] trait that can be used to abstract the
19//! way the current time is retrieved. It has two implementation:
20//! [`SystemClock`] that uses the system time and [`MockClock`] which is useful
21//! for testing.
22//!
23//! [`MockClock`]: crate::clock::MockClock
24//!
25//! # Defining a new repository
26//!
27//! To define a new repository, you have to:
28//! 1. Define a new (async) repository trait, with the methods you need
29//! 2. Write an implementation of this trait for each storage backend you want
30//! (currently only for [`mas-storage-pg`])
31//! 3. Make it accessible via the [`RepositoryAccess`] trait
32//!
33//! The repository trait definition should look like this:
34//!
35//! ```ignore
36//! #[async_trait]
37//! pub trait FakeDataRepository: Send + Sync {
38//! /// The error type returned by the repository
39//! type Error;
40//!
41//! /// Lookup a [`FakeData`] by its ID
42//! ///
43//! /// Returns `None` if no [`FakeData`] was found
44//! ///
45//! /// # Parameters
46//! ///
47//! /// * `id`: The ID of the [`FakeData`] to lookup
48//! ///
49//! /// # Errors
50//! ///
51//! /// Returns [`Self::Error`] if the underlying repository fails
52//! async fn lookup(&mut self, id: Ulid) -> Result<Option<FakeData>, Self::Error>;
53//!
54//! /// Create a new [`FakeData`]
55//! ///
56//! /// Returns the newly-created [`FakeData`].
57//! ///
58//! /// # Parameters
59//! ///
60//! /// * `rng`: The random number generator to use
61//! /// * `clock`: The clock used to generate timestamps
62//! ///
63//! /// # Errors
64//! ///
65//! /// Returns [`Self::Error`] if the underlying repository fails
66//! async fn add(
67//! &mut self,
68//! rng: &mut (dyn RngCore + Send),
69//! clock: &dyn Clock,
70//! ) -> Result<FakeData, Self::Error>;
71//! }
72//!
73//! repository_impl!(FakeDataRepository:
74//! async fn lookup(&mut self, id: Ulid) -> Result<Option<FakeData>, Self::Error>;
75//! async fn add(
76//! &mut self,
77//! rng: &mut (dyn RngCore + Send),
78//! clock: &dyn Clock,
79//! ) -> Result<FakeData, Self::Error>;
80//! );
81//! ```
82//!
83//! Four things to note with the implementation:
84//!
85//! 1. It defined an assocated error type, and all functions are faillible,
86//! and use that error type
87//! 2. Lookups return an `Result<Option<T>, Self::Error>`, because 'not found'
88//! errors are usually cases that are handled differently
89//! 3. Operations that need to record the current type use a [`Clock`]
90//! parameter. Operations that need to generate new IDs also use a random
91//! number generator.
92//! 4. All the methods use an `&mut self`. This is ensures only one operation
93//! is done at a time on a single repository instance.
94//!
95//! Then update the [`RepositoryAccess`] trait to make the new repository
96//! available:
97//!
98//! ```ignore
99//! /// Access the various repositories the backend implements.
100//! pub trait RepositoryAccess: Send {
101//! /// The backend-specific error type used by each repository.
102//! type Error: std::error::Error + Send + Sync + 'static;
103//!
104//! // ...other repositories...
105//!
106//! /// Get a [`FakeDataRepository`]
107//! fn fake_data<'c>(&'c mut self) -> Box<dyn FakeDataRepository<Error = Self::Error> + 'c>;
108//! }
109//! ```
110
111#![deny(clippy::future_not_send, missing_docs)]
112#![allow(clippy::module_name_repetitions)]
113
114pub mod clock;
115pub mod pagination;
116pub(crate) mod repository;
117mod utils;
118
119pub mod app_session;
120pub mod compat;
121pub mod oauth2;
122pub mod policy_data;
123pub mod queue;
124pub mod upstream_oauth2;
125pub mod user;
126
127pub use self::{
128 clock::{Clock, SystemClock},
129 pagination::{Page, Pagination},
130 repository::{
131 BoxRepository, Repository, RepositoryAccess, RepositoryError, RepositoryTransaction,
132 },
133 utils::{BoxClock, BoxRng, MapErr},
134};