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