post_archiver/manager/
file_meta.rs

1use std::collections::HashMap;
2
3use rusqlite::{params, OptionalExtension};
4use serde_json::Value;
5
6use crate::{
7    manager::{PostArchiverConnection, PostArchiverManager},
8    FileMeta, FileMetaId, PostId,
9};
10
11//=============================================================
12// Querying
13//=============================================================
14impl<T> PostArchiverManager<T>
15where
16    T: PostArchiverConnection,
17{
18    /// Find a file's metadata by its post ID and filename.
19    ///
20    /// # Errors
21    ///
22    /// Returns `rusqlite::Error` if there was an error accessing the database.
23    pub fn find_file_meta(
24        &self,
25        post: PostId,
26        filename: &str,
27    ) -> Result<Option<FileMetaId>, rusqlite::Error> {
28        let mut stmt = self
29            .conn()
30            .prepare_cached("SELECT id FROM file_metas WHERE post = ? AND filename = ?")?;
31
32        stmt.query_row(params![post, filename], |row| row.get(0))
33            .optional()
34    }
35    /// Retrieve a file's metadata by its ID.
36    ///
37    /// Fetches all information about the file including its post ID, filename, MIME type, and extra metadata.
38    ///
39    /// # Errors
40    ///
41    /// Returns `rusqlite::Error` if:
42    /// * The file ID does not exist
43    /// * There was an error accessing the database
44    pub fn get_file_meta(&self, id: &FileMetaId) -> Result<FileMeta, rusqlite::Error> {
45        let mut stmt = self
46            .conn()
47            .prepare_cached("SELECT * FROM file_metas WHERE id = ?")?;
48        stmt.query_row([id], FileMeta::from_row)
49    }
50}
51
52//=============================================================
53// Modifying
54//=============================================================
55impl<T> PostArchiverManager<T>
56where
57    T: PostArchiverConnection,
58{
59    /// Add a new file metadata to the archive.
60    ///
61    /// Inserts a new file metadata with the given post ID, filename, MIME type, and extra metadata.
62    /// It will check if a file with the same post and filename already exists.
63    ///
64    /// # Errors
65    ///
66    /// Returns `rusqlite::Error` if:
67    /// * The post ID does not exist
68    /// * Duplicate filename for the same post
69    /// * There was an error accessing the database
70    pub fn add_file_meta(
71        &self,
72        post: PostId,
73        filename: String,
74        mime: String,
75        extra: HashMap<String, Value>,
76    ) -> Result<FileMetaId, rusqlite::Error> {
77        let mut stmt = self.conn().prepare_cached(
78            "INSERT INTO file_metas (post, filename, mime, extra) VALUES (?, ?, ?, ?) RETURNING id",
79        )?;
80        stmt.query_row(
81            params![post, filename, mime, serde_json::to_string(&extra).unwrap()],
82            |row| row.get(0),
83        )
84    }
85    /// Remove a file metadata from the archive.
86    ///
87    /// This operation will also remove all associated thumb references.
88    /// But it will not delete post.content related to this file.
89    ///
90    /// # Errors
91    ///
92    /// Returns `rusqlite::Error` if there was an error accessing the database.
93    pub fn remove_file_meta(&self, id: FileMetaId) -> Result<(), rusqlite::Error> {
94        let mut stmt = self
95            .conn()
96            .prepare_cached("DELETE FROM file_metas WHERE id = ?")?;
97        stmt.execute([id])?;
98        Ok(())
99    }
100
101    /// Set the filename of a file metadata.
102    ///
103    /// # Errors
104    ///
105    /// Returns `rusqlite::Error` if there was an error accessing the database.
106    pub fn set_file_meta_mime(&self, id: FileMetaId, mime: String) -> Result<(), rusqlite::Error> {
107        let mut stmt = self
108            .conn()
109            .prepare_cached("UPDATE file_metas SET mime = ? WHERE id = ?")?;
110        stmt.execute(params![mime, id])?;
111        Ok(())
112    }
113
114    /// Set the extra metadata of a file metadata.
115    ///
116    /// # Errors
117    ///
118    /// Returns `rusqlite::Error` if there was an error accessing the database.
119    pub fn set_file_meta_extra(
120        &self,
121        id: FileMetaId,
122        extra: HashMap<String, Value>,
123    ) -> Result<(), rusqlite::Error> {
124        let extra_json = serde_json::to_string(&extra).unwrap();
125        let mut stmt = self
126            .conn()
127            .prepare_cached("UPDATE file_metas SET extra = ? WHERE id = ?")?;
128        stmt.execute(params![extra_json, id])?;
129        Ok(())
130    }
131
132    // TODO: implement a method to update the filename or post of a file_meta (because these need
133    // fs renames and moves)
134}