mas_storage/
lib.rs

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