twilight_cache_any_backend/
backend.rs

1use core::fmt::Display;
2
3use async_trait::async_trait;
4use twilight_model::{
5    channel::{ReactionType, StageInstance},
6    id::{
7        marker::{
8            ChannelMarker, EmojiMarker, GenericMarker, GuildMarker, MessageMarker, RoleMarker,
9            StageMarker, StickerMarker, UserMarker,
10        },
11        Id,
12    },
13    user::CurrentUser,
14};
15
16use crate::{
17    cache,
18    model::{
19        CachedActivity, CachedAttachment, CachedChannel, CachedEmbed, CachedEmbedField,
20        CachedEmoji, CachedGuild, CachedMember, CachedMessage, CachedMessageSticker,
21        CachedPresence, CachedReaction, CachedRole, CachedSticker,
22    },
23};
24
25/// Implemented on backend errors, for example `sqlx::Error`
26pub trait Error: Display + Send {}
27
28impl<E: Error> From<E> for cache::Error<E> {
29    fn from(err: E) -> Self {
30        Self::Backend(err)
31    }
32}
33
34/// Provides methods to add or replace data in the cache
35///
36/// This is for adding support for a backend, users of the cache itself only
37/// need the methods in [`super::Cache`]
38///
39/// All of the IDs are unique unless documented otherwise, this means if the ID
40/// is already in the cache, the new value's fields should overwrite the current
41/// fields, most backends abstract over this
42///
43/// # This trait is not complete
44///
45/// You should expose the backend so that users can filter the results in the
46/// query, for example they can do `SELECT *  FROM users WHERE name = ?`
47///
48/// It's also advisable to implement your backend library's traits to
49/// (de)serialize Discord models for the backend to streamline your codebase
50///
51/// Creating indexes for every ID field/column (for example, both `user_id` and
52/// `guild_id` in `users`) will be a huge performance improvement
53///
54/// # Example
55///
56/// Though the example uses PostgresSQL, you can use this library with any SQL
57/// or NoSQL backend
58///
59/// ```ignore
60/// use twilight_cache::backend::Backend;
61/// use twilight_model::id::{
62///     marker::{GuildMarker, UserMarker},
63///     Id,
64/// };
65///
66/// struct MyCache {
67///     pub db: sql_library::Database, // Or add a getter method instead of making the field public
68/// };
69///
70/// impl MyCache {
71///     fn new() {
72///         let db = sql_library::Database::connect("postgresql://localhost/discord");
73///         db.query("CREATE UNIQUE INDEX channels_idx ON channels (channel_id);");
74///         db.query("CREATE INDEX channels_guild_id_idx ON channels (guild_id);");
75///     }
76/// }
77///
78/// impl Backend for MyCache {
79///     type Error = sqlx::Error;
80///
81///     async fn upsert_channel(&self, channel: CachedChannel) -> Result<(), Self::Error> {
82///         sqlx::query!(
83///             channel.id,
84///             // Other fields here
85///             "INSERT INTO channels (id, ...) VALUES ($1, ...)"
86///         ).exec(&self.db)?;
87///         Ok(())
88///     }
89///     // Implement other methods similarly
90/// }
91///
92/// impl Cache for MyCache {
93///     // Implement the methods here, usually using getter queries
94/// }
95/// ```
96#[async_trait]
97pub trait Backend {
98    /// The error type the backend returns, for example `sqlx::Error`
99    type Error: Error;
100
101    /// Set or replace the current user information of the bot
102    async fn set_current_user(&self, current_user: CurrentUser) -> Result<(), Self::Error>;
103
104    /// Add or replace a channel in the cache
105    async fn upsert_channel(&self, channel: CachedChannel) -> Result<(), Self::Error>;
106
107    /// Remove a channel from the cache
108    async fn delete_channel(&self, channel_id: Id<ChannelMarker>) -> Result<(), Self::Error>;
109
110    /// Remove a guild's channels from the cache
111    ///
112    /// This should be something like `DELETE FROM channels WHERE guild_id = ?`
113    async fn delete_guild_channels(&self, guild_id: Id<GuildMarker>) -> Result<(), Self::Error>;
114
115    /// Add a DM channel to the cache, both of the IDs are unique
116    async fn upsert_private_channel(
117        &self,
118        channel_id: Id<ChannelMarker>,
119        user_id: Id<UserMarker>,
120    ) -> Result<(), Self::Error>;
121
122    /// Add or replace a guild in the cache
123    async fn upsert_guild(&self, guild: CachedGuild) -> Result<(), Self::Error>;
124
125    /// Remove a channel from the cache
126    async fn delete_guild(&self, guild_id: Id<GuildMarker>) -> Result<(), Self::Error>;
127
128    /// Add or replace an emoji in the cache
129    async fn upsert_emoji(&self, emoji: CachedEmoji) -> Result<(), Self::Error>;
130
131    /// Remove an emoji from the cache
132    async fn delete_emoji(&self, emoji_id: Id<EmojiMarker>) -> Result<(), Self::Error>;
133
134    /// Remove a guild's emojis from the cache
135    ///
136    /// This should be something like `DELETE FROM emojis WHERE guild_id = ?`
137    async fn delete_guild_emojis(&self, guild_id: Id<GuildMarker>) -> Result<(), Self::Error>;
138
139    /// Add or replace a sticker in the cache
140    async fn upsert_sticker(&self, sticker: CachedSticker) -> Result<(), Self::Error>;
141
142    /// Remove a sticker from the cache
143    async fn delete_sticker(&self, sticker_id: Id<StickerMarker>) -> Result<(), Self::Error>;
144
145    /// Remove a guild's stickers from the cache
146    ///
147    /// This should be something like `DELETE FROM stickers WHERE guild_id = ?`
148    async fn delete_guild_stickers(&self, guild_id: Id<GuildMarker>) -> Result<(), Self::Error>;
149
150    /// Add or replace a member in the cache
151    async fn upsert_member(&self, member: CachedMember) -> Result<(), Self::Error>;
152
153    /// Remove a member from the cache
154    async fn delete_member(
155        &self,
156        user_id: Id<UserMarker>,
157        guild_id: Id<GuildMarker>,
158    ) -> Result<(), Self::Error>;
159
160    /// Remove a guild's stickers from the cache
161    ///
162    /// This should be something like `DELETE FROM members WHERE guild_id = ?`
163    async fn delete_guild_members(&self, guild_id: Id<GuildMarker>) -> Result<(), Self::Error>;
164
165    /// Add or replace a message in the cache
166    async fn upsert_message(&self, message: CachedMessage) -> Result<(), Self::Error>;
167
168    /// Remove a message from the cache
169    async fn delete_message(&self, message_id: Id<MessageMarker>) -> Result<(), Self::Error>;
170
171    /// Add an embed to the cache, the embed ID is unique
172    async fn upsert_embed(&self, embed: CachedEmbed) -> Result<(), Self::Error>;
173
174    /// Remove an embed from the cache
175    async fn delete_embed(&self, embed_id: Id<GenericMarker>) -> Result<(), Self::Error>;
176
177    /// Add an embed field to the cache, the embed ID is not unique
178    async fn upsert_embed_field(&self, embed_field: CachedEmbedField) -> Result<(), Self::Error>;
179
180    /// Remove an embed's fields from the cache
181    ///
182    /// This should be something like `DELETE FROM embed_fields WHERE embed_id =
183    /// ?`
184    async fn delete_embed_fields(&self, embed_id: Id<GenericMarker>) -> Result<(), Self::Error>;
185
186    /// Get embeds of a message by its ID
187    ///
188    /// This method is used internally in [`super::Cache::embeds`]
189    async fn cached_embeds(
190        &self,
191        message_id: Id<MessageMarker>,
192    ) -> Result<Vec<CachedEmbed>, Self::Error>;
193
194    /// Get fields of an embed by its ID
195    ///
196    /// This method is used internally in [`super::Cache::embeds`]
197    async fn embed_fields(
198        &self,
199        embed_id: Id<GenericMarker>,
200    ) -> Result<Vec<CachedEmbedField>, Self::Error>;
201
202    /// Add an attachment to the cache, the message ID is not unique
203    async fn upsert_attachment(&self, attachment: CachedAttachment) -> Result<(), Self::Error>;
204
205    /// Remove a message's attachments from the cache
206    ///
207    /// This should be something like `DELETE FROM attachments WHERE message_id
208    /// = ?`
209    async fn delete_message_attachments(
210        &self,
211        message_id: Id<MessageMarker>,
212    ) -> Result<(), Self::Error>;
213
214    /// Add or replace a message sticker in the cache
215    async fn upsert_message_sticker(
216        &self,
217        sticker: CachedMessageSticker,
218    ) -> Result<(), Self::Error>;
219
220    /// Remove a message's stickers from the cache
221    ///
222    /// This should be something like `DELETE FROM message_stickers WHERE
223    /// message_id = ?`
224    async fn delete_message_stickers(
225        &self,
226        message_id: Id<MessageMarker>,
227    ) -> Result<(), Self::Error>;
228
229    /// Add or replace a presence in the cache
230    async fn upsert_presence(&self, presence: CachedPresence) -> Result<(), Self::Error>;
231
232    /// Remove a presence from the cache
233    async fn delete_presence(&self, user_id: Id<UserMarker>) -> Result<(), Self::Error>;
234
235    /// Remove a guild's presences from the cache
236    ///
237    /// This should be something like `DELETE FROM presences WHERE guild_id = ?`
238    async fn delete_guild_presences(&self, guild_id: Id<GuildMarker>) -> Result<(), Self::Error>;
239
240    /// Add an activity to the cache, none of the activity's IDs are unique
241    async fn upsert_activity(&self, activity: CachedActivity) -> Result<(), Self::Error>;
242
243    /// Remove a user's activities from the cache
244    ///
245    /// This should be something like `DELETE FROM activities WHERE user_id = ?`
246    async fn delete_user_activities(&self, user_id: Id<UserMarker>) -> Result<(), Self::Error>;
247
248    /// Add a reaction to the cache, only the combination of message ID, user ID
249    /// and emoji is unique, they're not unique on their own
250    async fn upsert_reaction(&self, reaction: CachedReaction) -> Result<(), Self::Error>;
251
252    /// Remove a reaction from the cache
253    async fn delete_reaction(
254        &self,
255        message_id: Id<MessageMarker>,
256        user_id: Id<UserMarker>,
257        emoji: ReactionType,
258    ) -> Result<(), Self::Error>;
259
260    /// Remove a message's reactions of the given emoji from the cache
261    ///
262    /// This should be something like `DELETE FROM reactions WHERE message_id =
263    /// ? AND emoji = ?`
264    async fn delete_message_reactions_by_emoji(
265        &self,
266        message_id: Id<MessageMarker>,
267        emoji: ReactionType,
268    ) -> Result<(), Self::Error>;
269
270    /// Remove a message's reactions from the cache
271    ///
272    /// This should be something like `DELETE FROM reactions WHERE message_id =
273    /// ?`
274    async fn delete_message_reactions(
275        &self,
276        message_id: Id<MessageMarker>,
277    ) -> Result<(), Self::Error>;
278
279    /// Add or update a role to the cache, only the combination of role ID and
280    /// user ID is unique, they're not unique on their own
281    ///
282    /// When updating roles, make sure the user IDs remain untouched
283    async fn upsert_role(&self, role: CachedRole) -> Result<(), Self::Error>;
284
285    /// Remove a role from the cache
286    async fn delete_role(&self, role_id: Id<RoleMarker>) -> Result<(), Self::Error>;
287
288    /// Remove a guild's roles from the cache
289    ///
290    /// This should be something like `DELETE FROM roles WHERE guild_id = ?`
291    async fn delete_guild_roles(&self, guild_id: Id<GuildMarker>) -> Result<(), Self::Error>;
292
293    /// Remove a member's roles from the cache
294    ///
295    /// This should be something like `DELETE FROM roles WHERE guild_id = ? AND
296    /// user_id = ?`
297    async fn delete_member_roles(
298        &self,
299        guild_id: Id<GuildMarker>,
300        user_id: Id<UserMarker>,
301    ) -> Result<(), Self::Error>;
302
303    /// Add or replace a stage instance in the cache
304    async fn upsert_stage_instance(&self, stage: StageInstance) -> Result<(), Self::Error>;
305
306    /// Remove a stage instance from the cache
307    async fn delete_stage_instance(&self, stage_id: Id<StageMarker>) -> Result<(), Self::Error>;
308
309    /// Remove a guild's stage instance from the cache
310    ///
311    /// This should be something like `DELETE FROM stage_instances WHERE
312    /// guild_id = ?`
313    async fn delete_guild_stage_instances(
314        &self,
315        guild_id: Id<GuildMarker>,
316    ) -> Result<(), Self::Error>;
317}