backend/objects/
channel.rs

1use diesel::{
2    ExpressionMethods, Insertable, QueryDsl, Queryable, Selectable, SelectableHelper, delete,
3    insert_into, update,
4};
5use diesel_async::RunQueryDsl;
6use serde::{Deserialize, Serialize};
7use uuid::Uuid;
8
9use crate::{
10    Conn,
11    error::Error,
12    schema::{channel_permissions, channels, messages},
13    utils::{CHANNEL_REGEX, CacheFns, order_by_is_above},
14};
15
16use super::{HasIsAbove, HasUuid, Message, load_or_empty, message::MessageBuilder};
17
18#[derive(Queryable, Selectable, Insertable, Clone, Debug)]
19#[diesel(table_name = channels)]
20#[diesel(check_for_backend(diesel::pg::Pg))]
21struct ChannelBuilder {
22    uuid: Uuid,
23    guild_uuid: Uuid,
24    name: String,
25    description: Option<String>,
26    is_above: Option<Uuid>,
27}
28
29impl ChannelBuilder {
30    async fn build(self, conn: &mut Conn) -> Result<Channel, Error> {
31        use self::channel_permissions::dsl::*;
32        let channel_permission: Vec<ChannelPermission> = load_or_empty(
33            channel_permissions
34                .filter(channel_uuid.eq(self.uuid))
35                .select(ChannelPermission::as_select())
36                .load(conn)
37                .await,
38        )?;
39
40        Ok(Channel {
41            uuid: self.uuid,
42            guild_uuid: self.guild_uuid,
43            name: self.name,
44            description: self.description,
45            is_above: self.is_above,
46            permissions: channel_permission,
47        })
48    }
49}
50
51#[derive(Serialize, Deserialize, Clone, Debug)]
52pub struct Channel {
53    pub uuid: Uuid,
54    pub guild_uuid: Uuid,
55    name: String,
56    description: Option<String>,
57    pub is_above: Option<Uuid>,
58    pub permissions: Vec<ChannelPermission>,
59}
60
61#[derive(Serialize, Deserialize, Clone, Queryable, Selectable, Debug)]
62#[diesel(table_name = channel_permissions)]
63#[diesel(check_for_backend(diesel::pg::Pg))]
64pub struct ChannelPermission {
65    pub role_uuid: Uuid,
66    pub permissions: i64,
67}
68
69impl HasUuid for Channel {
70    fn uuid(&self) -> &Uuid {
71        self.uuid.as_ref()
72    }
73}
74
75impl HasIsAbove for Channel {
76    fn is_above(&self) -> Option<&Uuid> {
77        self.is_above.as_ref()
78    }
79}
80
81impl Channel {
82    pub async fn fetch_all(conn: &mut Conn, guild_uuid: Uuid) -> Result<Vec<Self>, Error> {
83        use channels::dsl;
84        let channel_builders: Vec<ChannelBuilder> = load_or_empty(
85            dsl::channels
86                .filter(dsl::guild_uuid.eq(guild_uuid))
87                .select(ChannelBuilder::as_select())
88                .load(conn)
89                .await,
90        )?;
91
92        let mut channels = vec![];
93
94        for builder in channel_builders {
95            channels.push(builder.build(conn).await?);
96        }
97
98        Ok(channels)
99    }
100
101    pub async fn fetch_one(
102        conn: &mut Conn,
103        cache_pool: &redis::Client,
104        channel_uuid: Uuid,
105    ) -> Result<Self, Error> {
106        if let Ok(cache_hit) = cache_pool.get_cache_key(channel_uuid.to_string()).await {
107            return Ok(cache_hit);
108        }
109
110        use channels::dsl;
111        let channel_builder: ChannelBuilder = dsl::channels
112            .filter(dsl::uuid.eq(channel_uuid))
113            .select(ChannelBuilder::as_select())
114            .get_result(conn)
115            .await?;
116
117        let channel = channel_builder.build(conn).await?;
118
119        cache_pool
120            .set_cache_key(channel_uuid.to_string(), channel.clone(), 60)
121            .await?;
122
123        Ok(channel)
124    }
125
126    pub async fn new(
127        conn: &mut Conn,
128        cache_pool: &redis::Client,
129        guild_uuid: Uuid,
130        name: String,
131        description: Option<String>,
132    ) -> Result<Self, Error> {
133        if !CHANNEL_REGEX.is_match(&name) {
134            return Err(Error::BadRequest("Channel name is invalid".to_string()));
135        }
136
137        let channel_uuid = Uuid::now_v7();
138
139        let channels = Self::fetch_all(conn, guild_uuid).await?;
140
141        let channels_ordered = order_by_is_above(channels).await?;
142
143        let last_channel = channels_ordered.last();
144
145        let new_channel = ChannelBuilder {
146            uuid: channel_uuid,
147            guild_uuid,
148            name: name.clone(),
149            description: description.clone(),
150            is_above: None,
151        };
152
153        insert_into(channels::table)
154            .values(new_channel.clone())
155            .execute(conn)
156            .await?;
157
158        if let Some(old_last_channel) = last_channel {
159            use channels::dsl;
160            update(channels::table)
161                .filter(dsl::uuid.eq(old_last_channel.uuid))
162                .set(dsl::is_above.eq(new_channel.uuid))
163                .execute(conn)
164                .await?;
165        }
166
167        // returns different object because there's no reason to build the channelbuilder (wastes 1 database request)
168        let channel = Self {
169            uuid: channel_uuid,
170            guild_uuid,
171            name,
172            description,
173            is_above: None,
174            permissions: vec![],
175        };
176
177        cache_pool
178            .set_cache_key(channel_uuid.to_string(), channel.clone(), 1800)
179            .await?;
180
181        if cache_pool
182            .get_cache_key::<Vec<Channel>>(format!("{guild_uuid}_channels"))
183            .await
184            .is_ok()
185        {
186            cache_pool
187                .del_cache_key(format!("{guild_uuid}_channels"))
188                .await?;
189        }
190
191        Ok(channel)
192    }
193
194    pub async fn delete(self, conn: &mut Conn, cache_pool: &redis::Client) -> Result<(), Error> {
195        use channels::dsl;
196        match update(channels::table)
197            .filter(dsl::is_above.eq(self.uuid))
198            .set(dsl::is_above.eq(None::<Uuid>))
199            .execute(conn)
200            .await
201        {
202            Ok(r) => Ok(r),
203            Err(diesel::result::Error::NotFound) => Ok(0),
204            Err(e) => Err(e),
205        }?;
206
207        delete(channels::table)
208            .filter(dsl::uuid.eq(self.uuid))
209            .execute(conn)
210            .await?;
211
212        match update(channels::table)
213            .filter(dsl::is_above.eq(self.uuid))
214            .set(dsl::is_above.eq(self.is_above))
215            .execute(conn)
216            .await
217        {
218            Ok(r) => Ok(r),
219            Err(diesel::result::Error::NotFound) => Ok(0),
220            Err(e) => Err(e),
221        }?;
222
223        if cache_pool
224            .get_cache_key::<Channel>(self.uuid.to_string())
225            .await
226            .is_ok()
227        {
228            cache_pool.del_cache_key(self.uuid.to_string()).await?;
229        }
230
231        if cache_pool
232            .get_cache_key::<Vec<Channel>>(format!("{}_channels", self.guild_uuid))
233            .await
234            .is_ok()
235        {
236            cache_pool
237                .del_cache_key(format!("{}_channels", self.guild_uuid))
238                .await?;
239        }
240
241        Ok(())
242    }
243
244    pub async fn fetch_messages(
245        &self,
246        conn: &mut Conn,
247        cache_pool: &redis::Client,
248        amount: i64,
249        offset: i64,
250    ) -> Result<Vec<Message>, Error> {
251        use messages::dsl;
252        let message_builders: Vec<MessageBuilder> = load_or_empty(
253            dsl::messages
254                .filter(dsl::channel_uuid.eq(self.uuid))
255                .select(MessageBuilder::as_select())
256                .order(dsl::uuid.desc())
257                .limit(amount)
258                .offset(offset)
259                .load(conn)
260                .await,
261        )?;
262
263        let mut messages = vec![];
264
265        for builder in message_builders {
266            messages.push(builder.build(conn, cache_pool).await?);
267        }
268
269        Ok(messages)
270    }
271
272    pub async fn new_message(
273        &self,
274        conn: &mut Conn,
275        cache_pool: &redis::Client,
276        user_uuid: Uuid,
277        message: String,
278        reply_to: Option<Uuid>,
279    ) -> Result<Message, Error> {
280        let message_uuid = Uuid::now_v7();
281
282        let message = MessageBuilder {
283            uuid: message_uuid,
284            channel_uuid: self.uuid,
285            user_uuid,
286            message,
287            reply_to,
288        };
289
290        insert_into(messages::table)
291            .values(message.clone())
292            .execute(conn)
293            .await?;
294
295        message.build(conn, cache_pool).await
296    }
297
298    pub async fn set_name(
299        &mut self,
300        conn: &mut Conn,
301        cache_pool: &redis::Client,
302        new_name: String,
303    ) -> Result<(), Error> {
304        if !CHANNEL_REGEX.is_match(&new_name) {
305            return Err(Error::BadRequest("Channel name is invalid".to_string()));
306        }
307
308        use channels::dsl;
309        update(channels::table)
310            .filter(dsl::uuid.eq(self.uuid))
311            .set(dsl::name.eq(&new_name))
312            .execute(conn)
313            .await?;
314
315        self.name = new_name;
316
317        if cache_pool
318            .get_cache_key::<Channel>(self.uuid.to_string())
319            .await
320            .is_ok()
321        {
322            cache_pool.del_cache_key(self.uuid.to_string()).await?;
323        }
324
325        if cache_pool
326            .get_cache_key::<Vec<Channel>>(format!("{}_channels", self.guild_uuid))
327            .await
328            .is_ok()
329        {
330            cache_pool
331                .del_cache_key(format!("{}_channels", self.guild_uuid))
332                .await?;
333        }
334
335        Ok(())
336    }
337
338    pub async fn set_description(
339        &mut self,
340        conn: &mut Conn,
341        cache_pool: &redis::Client,
342        new_description: String,
343    ) -> Result<(), Error> {
344        use channels::dsl;
345        update(channels::table)
346            .filter(dsl::uuid.eq(self.uuid))
347            .set(dsl::description.eq(&new_description))
348            .execute(conn)
349            .await?;
350
351        self.description = Some(new_description);
352
353        if cache_pool
354            .get_cache_key::<Channel>(self.uuid.to_string())
355            .await
356            .is_ok()
357        {
358            cache_pool.del_cache_key(self.uuid.to_string()).await?;
359        }
360
361        if cache_pool
362            .get_cache_key::<Vec<Channel>>(format!("{}_channels", self.guild_uuid))
363            .await
364            .is_ok()
365        {
366            cache_pool
367                .del_cache_key(format!("{}_channels", self.guild_uuid))
368                .await?;
369        }
370
371        Ok(())
372    }
373
374    pub async fn move_channel(
375        &mut self,
376        conn: &mut Conn,
377        cache_pool: &redis::Client,
378        new_is_above: Uuid,
379    ) -> Result<(), Error> {
380        use channels::dsl;
381        let old_above_uuid: Option<Uuid> = match dsl::channels
382            .filter(dsl::is_above.eq(self.uuid))
383            .select(dsl::uuid)
384            .get_result(conn)
385            .await
386        {
387            Ok(r) => Ok(Some(r)),
388            Err(diesel::result::Error::NotFound) => Ok(None),
389            Err(e) => Err(e),
390        }?;
391
392        if let Some(uuid) = old_above_uuid {
393            update(channels::table)
394                .filter(dsl::uuid.eq(uuid))
395                .set(dsl::is_above.eq(None::<Uuid>))
396                .execute(conn)
397                .await?;
398        }
399
400        match update(channels::table)
401            .filter(dsl::is_above.eq(new_is_above))
402            .set(dsl::is_above.eq(self.uuid))
403            .execute(conn)
404            .await
405        {
406            Ok(r) => Ok(r),
407            Err(diesel::result::Error::NotFound) => Ok(0),
408            Err(e) => Err(e),
409        }?;
410
411        update(channels::table)
412            .filter(dsl::uuid.eq(self.uuid))
413            .set(dsl::is_above.eq(new_is_above))
414            .execute(conn)
415            .await?;
416
417        if let Some(uuid) = old_above_uuid {
418            update(channels::table)
419                .filter(dsl::uuid.eq(uuid))
420                .set(dsl::is_above.eq(self.is_above))
421                .execute(conn)
422                .await?;
423        }
424
425        self.is_above = Some(new_is_above);
426
427        if cache_pool
428            .get_cache_key::<Channel>(self.uuid.to_string())
429            .await
430            .is_ok()
431        {
432            cache_pool.del_cache_key(self.uuid.to_string()).await?;
433        }
434
435        if cache_pool
436            .get_cache_key::<Vec<Channel>>(format!("{}_channels", self.guild_uuid))
437            .await
438            .is_ok()
439        {
440            cache_pool
441                .del_cache_key(format!("{}_channels", self.guild_uuid))
442                .await?;
443        }
444
445        Ok(())
446    }
447}