summaryrefslogtreecommitdiff
path: root/src/database/friends.rs
blob: 31434d4f5f0e6631e6ccd3ae7255c902982aee39 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
use tracing::instrument;

use crate::types::user::{User, FOLLOWED, FOLLOWING, NO_RELATION};

use super::Database;

impl Database {
    pub fn init_friends(&self) -> Result<(), rusqlite::Error> {
        let sql = "
            CREATE TABLE IF NOT EXISTS friends (
                follower_id     INTEGER NOT NULL,
                followee_id     INTEGER NOT NULL,
                FOREIGN KEY(follower_id) REFERENCES users(user_id),
                FOREIGN KEY(followee_id) REFERENCES users(user_id),
                PRIMARY KEY (follower_id, followee_id)
            );
        ";
        self.0.execute(sql, ())?;
        Ok(())
    }

    #[instrument(skip(self))]
    pub fn get_friend_status(&self, user_id_1: u64, user_id_2: u64) -> Result<u8, rusqlite::Error> {
        tracing::trace!("Retrieving friend status");
        let mut stmt = self.0.prepare("SELECT * FROM friends WHERE (follower_id = ? AND followee_id = ?) OR (follower_id = ? AND followee_id = ?);")?;
        let mut status = NO_RELATION;
        let rows: Vec<u64> = stmt
            .query_map([user_id_1, user_id_2, user_id_2, user_id_1], |row| {
                let id: u64 = row.get(0)?;
                Ok(id)
            })?
            .into_iter()
            .flatten()
            .collect();

        for follower in rows {
            if follower == user_id_1 {
                status |= FOLLOWING;
            }

            if follower == user_id_2 {
                status |= FOLLOWED;
            }
        }

        Ok(status)
    }

    #[instrument(skip(self))]
    pub fn get_friends(&self, user_id: u64) -> Result<Vec<User>, rusqlite::Error> {
        tracing::trace!("Retrieving friends");
        let mut stmt = self.0.prepare(
            "
            SELECT * 
            FROM users u 
            WHERE EXISTS (
                SELECT NULL 
                FROM friends f 
                WHERE u.user_id = f.follower_id
                AND f.followee_id = ?
            )
            AND EXISTS (
                SELECT NULL
                FROM friends f
                WHERE u.user_id = f.followee_id
                AND f.follower_id = ?
            )
        ",
        )?;
        let row = stmt.query_map([user_id, user_id], |row| {
            let row = Self::user_from_row(row, true)?;
            Ok(row)
        })?;
        Ok(row.into_iter().flatten().collect())
    }

    #[instrument(skip(self))]
    pub fn set_following(&self, user_id_1: u64, user_id_2: u64) -> Result<bool, rusqlite::Error> {
        tracing::trace!("Setting following");
        let mut stmt = self
            .0
            .prepare("INSERT OR REPLACE INTO friends (follower_id, followee_id) VALUES (?,?)")?;
        let changes = stmt.execute([user_id_1, user_id_2])?;
        Ok(changes == 1)
    }

    #[instrument(skip(self))]
    pub fn remove_following(
        &self,
        user_id_1: u64,
        user_id_2: u64,
    ) -> Result<bool, rusqlite::Error> {
        tracing::trace!("Removing following");
        let mut stmt = self
            .0
            .prepare("DELETE FROM friends WHERE follower_id = ? AND followee_id = ?")?;
        let changes = stmt.execute([user_id_1, user_id_2])?;
        Ok(changes == 1)
    }
}