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}