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