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};