Skip to main content

max / makenotwork

1.1 KB · 39 lines History Blame Raw
1 //! endorsement toggle, idempotent per (user, post) under concurrent submits
2
3 use super::{PgPool, Uuid};
4
5 /// Toggle endorsement: insert if missing, delete if exists. Returns true if now endorsed.
6 /// Uses a transaction to prevent race conditions between concurrent toggle requests.
7 #[tracing::instrument(skip_all)]
8 pub async fn toggle_endorsement(
9 pool: &PgPool,
10 post_id: Uuid,
11 endorser_id: Uuid,
12 ) -> Result<bool, sqlx::Error> {
13 let mut tx = pool.begin().await?;
14
15 let result = sqlx::query!(
16 "INSERT INTO post_endorsements (post_id, endorser_id) VALUES ($1, $2) ON CONFLICT DO NOTHING",
17 post_id,
18 endorser_id,
19 )
20 .execute(&mut *tx)
21 .await?;
22
23 if result.rows_affected() == 0 {
24 // Already existed; remove it
25 sqlx::query!(
26 "DELETE FROM post_endorsements WHERE post_id = $1 AND endorser_id = $2",
27 post_id,
28 endorser_id,
29 )
30 .execute(&mut *tx)
31 .await?;
32 tx.commit().await?;
33 Ok(false)
34 } else {
35 tx.commit().await?;
36 Ok(true)
37 }
38 }
39