Skip to main content

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