revolt_database/models/files/ops/
reference.rs

1use revolt_result::Result;
2
3use crate::File;
4use crate::FileUsedFor;
5use crate::ReferenceDb;
6
7use super::AbstractAttachments;
8
9#[async_trait]
10impl AbstractAttachments for ReferenceDb {
11    /// Insert attachment into database.
12    async fn insert_attachment(&self, attachment: &File) -> Result<()> {
13        let mut attachments = self.files.lock().await;
14        if attachments.contains_key(&attachment.id) {
15            Err(create_database_error!("insert", "attachment"))
16        } else {
17            attachments.insert(attachment.id.to_string(), attachment.clone());
18            Ok(())
19        }
20    }
21
22    /// Fetch an attachment by its id.
23    async fn fetch_attachment(&self, tag: &str, file_id: &str) -> Result<File> {
24        let files = self.files.lock().await;
25        if let Some(file) = files.get(file_id) {
26            if file.tag == tag {
27                Ok(file.clone())
28            } else {
29                Err(create_error!(NotFound))
30            }
31        } else {
32            Err(create_error!(NotFound))
33        }
34    }
35
36    /// Fetch all deleted attachments.
37    async fn fetch_deleted_attachments(&self) -> Result<Vec<File>> {
38        let files = self.files.lock().await;
39        Ok(files
40            .values()
41            .filter(|file| {
42                // file has been marked as deleted
43                file.deleted.is_some_and(|v| v)
44                    // and it has not been reported
45                    && !file.reported.is_some_and(|v| v)
46            })
47            .cloned()
48            .collect())
49    }
50
51    /// Fetch all dangling attachments.
52    async fn fetch_dangling_files(&self) -> Result<Vec<File>> {
53        let files = self.files.lock().await;
54        Ok(files
55            .values()
56            .filter(|file| file.used_for.is_none() && !file.deleted.is_some_and(|v| v))
57            .cloned()
58            .collect())
59    }
60
61    /// Count references to a given hash.
62    async fn count_file_hash_references(&self, hash: &str) -> Result<usize> {
63        let files = self.files.lock().await;
64        Ok(files
65            .values()
66            .filter(|file| file.hash.as_ref().is_some_and(|h| h == hash))
67            .cloned()
68            .count())
69    }
70
71    /// Find an attachment by its details and mark it as used by a given parent.
72    async fn find_and_use_attachment(
73        &self,
74        id: &str,
75        tag: &str,
76        used_for: FileUsedFor,
77        uploader_id: String,
78    ) -> Result<File> {
79        let mut files = self.files.lock().await;
80        if let Some(file) = files.get_mut(id) {
81            if file.tag == tag {
82                file.uploader_id = Some(uploader_id);
83                file.used_for = Some(used_for);
84
85                Ok(file.clone())
86            } else {
87                Err(create_error!(NotFound))
88            }
89        } else {
90            Err(create_error!(NotFound))
91        }
92    }
93
94    /// Mark an attachment as having been reported.
95    async fn mark_attachment_as_reported(&self, id: &str) -> Result<()> {
96        let mut files = self.files.lock().await;
97        if let Some(file) = files.get_mut(id) {
98            file.reported = Some(true);
99            Ok(())
100        } else {
101            Err(create_error!(NotFound))
102        }
103    }
104
105    /// Mark an attachment as having been deleted.
106    async fn mark_attachment_as_deleted(&self, id: &str) -> Result<()> {
107        let mut files = self.files.lock().await;
108        if let Some(file) = files.get_mut(id) {
109            file.deleted = Some(true);
110            Ok(())
111        } else {
112            Err(create_error!(NotFound))
113        }
114    }
115
116    /// Mark multiple attachments as having been deleted.
117    async fn mark_attachments_as_deleted(&self, ids: &[String]) -> Result<()> {
118        let mut files = self.files.lock().await;
119
120        for id in ids {
121            if !files.contains_key(id) {
122                return Err(create_error!(NotFound));
123            }
124        }
125
126        for id in ids {
127            if let Some(file) = files.get_mut(id) {
128                file.reported = Some(true);
129            }
130        }
131
132        Ok(())
133    }
134
135    /// Delete the attachment entry.
136    async fn delete_attachment(&self, id: &str) -> Result<()> {
137        let mut files = self.files.lock().await;
138        if files.remove(id).is_some() {
139            Ok(())
140        } else {
141            Err(create_error!(NotFound))
142        }
143    }
144}