Skip to main content

tetratto_core2/database/
common.rs

1use crate::model::{Error, Result};
2use super::{DataManager, drivers::common};
3use oiseau::{cache::Cache, execute, params, query_row, query_rows};
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_GROUPS).unwrap();
16        execute!(&conn, common::CREATE_TABLE_GROUP_MEMBERSHIPS).unwrap();
17        execute!(&conn, common::CREATE_TABLE_POLLS).unwrap();
18        execute!(&conn, common::CREATE_TABLE_POLL_VOTES).unwrap();
19        execute!(&conn, common::CREATE_TABLE_POSTS).unwrap();
20        execute!(&conn, common::CREATE_TABLE_REACTIONS).unwrap();
21        execute!(&conn, common::CREATE_TABLE_NOTIFICATIONS).unwrap();
22        execute!(&conn, common::CREATE_TABLE_USER_FOLLOWS).unwrap();
23        execute!(&conn, common::CREATE_TABLE_USER_BLOCKS).unwrap();
24        execute!(&conn, common::CREATE_TABLE_IP_BANS).unwrap();
25        execute!(&conn, common::CREATE_TABLE_AUDIT_LOG).unwrap();
26        execute!(&conn, common::CREATE_TABLE_REPORTS).unwrap();
27        execute!(&conn, common::CREATE_TABLE_USER_WARNINGS).unwrap();
28        execute!(&conn, common::CREATE_TABLE_REQUESTS).unwrap();
29        execute!(&conn, common::CREATE_TABLE_QUESTIONS).unwrap();
30        execute!(&conn, common::CREATE_TABLE_IP_BLOCKS).unwrap();
31        execute!(&conn, common::CREATE_TABLE_DRAFTS).unwrap();
32        execute!(&conn, common::CREATE_TABLE_APPS).unwrap();
33
34        for x in common::VERSION_MIGRATIONS.split(";") {
35            execute!(&conn, x).unwrap();
36        }
37
38        self.0
39            .1
40            .set("atto.active_connections:users".to_string(), "0".to_string())
41            .await;
42
43        self.1.init().await.expect("failed to init buckets manager");
44        Ok(())
45    }
46
47    pub async fn get_table_row_count(&self, table: &str) -> Result<i32> {
48        let conn = match self.0.connect().await {
49            Ok(c) => c,
50            Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
51        };
52
53        let res = query_row!(
54            &conn,
55            &format!("SELECT COUNT(*)::int FROM {}", table),
56            params![],
57            |x| Ok(x.get::<usize, i32>(0))
58        );
59
60        if let Err(e) = res {
61            return Err(Error::DatabaseError(e.to_string()));
62        }
63
64        Ok(res.unwrap())
65    }
66
67    pub async fn get_table_row_count_where(&self, table: &str, r#where: &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 {} WHERE {}", table, r#where),
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 list_tables(&self) -> Result<Vec<String>> {
88        let conn = match self.0.connect().await {
89            Ok(x) => x,
90            Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
91        };
92
93        let res = query_rows!(
94            &conn,
95            "SELECT tablename FROM pg_catalog.pg_tables WHERE schemaname != 'pg_catalog' AND schemaname != 'information_schema'",
96            params![],
97            |x| x.get::<usize, String>(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    // case 0
111    ($name:ident()@$select_fn:ident -> $query:literal --name=$name_:literal --returns=$returns_:tt) => {
112        pub async fn $name(&self, id: &$crate::model::id::Id) -> Result<$returns_> {
113            let conn = match self.0.connect().await {
114                Ok(c) => c,
115                Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
116            };
117
118            let res = query_row!(&conn, $query, &[&id.printable()], |x| {
119                Ok(Self::$select_fn(x))
120            });
121
122            if res.is_err() {
123                return Err(Error::GeneralNotFound($name_.to_string()));
124            }
125
126            Ok(res.unwrap())
127        }
128    };
129
130    // case 1
131    ($name:ident()@$select_fn:ident -> $query:literal --name=$name_:literal --returns=$returns_:tt --cache-key-tmpl=$cache_key_tmpl:literal) => {
132        pub async fn $name(&self, id: &$crate::model::id::Id) -> Result<$returns_> {
133            if let Some(cached) = self.0.1.get(format!($cache_key_tmpl, id)).await {
134                match serde_json::from_str(&cached) {
135                    Ok(x) => return Ok(x),
136                    Err(_) => {
137                        self.0.1.remove(format!($cache_key_tmpl, id)).await;
138                    }
139                }
140            }
141
142            let conn = match self.0.connect().await {
143                Ok(c) => c,
144                Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
145            };
146
147            let res = oiseau::query_row!(&conn, $query, &[&id.printable()], |x| {
148                Ok(Self::$select_fn(x))
149            });
150
151            if res.is_err() {
152                return Err(Error::GeneralNotFound($name_.to_string()));
153            }
154
155            let x = res.unwrap();
156            self.0
157                .1
158                .set(
159                    format!($cache_key_tmpl, id),
160                    serde_json::to_string(&x).unwrap(),
161                )
162                .await;
163
164            Ok(x)
165        }
166    };
167
168    // case 2
169    ($name:ident(Id :: usize)@$select_fn:ident -> $query:literal --name=$name_:literal --returns=$returns_:tt --cache-key-tmpl=$cache_key_tmpl:literal) => {
170        pub async fn $name(&self, id: &$crate::model::id::Id) -> Result<$returns_> {
171            if let Some(cached) = self.0.1.get(format!($cache_key_tmpl, id)).await {
172                match serde_json::from_str(&cached) {
173                    Ok(x) => return Ok(x),
174                    Err(_) => {
175                        self.0.1.remove(format!($cache_key_tmpl, id)).await;
176                    }
177                }
178            }
179
180            let conn = match self.0.connect().await {
181                Ok(c) => c,
182                Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
183            };
184
185            let res = oiseau::query_row!(&conn, $query, &[&(id.as_usize() as i64)], |x| {
186                Ok(Self::$select_fn(x))
187            });
188
189            if res.is_err() {
190                return Err(Error::GeneralNotFound($name_.to_string()));
191            }
192
193            let x = res.unwrap();
194            self.0
195                .1
196                .set(
197                    format!($cache_key_tmpl, id),
198                    serde_json::to_string(&x).unwrap(),
199                )
200                .await;
201
202            Ok(x)
203        }
204    };
205
206    // case 3
207    ($name:ident($selector_t:ty)@$select_fn:ident -> $query:literal --name=$name_:literal --returns=$returns_:tt) => {
208        pub async fn $name(&self, selector: $selector_t) -> Result<$returns_> {
209            let conn = match self.0.connect().await {
210                Ok(c) => c,
211                Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
212            };
213
214            let res =
215                oiseau::query_row!(&conn, $query, &[&selector], |x| { Ok(Self::$select_fn(x)) });
216
217            if res.is_err() {
218                return Err(Error::GeneralNotFound($name_.to_string()));
219            }
220
221            Ok(res.unwrap())
222        }
223    };
224
225    // case 4
226    ($name:ident($selector_t:ty)@$select_fn:ident -> $query:literal --name=$name_:literal --returns=$returns_:tt --cache-key-tmpl=$cache_key_tmpl:literal) => {
227        pub async fn $name(&self, selector: $selector_t) -> Result<$returns_> {
228            let selector = selector.to_string().to_lowercase();
229
230            if let Some(cached) = self.0.1.get(format!($cache_key_tmpl, selector)).await {
231                match serde_json::from_str(&cached) {
232                    Ok(x) => return Ok(x),
233                    Err(_) => {
234                        self.0.1.remove(format!($cache_key_tmpl, selector)).await;
235                    }
236                }
237            }
238
239            let conn = match self.0.connect().await {
240                Ok(c) => c,
241                Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
242            };
243
244            let res = query_row!(&conn, $query, &[&selector.to_string()], |x| {
245                Ok(Self::$select_fn(x))
246            });
247
248            if res.is_err() {
249                return Err(Error::GeneralNotFound($name_.to_string()));
250            }
251
252            let x = res.unwrap();
253            self.0
254                .1
255                .set(
256                    format!($cache_key_tmpl, selector),
257                    serde_json::to_string(&x).unwrap(),
258                )
259                .await;
260
261            Ok(x)
262        }
263    };
264
265    // case 5
266    ($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) => {
267        pub async fn $name(&self, selector: $selector_t) -> Result<$returns_> {
268            if let Some(cached) = self
269                .0
270                .1
271                .get(format!($cache_key_tmpl, selector.to_string()))
272                .await
273            {
274                match serde_json::from_str(&cached) {
275                    Ok(x) => return Ok(x),
276                    Err(_) => {
277                        self.0
278                            .1
279                            .remove(format!($cache_key_tmpl, selector.to_string()))
280                            .await
281                    }
282                };
283            }
284
285            let conn = match self.0.connect().await {
286                Ok(c) => c,
287                Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
288            };
289
290            let res = oiseau::query_row!(&conn, $query, &[&(selector as i64)], |x| {
291                Ok(Self::$select_fn(x))
292            });
293
294            if res.is_err() {
295                return Err(Error::GeneralNotFound($name_.to_string()));
296            }
297
298            let x = res.unwrap();
299            self.0
300                .1
301                .set(
302                    format!($cache_key_tmpl, selector),
303                    serde_json::to_string(&x).unwrap(),
304                )
305                .await;
306
307            Ok(x)
308        }
309    };
310
311    // case 6
312    ($name:ident()@$select_fn:ident:$permission:expr; -> $query:literal) => {
313        pub async fn $name(&self, id: &$crate::model::id::Id, user: &User) -> Result<()> {
314            let y = self.$select_fn(id).await?;
315
316            if user.id != y.owner {
317                if !user.permissions.check($permission) {
318                    return Err(Error::NotAllowed);
319                } else {
320                    self.create_audit_log_entry($crate::model::moderation::AuditLogEntry::new(
321                        user.id,
322                        format!("invoked `{}` with x value `{id}`", stringify!($name)),
323                    ))
324                }
325            }
326
327            let conn = match self.0.connect().await {
328                Ok(c) => c,
329                Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
330            };
331
332            let res = execute!(&conn, $query, &[&id.printable()]);
333
334            if let Err(e) = res {
335                return Err(Error::DatabaseError(e.to_string()));
336            }
337
338            Ok(())
339        }
340    };
341
342    // case 7
343    ($name:ident()@$select_fn:ident:$permission:expr; -> $query:literal --cache-key-tmpl=$cache_key_tmpl:literal) => {
344        pub async fn $name(&self, id: &$crate::model::id::Id, user: &User) -> Result<()> {
345            let y = self.$select_fn(id).await?;
346
347            if user.id != y.owner {
348                if !user.permissions.check($permission) {
349                    return Err(Error::NotAllowed);
350                } else {
351                    self.create_audit_log_entry($crate::model::moderation::AuditLogEntry::new(
352                        user.id,
353                        format!("invoked `{}` with x value `{id}`", stringify!($name)),
354                    ))
355                }
356            }
357
358            let conn = match self.0.connect().await {
359                Ok(c) => c,
360                Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
361            };
362
363            let res = execute!(&conn, $query, &[&id.printable()]);
364
365            if let Err(e) = res {
366                return Err(Error::DatabaseError(e.to_string()));
367            }
368
369            self.0.1.remove(format!($cache_key_tmpl, id)).await;
370
371            Ok(())
372        }
373    };
374
375    // case 8
376    ($name:ident($x:ty)@$select_fn:ident:$permission:expr; -> $query:literal) => {
377        pub async fn $name(&self, id: &$crate::model::id::Id, user: &User, x: $x) -> Result<()> {
378            let y = self.$select_fn(id).await?;
379
380            if user.id != y.owner {
381                if !user.permissions.check($permission) {
382                    return Err(Error::NotAllowed);
383                } else {
384                    self.create_audit_log_entry($crate::model::moderation::AuditLogEntry::new(
385                        user.id,
386                        format!("invoked `{}` with x value `{id}`", stringify!($name)),
387                    ))
388                    .await?
389                }
390            }
391
392            let conn = match self.0.connect().await {
393                Ok(c) => c,
394                Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
395            };
396
397            let res = execute!(&conn, $query, &[&x, &id.printable()]);
398
399            if let Err(e) = res {
400                return Err(Error::DatabaseError(e.to_string()));
401            }
402
403            Ok(())
404        }
405    };
406
407    // case 9
408    ($name:ident($x:ty)@$select_fn:ident:$permission:expr; -> $query:literal --cache-key-tmpl=$cache_key_tmpl:literal) => {
409        pub async fn $name(&self, id: &$crate::model::id::Id, user: &User, x: $x) -> Result<()> {
410            let y = self.$select_fn(id).await?;
411
412            if user.id != y.owner {
413                if !user.permissions.check($permission) {
414                    return Err(Error::NotAllowed);
415                } else {
416                    self.create_audit_log_entry($crate::model::moderation::AuditLogEntry::new(
417                        user.id,
418                        format!("invoked `{}` with x value `{x}`", stringify!($name)),
419                    ))
420                    .await?
421                }
422            }
423
424            let conn = match self.0.connect().await {
425                Ok(c) => c,
426                Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
427            };
428
429            let res = execute!(&conn, $query, params![&x, &id.printable()]);
430
431            if let Err(e) = res {
432                return Err(Error::DatabaseError(e.to_string()));
433            }
434
435            self.0.1.remove(format!($cache_key_tmpl, id)).await;
436
437            Ok(())
438        }
439    };
440
441    // case 10
442    ($name:ident($x:ty)@$select_fn:ident:$permission:expr; -> $query:literal --serde) => {
443        pub async fn $name(&self, id: &$crate::model::id::Id, user: &User, x: $x) -> Result<()> {
444            let y = self.$select_fn(id).await?;
445
446            if user.id != y.owner {
447                if !user.permissions.check($permission) {
448                    return Err(Error::NotAllowed);
449                } else {
450                    self.create_audit_log_entry($crate::model::moderation::AuditLogEntry::new(
451                        user.id,
452                        format!("invoked `{}` with x value `{id}`", stringify!($name), id),
453                    ))
454                    .await?
455                }
456            }
457
458            let conn = match self.0.connect().await {
459                Ok(c) => c,
460                Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
461            };
462
463            let res = execute!(
464                &conn,
465                $query,
466                &[&serde_json::to_string(&x).unwrap(), &id.printable()]
467            );
468
469            if let Err(e) = res {
470                return Err(Error::DatabaseError(e.to_string()));
471            }
472
473            Ok(())
474        }
475    };
476
477    // case 11
478    ($name:ident($x:ty)@$select_fn:ident:$permission:expr; -> $query:literal --serde --cache-key-tmpl=$cache_key_tmpl:literal) => {
479        pub async fn $name(&self, id: &$crate::model::id::Id, user: &User, x: $x) -> Result<()> {
480            let y = self.$select_fn(id).await?;
481
482            if user.id != y.owner {
483                if !user.permissions.check($permission) {
484                    return Err(Error::NotAllowed);
485                } else {
486                    self.create_audit_log_entry($crate::model::moderation::AuditLogEntry::new(
487                        user.id,
488                        format!("invoked `{}` with x value `{id}`", stringify!($name)),
489                    ))
490                    .await?
491                }
492            }
493
494            let conn = match self.0.connect().await {
495                Ok(c) => c,
496                Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
497            };
498
499            let res = execute!(
500                &conn,
501                $query,
502                params![&serde_json::to_string(&x).unwrap(), &id.printable()]
503            );
504
505            if let Err(e) = res {
506                return Err(Error::DatabaseError(e.to_string()));
507            }
508
509            self.0.1.remove(format!($cache_key_tmpl, id)).await;
510
511            Ok(())
512        }
513    };
514
515    // case 12
516    ($name:ident($x:ty) -> $query:literal) => {
517        pub async fn $name(&self, id: &$crate::model::id::Id, x: $x) -> Result<()> {
518            let conn = match self.0.connect().await {
519                Ok(c) => c,
520                Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
521            };
522
523            let res = execute!(&conn, $query, &[&x, &id.printable()]);
524
525            if let Err(e) = res {
526                return Err(Error::DatabaseError(e.to_string()));
527            }
528
529            Ok(())
530        }
531    };
532
533    // case 13
534    ($name:ident($x:ty) -> $query:literal --cache-key-tmpl=$cache_key_tmpl:literal) => {
535        pub async fn $name(&self, id: &$crate::model::id::Id, x: $x) -> Result<()> {
536            let conn = match self.0.connect().await {
537                Ok(c) => c,
538                Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
539            };
540
541            let res = execute!(&conn, $query, &[&x, &id.printable()]);
542
543            if let Err(e) = res {
544                return Err(Error::DatabaseError(e.to_string()));
545            }
546
547            self.0.1.remove(format!($cache_key_tmpl, id)).await;
548
549            Ok(())
550        }
551    };
552
553    // case 14
554    ($name:ident($x:ty) -> $query:literal --serde) => {
555        pub async fn $name(&self, id: &$crate::model::id::Id, x: $x) -> Result<()> {
556            let conn = match self.0.connect().await {
557                Ok(c) => c,
558                Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
559            };
560
561            let res = execute!(
562                &conn,
563                $query,
564                &[&serde_json::to_string(&x).unwrap(), &id.printable()]
565            );
566
567            if let Err(e) = res {
568                return Err(Error::DatabaseError(e.to_string()));
569            }
570
571            Ok(())
572        }
573    };
574
575    // case 15
576    ($name:ident($x:ty) -> $query:literal --serde --cache-key-tmpl=$cache_key_tmpl:literal) => {
577        pub async fn $name(&self, id: &$crate::model::id::Id, x: $x) -> Result<()> {
578            let conn = match self.0.connect().await {
579                Ok(c) => c,
580                Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
581            };
582
583            let res = execute!(
584                &conn,
585                $query,
586                params![&serde_json::to_string(&x).unwrap(), &id.printable()]
587            );
588
589            if let Err(e) = res {
590                return Err(Error::DatabaseError(e.to_string()));
591            }
592
593            self.0.1.remove(format!($cache_key_tmpl, id)).await;
594
595            Ok(())
596        }
597    };
598
599    // case 16
600    ($name:ident() -> $query:literal --cache-key-tmpl=$cache_key_tmpl:literal --incr) => {
601        pub async fn $name(&self, id: &$crate::model::id::Id) -> Result<()> {
602            let conn = match self.0.connect().await {
603                Ok(c) => c,
604                Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
605            };
606
607            let res = execute!(&conn, $query, &[&id.printable()]);
608
609            if let Err(e) = res {
610                return Err(Error::DatabaseError(e.to_string()));
611            }
612
613            self.0.1.remove(format!($cache_key_tmpl, id)).await;
614            Ok(())
615        }
616    };
617
618    // case 17
619    ($name:ident() -> $query:literal --cache-key-tmpl=$cache_key_tmpl:literal --decr) => {
620        pub async fn $name(&self, id: &$crate::model::id::Id) -> Result<()> {
621            let conn = match self.0.connect().await {
622                Ok(c) => c,
623                Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
624            };
625
626            let res = execute!(&conn, $query, &[&id.printable()]);
627
628            if let Err(e) = res {
629                return Err(Error::DatabaseError(e.to_string()));
630            }
631
632            self.0.1.remove(format!($cache_key_tmpl, id)).await;
633
634            Ok(())
635        }
636    };
637
638    // case 18
639    ($name:ident()@$select_fn:ident -> $query:literal --cache-key-tmpl=$cache_key_tmpl:literal --decr=$field:ident) => {
640        pub async fn $name(&self, id: &$crate::model::id::Id) -> Result<()> {
641            let y = self.$select_fn(id).await?;
642
643            if (y.$field as isize) - 1 < 0 {
644                return Ok(());
645            }
646
647            let conn = match self.0.connect().await {
648                Ok(c) => c,
649                Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
650            };
651
652            let res = execute!(&conn, $query, &[&id.printable()]);
653
654            if let Err(e) = res {
655                return Err(Error::DatabaseError(e.to_string()));
656            }
657
658            self.0.1.remove(format!($cache_key_tmpl, id)).await;
659
660            Ok(())
661        }
662    };
663
664    // case 19
665    ($name:ident()@$select_fn:ident:$permission:expr; -> $query:literal --cache-key-tmpl=$cache_key_tmpl:ident) => {
666        pub async fn $name(&self, id: &$crate::model::id::Id, user: &User) -> Result<()> {
667            let y = self.$select_fn(id).await?;
668
669            if user.id != y.owner {
670                if !user.permissions.check($permission) {
671                    return Err(Error::NotAllowed);
672                } else {
673                    self.create_audit_log_entry($crate::model::moderation::AuditLogEntry::new(
674                        user.id,
675                        format!("invoked `{}` with x value `{id}`", stringify!($name)),
676                    ))
677                    .await?
678                }
679            }
680
681            let conn = match self.0.connect().await {
682                Ok(c) => c,
683                Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
684            };
685
686            let res = execute!(&conn, $query, &[&id.printable()]);
687
688            if let Err(e) = res {
689                return Err(Error::DatabaseError(e.to_string()));
690            }
691
692            self.$cache_key_tmpl(&y).await;
693
694            Ok(())
695        }
696    };
697
698    // case 20
699    ($name:ident($x:ty)@$select_fn:ident:$permission:path; -> $query:literal --cache-key-tmpl=$cache_key_tmpl:ident) => {
700        pub async fn $name(&self, id: &$crate::model::id::Id, user: &User, x: $x) -> Result<()> {
701            let y = self.$select_fn(id).await?;
702
703            if user.id != y.owner {
704                if !user.permissions.check($permission) {
705                    return Err(Error::NotAllowed);
706                } else {
707                    self.create_audit_log_entry($crate::model::moderation::AuditLogEntry::new(
708                        user.id.to_owned(),
709                        format!("invoked `{}` with x value `{x}`", stringify!($name)),
710                    ))
711                    .await?
712                }
713            }
714
715            let conn = match self.0.connect().await {
716                Ok(c) => c,
717                Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
718            };
719
720            let res = execute!(&conn, $query, params![&x, &id.printable()]);
721
722            if let Err(e) = res {
723                return Err(Error::DatabaseError(e.to_string()));
724            }
725
726            self.$cache_key_tmpl(&y).await;
727
728            Ok(())
729        }
730    };
731
732    // case 21
733    ($name:ident($x:ty)@$select_fn:ident -> $query:literal --cache-key-tmpl=$cache_key_tmpl:ident) => {
734        pub async fn $name(&self, id: &$crate::model::id::Id, x: $x) -> Result<()> {
735            let y = self.$select_fn(id).await?;
736
737            let conn = match self.0.connect().await {
738                Ok(c) => c,
739                Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
740            };
741
742            let res = execute!(&conn, $query, params![&x, &id.printable()]);
743
744            if let Err(e) = res {
745                return Err(Error::DatabaseError(e.to_string()));
746            }
747
748            self.$cache_key_tmpl(&y).await;
749
750            Ok(())
751        }
752    };
753
754    // case 22
755    ($name:ident(Id :: usize, $x:ty)@$select_fn:ident -> $query:literal --cache-key-tmpl=$cache_key_tmpl:ident) => {
756        pub async fn $name(&self, id: &$crate::model::id::Id, x: $x) -> Result<()> {
757            let y = self.$select_fn(id).await?;
758
759            let conn = match self.0.connect().await {
760                Ok(c) => c,
761                Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
762            };
763
764            let res = execute!(&conn, $query, params![&x, &(id.as_usize() as i64)]);
765
766            if let Err(e) = res {
767                return Err(Error::DatabaseError(e.to_string()));
768            }
769
770            self.$cache_key_tmpl(&y).await;
771            Ok(())
772        }
773    };
774
775    // case 23
776    ($name:ident($x:ty)@$select_fn:ident -> $query:literal --serde --cache-key-tmpl=$cache_key_tmpl:ident) => {
777        pub async fn $name(&self, id: &$crate::model::id::Id, x: $x) -> Result<()> {
778            let y = self.$select_fn(id).await?;
779
780            let conn = match self.0.connect().await {
781                Ok(c) => c,
782                Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
783            };
784
785            let res = execute!(
786                &conn,
787                $query,
788                params![&serde_json::to_string(&x).unwrap(), &id.printable()]
789            );
790
791            if let Err(e) = res {
792                return Err(Error::DatabaseError(e.to_string()));
793            }
794
795            self.$cache_key_tmpl(&y).await;
796            Ok(())
797        }
798    };
799
800    // case 24
801    ($name:ident(Id :: usize, $x:ty)@$select_fn:ident -> $query:literal --serde --cache-key-tmpl=$cache_key_tmpl:ident) => {
802        pub async fn $name(&self, id: &$crate::model::id::Id, x: $x) -> Result<()> {
803            let y = self.$select_fn(id).await?;
804
805            let conn = match self.0.connect().await {
806                Ok(c) => c,
807                Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
808            };
809
810            let res = execute!(
811                &conn,
812                $query,
813                params![&serde_json::to_string(&x).unwrap(), &(id.as_usize() as i64)]
814            );
815
816            if let Err(e) = res {
817                return Err(Error::DatabaseError(e.to_string()));
818            }
819
820            self.$cache_key_tmpl(&y).await;
821            Ok(())
822        }
823    };
824
825    // case 25
826    ($name:ident($x:ty)@$select_fn:ident:$permission:expr; -> $query:literal --serde --cache-key-tmpl=$cache_key_tmpl:ident) => {
827        pub async fn $name(&self, id: &$crate::model::id::Id, user: &User, x: $x) -> Result<()> {
828            let y = self.$select_fn(id).await?;
829
830            if user.id != y.owner {
831                if !user.permissions.check($permission) {
832                    return Err(Error::NotAllowed);
833                } else {
834                    self.create_audit_log_entry($crate::model::moderation::AuditLogEntry::new(
835                        user.id.to_owned(),
836                        format!("invoked `{}` with x value `{x:?}`", stringify!($name)),
837                    ))
838                    .await?
839                }
840            }
841
842            let conn = match self.0.connect().await {
843                Ok(c) => c,
844                Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
845            };
846
847            let res = execute!(
848                &conn,
849                $query,
850                params![&serde_json::to_string(&x).unwrap(), &id.printable()]
851            );
852
853            if let Err(e) = res {
854                return Err(Error::DatabaseError(e.to_string()));
855            }
856
857            self.$cache_key_tmpl(&y).await;
858
859            Ok(())
860        }
861    };
862
863    // case 26
864    ($name:ident()@$select_fn:ident -> $query:literal --cache-key-tmpl=$cache_key_tmpl:ident --incr) => {
865        pub async fn $name(&self, id: &$crate::model::id::Id) -> Result<()> {
866            let y = self.$select_fn(id).await?;
867
868            let conn = match self.0.connect().await {
869                Ok(c) => c,
870                Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
871            };
872
873            let res = execute!(&conn, $query, &[&id.printable()]);
874
875            if let Err(e) = res {
876                return Err(Error::DatabaseError(e.to_string()));
877            }
878
879            self.$cache_key_tmpl(&y).await;
880
881            Ok(())
882        }
883    };
884
885    // case 27
886    ($name:ident(Id :: usize)@$select_fn:ident -> $query:literal --cache-key-tmpl=$cache_key_tmpl:ident --incr) => {
887        pub async fn $name(&self, id: &$crate::model::id::Id) -> Result<()> {
888            let y = self.$select_fn(id).await?;
889
890            let conn = match self.0.connect().await {
891                Ok(c) => c,
892                Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
893            };
894
895            let res = execute!(&conn, $query, &[&(id.as_usize() as i64)]);
896
897            if let Err(e) = res {
898                return Err(Error::DatabaseError(e.to_string()));
899            }
900
901            self.$cache_key_tmpl(&y).await;
902
903            Ok(())
904        }
905    };
906
907    // case 28
908    ($name:ident()@$select_fn:ident -> $query:literal --cache-key-tmpl=$cache_key_tmpl:ident --decr=$field:ident) => {
909        pub async fn $name(&self, id: &$crate::model::id::Id) -> Result<()> {
910            let y = self.$select_fn(id).await?;
911
912            if (y.$field as isize) - 1 < 0 {
913                return Ok(());
914            }
915
916            let conn = match self.0.connect().await {
917                Ok(c) => c,
918                Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
919            };
920
921            let res = execute!(&conn, $query, &[&id.printable()]);
922
923            if let Err(e) = res {
924                return Err(Error::DatabaseError(e.to_string()));
925            }
926
927            self.$cache_key_tmpl(&y).await;
928
929            Ok(())
930        }
931    };
932
933    // case 29
934    ($name:ident(Id :: usize)@$select_fn:ident -> $query:literal --cache-key-tmpl=$cache_key_tmpl:ident --decr=$field:ident) => {
935        pub async fn $name(&self, id: &$crate::model::id::Id) -> Result<()> {
936            let y = self.$select_fn(id).await?;
937
938            if (y.$field as isize) - 1 < 0 {
939                return Ok(());
940            }
941
942            let conn = match self.0.connect().await {
943                Ok(c) => c,
944                Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
945            };
946
947            let res = execute!(&conn, $query, &[&(id.as_usize() as i64)]);
948
949            if let Err(e) = res {
950                return Err(Error::DatabaseError(e.to_string()));
951            }
952
953            self.$cache_key_tmpl(&y).await;
954
955            Ok(())
956        }
957    };
958}