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