1use crate::model::{Error, Result};
2use super::{DataManager, drivers::common};
3use oiseau::{cache::Cache, execute, query_row, params};
4
5pub const NAME_REGEX: &str = r"[^\w_\-\.,!]+";
6
7impl DataManager {
8 pub async fn init(&self) -> Result<()> {
9 let conn = match self.0.connect().await {
10 Ok(c) => c,
11 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
12 };
13
14 execute!(&conn, common::CREATE_TABLE_USERS).unwrap();
15 execute!(&conn, common::CREATE_TABLE_COMMUNITIES).unwrap();
16 execute!(&conn, common::CREATE_TABLE_POSTS).unwrap();
17 execute!(&conn, common::CREATE_TABLE_MEMBERSHIPS).unwrap();
18 execute!(&conn, common::CREATE_TABLE_REACTIONS).unwrap();
19 execute!(&conn, common::CREATE_TABLE_NOTIFICATIONS).unwrap();
20 execute!(&conn, common::CREATE_TABLE_USERFOLLOWS).unwrap();
21 execute!(&conn, common::CREATE_TABLE_USERBLOCKS).unwrap();
22 execute!(&conn, common::CREATE_TABLE_IPBANS).unwrap();
23 execute!(&conn, common::CREATE_TABLE_AUDIT_LOG).unwrap();
24 execute!(&conn, common::CREATE_TABLE_REPORTS).unwrap();
25 execute!(&conn, common::CREATE_TABLE_USER_WARNINGS).unwrap();
26 execute!(&conn, common::CREATE_TABLE_REQUESTS).unwrap();
27 execute!(&conn, common::CREATE_TABLE_QUESTIONS).unwrap();
28 execute!(&conn, common::CREATE_TABLE_IPBLOCKS).unwrap();
29 execute!(&conn, common::CREATE_TABLE_CHANNELS).unwrap();
30 execute!(&conn, common::CREATE_TABLE_MESSAGES).unwrap();
31 execute!(&conn, common::CREATE_TABLE_UPLOADS).unwrap();
32 execute!(&conn, common::CREATE_TABLE_EMOJIS).unwrap();
33 execute!(&conn, common::CREATE_TABLE_STACKS).unwrap();
34 execute!(&conn, common::CREATE_TABLE_DRAFTS).unwrap();
35 execute!(&conn, common::CREATE_TABLE_POLLS).unwrap();
36 execute!(&conn, common::CREATE_TABLE_POLLVOTES).unwrap();
37 execute!(&conn, common::CREATE_TABLE_APPS).unwrap();
38 execute!(&conn, common::CREATE_TABLE_STACKBLOCKS).unwrap();
39 execute!(&conn, common::CREATE_TABLE_JOURNALS).unwrap();
40 execute!(&conn, common::CREATE_TABLE_NOTES).unwrap();
41 execute!(&conn, common::CREATE_TABLE_MESSAGE_REACTIONS).unwrap();
42 execute!(&conn, common::CREATE_TABLE_INVITE_CODES).unwrap();
43 execute!(&conn, common::CREATE_TABLE_DOMAINS).unwrap();
44 execute!(&conn, common::CREATE_TABLE_SERVICES).unwrap();
45 execute!(&conn, common::CREATE_TABLE_APP_DATA).unwrap();
46 execute!(&conn, common::CREATE_TABLE_LETTERS).unwrap();
47 execute!(&conn, common::CREATE_TABLE_TRANSFERS).unwrap();
48 execute!(&conn, common::CREATE_TABLE_PRODUCTS).unwrap();
49 execute!(&conn, common::CREATE_TABLE_ADS).unwrap();
50
51 for x in common::VERSION_MIGRATIONS.split(";") {
52 execute!(&conn, x).unwrap();
53 }
54
55 self.0
56 .1
57 .set("atto.active_connections:users".to_string(), "0".to_string())
58 .await;
59 self.0
60 .1
61 .set("atto.active_connections:chats".to_string(), "0".to_string())
62 .await;
63
64 Ok(())
65 }
66
67 pub async fn get_table_row_count(&self, table: &str) -> Result<i32> {
68 let conn = match self.0.connect().await {
69 Ok(c) => c,
70 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
71 };
72
73 let res = query_row!(
74 &conn,
75 &format!("SELECT COUNT(*)::int FROM {}", table),
76 params![],
77 |x| Ok(x.get::<usize, i32>(0))
78 );
79
80 if let Err(e) = res {
81 return Err(Error::DatabaseError(e.to_string()));
82 }
83
84 Ok(res.unwrap())
85 }
86
87 pub async fn get_table_row_count_where(&self, table: &str, r#where: &str) -> Result<i32> {
88 let conn = match self.0.connect().await {
89 Ok(c) => c,
90 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
91 };
92
93 let res = query_row!(
94 &conn,
95 &format!("SELECT COUNT(*)::int FROM {} WHERE {}", table, r#where),
96 params![],
97 |x| Ok(x.get::<usize, i32>(0))
98 );
99
100 if let Err(e) = res {
101 return Err(Error::DatabaseError(e.to_string()));
102 }
103
104 Ok(res.unwrap())
105 }
106}
107
108#[macro_export]
109macro_rules! auto_method {
110 ($name:ident()@$select_fn:ident -> $query:literal --name=$name_:literal --returns=$returns_:tt) => {
111 pub async fn $name(&self, id: usize) -> Result<$returns_> {
112 let conn = match self.0.connect().await {
113 Ok(c) => c,
114 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
115 };
116
117 let res = query_row!(&conn, $query, &[&(id as i64)], |x| {
118 Ok(Self::$select_fn(x))
119 });
120
121 if res.is_err() {
122 return Err(Error::GeneralNotFound($name_.to_string()));
123 }
124
125 Ok(res.unwrap())
126 }
127 };
128
129 ($name:ident()@$select_fn:ident -> $query:literal --name=$name_:literal --returns=$returns_:tt --cache-key-tmpl=$cache_key_tmpl:literal) => {
130 pub async fn $name(&self, id: usize) -> Result<$returns_> {
131 if let Some(cached) = self.0.1.get(format!($cache_key_tmpl, id)).await {
132 if let Ok(c) = serde_json::from_str(&cached) {
133 return Ok(c);
134 }
135 }
136
137 let conn = match self.0.connect().await {
138 Ok(c) => c,
139 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
140 };
141
142 let res = oiseau::query_row!(&conn, $query, &[&(id as i64)], |x| {
143 Ok(Self::$select_fn(x))
144 });
145
146 if res.is_err() {
147 return Err(Error::GeneralNotFound($name_.to_string()));
148 }
149
150 let x = res.unwrap();
151 self.0
152 .1
153 .set(
154 format!($cache_key_tmpl, id),
155 serde_json::to_string(&x).unwrap(),
156 )
157 .await;
158
159 Ok(x)
160 }
161 };
162
163 ($name:ident($selector_t:ty)@$select_fn:ident -> $query:literal --name=$name_:literal --returns=$returns_:tt) => {
164 pub async fn $name(&self, selector: $selector_t) -> Result<$returns_> {
165 let conn = match self.0.connect().await {
166 Ok(c) => c,
167 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
168 };
169
170 let res =
171 oiseau::query_row!(&conn, $query, &[&selector], |x| { Ok(Self::$select_fn(x)) });
172
173 if res.is_err() {
174 return Err(Error::GeneralNotFound($name_.to_string()));
175 }
176
177 Ok(res.unwrap())
178 }
179 };
180
181 ($name:ident($selector_t:ty)@$select_fn:ident -> $query:literal --name=$name_:literal --returns=$returns_:tt --cache-key-tmpl=$cache_key_tmpl:literal) => {
182 pub async fn $name(&self, selector: $selector_t) -> Result<$returns_> {
183 let selector = selector.to_string().to_lowercase();
184
185 if let Some(cached) = self.0.1.get(format!($cache_key_tmpl, selector)).await {
186 return Ok(serde_json::from_str(&cached).unwrap());
187 }
188
189 let conn = match self.0.connect().await {
190 Ok(c) => c,
191 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
192 };
193
194 let res = query_row!(&conn, $query, &[&selector.to_string()], |x| {
195 Ok(Self::$select_fn(x))
196 });
197
198 if res.is_err() {
199 return Err(Error::GeneralNotFound($name_.to_string()));
200 }
201
202 let x = res.unwrap();
203 self.0
204 .1
205 .set(
206 format!($cache_key_tmpl, selector),
207 serde_json::to_string(&x).unwrap(),
208 )
209 .await;
210
211 Ok(x)
212 }
213 };
214
215 ($name:ident($selector_t:ty as i64)@$select_fn:ident -> $query:literal --name=$name_:literal --returns=$returns_:tt --cache-key-tmpl=$cache_key_tmpl:literal) => {
216 pub async fn $name(&self, selector: $selector_t) -> Result<$returns_> {
217 if let Some(cached) = self
218 .0
219 .1
220 .get(format!($cache_key_tmpl, selector.to_string()))
221 .await
222 {
223 match serde_json::from_str(&cached) {
224 Ok(x) => return Ok(x),
225 Err(_) => {
226 self.0
227 .1
228 .remove(format!($cache_key_tmpl, selector.to_string()))
229 .await
230 }
231 };
232 }
233
234 let conn = match self.0.connect().await {
235 Ok(c) => c,
236 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
237 };
238
239 let res = oiseau::query_row!(&conn, $query, &[&(selector as i64)], |x| {
240 Ok(Self::$select_fn(x))
241 });
242
243 if res.is_err() {
244 return Err(Error::GeneralNotFound($name_.to_string()));
245 }
246
247 let x = res.unwrap();
248 self.0
249 .1
250 .set(
251 format!($cache_key_tmpl, selector),
252 serde_json::to_string(&x).unwrap(),
253 )
254 .await;
255
256 Ok(x)
257 }
258 };
259
260 ($name:ident()@$select_fn:ident:$permission:expr; -> $query:literal) => {
261 pub async fn $name(&self, id: usize, user: &User) -> Result<()> {
262 let y = self.$select_fn(id).await?;
263
264 if user.id != y.owner {
265 if !user.permissions.check($permission) {
266 return Err(Error::NotAllowed);
267 } else {
268 self.create_audit_log_entry($crate::model::moderation::AuditLogEntry::new(
269 user.id,
270 format!("invoked `{}` with x value `{id}`", stringify!($name)),
271 ))
272 }
273 }
274
275 let conn = match self.0.connect().await {
276 Ok(c) => c,
277 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
278 };
279
280 let res = execute!(&conn, $query, &[&(id as i64)]);
281
282 if let Err(e) = res {
283 return Err(Error::DatabaseError(e.to_string()));
284 }
285
286 Ok(())
287 }
288 };
289
290 ($name:ident()@$select_fn:ident:$permission:expr; -> $query:literal --cache-key-tmpl=$cache_key_tmpl:literal) => {
291 pub async fn $name(&self, id: usize, user: &User) -> Result<()> {
292 let y = self.$select_fn(id).await?;
293
294 if user.id != y.owner {
295 if !user.permissions.check($permission) {
296 return Err(Error::NotAllowed);
297 } else {
298 self.create_audit_log_entry($crate::model::moderation::AuditLogEntry::new(
299 user.id,
300 format!("invoked `{}` with x value `{id}`", stringify!($name)),
301 ))
302 }
303 }
304
305 let conn = match self.0.connect().await {
306 Ok(c) => c,
307 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
308 };
309
310 let res = execute!(&conn, $query, &[&(id as i64)]);
311
312 if let Err(e) = res {
313 return Err(Error::DatabaseError(e.to_string()));
314 }
315
316 self.0.1.remove(format!($cache_key_tmpl, id)).await;
317
318 Ok(())
319 }
320 };
321
322 ($name:ident($x:ty)@$select_fn:ident:$permission:expr; -> $query:literal) => {
323 pub async fn $name(&self, id: usize, user: &User, x: $x) -> Result<()> {
324 let y = self.$select_fn(id).await?;
325
326 if user.id != y.owner {
327 if !user.permissions.check($permission) {
328 return Err(Error::NotAllowed);
329 } else {
330 self.create_audit_log_entry($crate::model::moderation::AuditLogEntry::new(
331 user.id,
332 format!("invoked `{}` with x value `{id}`", stringify!($name)),
333 ))
334 .await?
335 }
336 }
337
338 let conn = match self.0.connect().await {
339 Ok(c) => c,
340 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
341 };
342
343 let res = execute!(&conn, $query, &[&x, &(id as i64)]);
344
345 if let Err(e) = res {
346 return Err(Error::DatabaseError(e.to_string()));
347 }
348
349 Ok(())
350 }
351 };
352
353 ($name:ident($x:ty)@$select_fn:ident:$permission:expr; -> $query:literal --cache-key-tmpl=$cache_key_tmpl:literal) => {
354 pub async fn $name(&self, id: usize, user: &User, x: $x) -> Result<()> {
355 let y = self.$select_fn(id).await?;
356
357 if user.id != y.owner {
358 if !user.permissions.check($permission) {
359 return Err(Error::NotAllowed);
360 } else {
361 self.create_audit_log_entry($crate::model::moderation::AuditLogEntry::new(
362 user.id,
363 format!("invoked `{}` with x value `{x}`", stringify!($name)),
364 ))
365 .await?
366 }
367 }
368
369 let conn = match self.0.connect().await {
370 Ok(c) => c,
371 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
372 };
373
374 let res = execute!(&conn, $query, params![&x, &(id as i64)]);
375
376 if let Err(e) = res {
377 return Err(Error::DatabaseError(e.to_string()));
378 }
379
380 self.0.1.remove(format!($cache_key_tmpl, id)).await;
381
382 Ok(())
383 }
384 };
385
386 ($name:ident($x:ty)@$select_fn:ident:$permission:expr; -> $query:literal --serde) => {
387 pub async fn $name(&self, id: usize, user: &User, x: $x) -> Result<()> {
388 let y = self.$select_fn(id).await?;
389
390 if user.id != y.owner {
391 if !user.permissions.check($permission) {
392 return Err(Error::NotAllowed);
393 } else {
394 self.create_audit_log_entry($crate::model::moderation::AuditLogEntry::new(
395 user.id,
396 format!("invoked `{}` with x value `{id}`", stringify!($name), id),
397 ))
398 .await?
399 }
400 }
401
402 let conn = match self.0.connect().await {
403 Ok(c) => c,
404 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
405 };
406
407 let res = execute!(
408 &conn,
409 $query,
410 &[&serde_json::to_string(&x).unwrap(), &(id as i64)]
411 );
412
413 if let Err(e) = res {
414 return Err(Error::DatabaseError(e.to_string()));
415 }
416
417 Ok(())
418 }
419 };
420
421 ($name:ident($x:ty)@$select_fn:ident:$permission:expr; -> $query:literal --serde --cache-key-tmpl=$cache_key_tmpl:literal) => {
422 pub async fn $name(&self, id: usize, user: &User, x: $x) -> Result<()> {
423 let y = self.$select_fn(id).await?;
424
425 if user.id != y.owner {
426 if !user.permissions.check($permission) {
427 return Err(Error::NotAllowed);
428 } else {
429 self.create_audit_log_entry($crate::model::moderation::AuditLogEntry::new(
430 user.id,
431 format!("invoked `{}` with x value `{id}`", stringify!($name)),
432 ))
433 .await?
434 }
435 }
436
437 let conn = match self.0.connect().await {
438 Ok(c) => c,
439 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
440 };
441
442 let res = execute!(
443 &conn,
444 $query,
445 params![&serde_json::to_string(&x).unwrap(), &(id as i64)]
446 );
447
448 if let Err(e) = res {
449 return Err(Error::DatabaseError(e.to_string()));
450 }
451
452 self.0.1.remove(format!($cache_key_tmpl, id)).await;
453
454 Ok(())
455 }
456 };
457
458 ($name:ident($x:ty) -> $query:literal) => {
459 pub async fn $name(&self, id: usize, x: $x) -> Result<()> {
460 let conn = match self.0.connect().await {
461 Ok(c) => c,
462 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
463 };
464
465 let res = execute!(&conn, $query, &[&x, &(id as i64)]);
466
467 if let Err(e) = res {
468 return Err(Error::DatabaseError(e.to_string()));
469 }
470
471 Ok(())
472 }
473 };
474
475 ($name:ident($x:ty) -> $query:literal --cache-key-tmpl=$cache_key_tmpl:literal) => {
476 pub async fn $name(&self, id: usize, x: $x) -> Result<()> {
477 let conn = match self.0.connect().await {
478 Ok(c) => c,
479 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
480 };
481
482 let res = execute!(&conn, $query, &[&x, &(id as i64)]);
483
484 if let Err(e) = res {
485 return Err(Error::DatabaseError(e.to_string()));
486 }
487
488 self.0.1.remove(format!($cache_key_tmpl, id)).await;
489
490 Ok(())
491 }
492 };
493
494 ($name:ident($x:ty) -> $query:literal --serde) => {
495 pub async fn $name(&self, id: usize, x: $x) -> Result<()> {
496 let conn = match self.0.connect().await {
497 Ok(c) => c,
498 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
499 };
500
501 let res = execute!(
502 &conn,
503 $query,
504 &[&serde_json::to_string(&x).unwrap(), &(id as i64)]
505 );
506
507 if let Err(e) = res {
508 return Err(Error::DatabaseError(e.to_string()));
509 }
510
511 Ok(())
512 }
513 };
514
515 ($name:ident($x:ty) -> $query:literal --serde --cache-key-tmpl=$cache_key_tmpl:literal) => {
516 pub async fn $name(&self, id: usize, x: $x) -> Result<()> {
517 let conn = match self.0.connect().await {
518 Ok(c) => c,
519 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
520 };
521
522 let res = execute!(
523 &conn,
524 $query,
525 params![&serde_json::to_string(&x).unwrap(), &(id as i64)]
526 );
527
528 if let Err(e) = res {
529 return Err(Error::DatabaseError(e.to_string()));
530 }
531
532 self.0.1.remove(format!($cache_key_tmpl, id)).await;
533
534 Ok(())
535 }
536 };
537
538 ($name:ident() -> $query:literal --cache-key-tmpl=$cache_key_tmpl:literal --incr) => {
539 pub async fn $name(&self, id: usize) -> Result<()> {
540 let conn = match self.0.connect().await {
541 Ok(c) => c,
542 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
543 };
544
545 let res = execute!(&conn, $query, &[&(id as i64)]);
546
547 if let Err(e) = res {
548 return Err(Error::DatabaseError(e.to_string()));
549 }
550
551 self.0.1.remove(format!($cache_key_tmpl, id)).await;
552
553 Ok(())
554 }
555 };
556
557 ($name:ident() -> $query:literal --cache-key-tmpl=$cache_key_tmpl:literal --decr) => {
558 pub async fn $name(&self, id: usize) -> Result<()> {
559 let conn = match self.0.connect().await {
560 Ok(c) => c,
561 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
562 };
563
564 let res = execute!(&conn, $query, &[&(id as i64)]);
565
566 if let Err(e) = res {
567 return Err(Error::DatabaseError(e.to_string()));
568 }
569
570 self.0.1.remove(format!($cache_key_tmpl, id)).await;
571
572 Ok(())
573 }
574 };
575
576 ($name:ident()@$select_fn:ident -> $query:literal --cache-key-tmpl=$cache_key_tmpl:literal --decr=$field:ident) => {
577 pub async fn $name(&self, id: usize) -> Result<()> {
578 let y = self.$select_fn(id).await?;
579
580 if (y.$field as isize) - 1 < 0 {
581 return Ok(());
582 }
583
584 let conn = match self.0.connect().await {
585 Ok(c) => c,
586 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
587 };
588
589 let res = execute!(&conn, $query, &[&(id as i64)]);
590
591 if let Err(e) = res {
592 return Err(Error::DatabaseError(e.to_string()));
593 }
594
595 self.0.1.remove(format!($cache_key_tmpl, id)).await;
596
597 Ok(())
598 }
599 };
600
601 ($name:ident()@$select_fn:ident:$permission:expr; -> $query:literal --cache-key-tmpl=$cache_key_tmpl:ident) => {
602 pub async fn $name(&self, id: usize, user: &User) -> Result<()> {
603 let y = self.$select_fn(id).await?;
604
605 if user.id != y.owner {
606 if !user.permissions.check($permission) {
607 return Err(Error::NotAllowed);
608 } else {
609 self.create_audit_log_entry($crate::model::moderation::AuditLogEntry::new(
610 user.id,
611 format!("invoked `{}` with x value `{id}`", stringify!($name)),
612 ))
613 .await?
614 }
615 }
616
617 let conn = match self.0.connect().await {
618 Ok(c) => c,
619 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
620 };
621
622 let res = execute!(&conn, $query, &[&(id as i64)]);
623
624 if let Err(e) = res {
625 return Err(Error::DatabaseError(e.to_string()));
626 }
627
628 self.$cache_key_tmpl(&y).await;
629
630 Ok(())
631 }
632 };
633
634 ($name:ident($x:ty)@$select_fn:ident:$permission:expr; -> $query:literal --cache-key-tmpl=$cache_key_tmpl:ident) => {
635 pub async fn $name(&self, id: usize, user: &User, x: $x) -> Result<()> {
636 let y = self.$select_fn(id).await?;
637
638 if user.id != y.owner {
639 if !user.permissions.check($permission) {
640 return Err(Error::NotAllowed);
641 } else {
642 self.create_audit_log_entry($crate::model::moderation::AuditLogEntry::new(
643 user.id,
644 format!("invoked `{}` with x value `{x}`", stringify!($name)),
645 ))
646 .await?
647 }
648 }
649
650 let conn = match self.0.connect().await {
651 Ok(c) => c,
652 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
653 };
654
655 let res = execute!(&conn, $query, params![&x, &(id as i64)]);
656
657 if let Err(e) = res {
658 return Err(Error::DatabaseError(e.to_string()));
659 }
660
661 self.$cache_key_tmpl(&y).await;
662
663 Ok(())
664 }
665 };
666
667 ($name:ident($x:ty)@$select_fn:ident -> $query:literal --cache-key-tmpl=$cache_key_tmpl:ident) => {
668 pub async fn $name(&self, id: usize, x: $x) -> Result<()> {
669 let y = self.$select_fn(id).await?;
670
671 let conn = match self.0.connect().await {
672 Ok(c) => c,
673 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
674 };
675
676 let res = execute!(&conn, $query, params![&x, &(id as i64)]);
677
678 if let Err(e) = res {
679 return Err(Error::DatabaseError(e.to_string()));
680 }
681
682 self.$cache_key_tmpl(&y).await;
683
684 Ok(())
685 }
686 };
687
688 ($name:ident($x:ty)@$select_fn:ident -> $query:literal --serde --cache-key-tmpl=$cache_key_tmpl:ident) => {
689 pub async fn $name(&self, id: usize, x: $x) -> Result<()> {
690 let y = self.$select_fn(id).await?;
691
692 let conn = match self.0.connect().await {
693 Ok(c) => c,
694 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
695 };
696
697 let res = execute!(
698 &conn,
699 $query,
700 params![&serde_json::to_string(&x).unwrap(), &(id as i64)]
701 );
702
703 if let Err(e) = res {
704 return Err(Error::DatabaseError(e.to_string()));
705 }
706
707 self.$cache_key_tmpl(&y).await;
708
709 Ok(())
710 }
711 };
712
713 ($name:ident($x:ty)@$select_fn:ident:$permission:expr; -> $query:literal --serde --cache-key-tmpl=$cache_key_tmpl:ident) => {
714 pub async fn $name(&self, id: usize, user: &User, x: $x) -> Result<()> {
715 let y = self.$select_fn(id).await?;
716
717 if user.id != y.owner {
718 if !user.permissions.check($permission) {
719 return Err(Error::NotAllowed);
720 } else {
721 self.create_audit_log_entry($crate::model::moderation::AuditLogEntry::new(
722 user.id,
723 format!("invoked `{}` with x value `{x:?}`", stringify!($name)),
724 ))
725 .await?
726 }
727 }
728
729 let conn = match self.0.connect().await {
730 Ok(c) => c,
731 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
732 };
733
734 let res = execute!(
735 &conn,
736 $query,
737 params![&serde_json::to_string(&x).unwrap(), &(id as i64)]
738 );
739
740 if let Err(e) = res {
741 return Err(Error::DatabaseError(e.to_string()));
742 }
743
744 self.$cache_key_tmpl(&y).await;
745
746 Ok(())
747 }
748 };
749
750 ($name:ident()@$select_fn:ident -> $query:literal --cache-key-tmpl=$cache_key_tmpl:ident --incr) => {
751 pub async fn $name(&self, id: usize) -> Result<()> {
752 let y = self.$select_fn(id).await?;
753
754 let conn = match self.0.connect().await {
755 Ok(c) => c,
756 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
757 };
758
759 let res = execute!(&conn, $query, &[&(id as i64)]);
760
761 if let Err(e) = res {
762 return Err(Error::DatabaseError(e.to_string()));
763 }
764
765 self.$cache_key_tmpl(&y).await;
766
767 Ok(())
768 }
769 };
770
771 ($name:ident()@$select_fn:ident -> $query:literal --cache-key-tmpl=$cache_key_tmpl:ident --decr=$field:ident) => {
772 pub async fn $name(&self, id: usize) -> Result<()> {
773 let y = self.$select_fn(id).await?;
774
775 if (y.$field as isize) - 1 < 0 {
776 return Ok(());
777 }
778
779 let conn = match self.0.connect().await {
780 Ok(c) => c,
781 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
782 };
783
784 let res = execute!(&conn, $query, &[&(id as i64)]);
785
786 if let Err(e) = res {
787 return Err(Error::DatabaseError(e.to_string()));
788 }
789
790 self.$cache_key_tmpl(&y).await;
791
792 Ok(())
793 }
794 };
795}