Skip to main content

post_archiver/
file_meta.rs

1use serde::{Deserialize, Serialize};
2
3use serde_json::Value;
4#[cfg(feature = "typescript")]
5use ts_rs::TS;
6
7use std::{collections::HashMap, hash::Hash, path::PathBuf};
8
9use crate::{
10    id::{FileMetaId, PostId},
11    Post,
12};
13
14/// The number of posts in one chunk.
15pub const POSTS_PRE_CHUNK: u32 = 2048;
16
17/// Metadata for a file in the system with hierarchical path organization
18#[cfg_attr(feature = "typescript", derive(TS))]
19#[cfg_attr(feature = "typescript", ts(export))]
20#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
21pub struct FileMeta {
22    pub id: FileMetaId,
23    pub filename: String,
24    pub post: PostId,
25    pub mime: String,
26    #[cfg_attr(feature = "typescript", ts(type = "Record<string, any>"))]
27    pub extra: HashMap<String, Value>,
28}
29
30impl FileMeta {
31    /// Returns relative path to the file.  
32    /// it will be `<chunk>/<index>/<filename>`.  
33    /// the `<chunk>` is `postId / POSTS_PRE_CHUNK`,
34    /// the `<index>` is `postId % POSTS_PRE_CHUNK`,
35    /// [`POSTS_PRE_CHUNK`] is a constant that defines how many posts are in one chunk. (default is 2048).
36    ///
37    /// # Examples
38    /// ```rust
39    /// use post_archiver::{FileMeta, AuthorId, PostId, FileMetaId};
40    /// use std::collections::HashMap;
41    /// use std::path::PathBuf;
42    ///
43    /// // You should never create a FileMeta struct
44    /// let file_meta = FileMeta {
45    ///     id: FileMetaId::new(6),
46    ///     post: PostId::new(2049),
47    ///     filename: "example.txt".to_string(),
48    ///     mime: "text/plain".to_string(),
49    ///     extra: HashMap::new(),
50    /// };
51    ///
52    /// let path = file_meta.path();
53    /// assert_eq!(path.to_str(), Some("1/1/example.txt"));
54    /// ```
55    pub fn path(&self) -> PathBuf {
56        let directory = Post::directory(self.post);
57        directory.join(&self.filename)
58    }
59
60    pub fn directory(&self) -> PathBuf {
61        Post::directory(self.post)
62    }
63}
64
65impl Hash for FileMeta {
66    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
67        self.id.hash(state);
68        self.post.hash(state);
69        self.filename.hash(state);
70        self.mime.hash(state);
71        // We don't hash `extra` because it can be large.
72    }
73}
74
75#[cfg(feature = "utils")]
76mod definitions {
77    use crate::utils::macros::as_table;
78
79    use super::*;
80
81    as_table!(
82        "file_metas" => FileMeta {
83            id: "id",
84            post: "post",
85            filename: "filename",
86            mime: "mime",
87            extra: "extra" => json,
88        }
89    );
90}