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
use serde::{Deserialize, Serialize};
use serde_json::Value;
#[cfg(feature = "typescript")]
use ts_rs::TS;
use std::{collections::HashMap, hash::Hash, path::PathBuf};
use crate::{
id::{FileMetaId, PostId},
Post,
};
/// The number of posts in one chunk.
pub const POSTS_PRE_CHUNK: u32 = 2048;
/// Metadata for a file in the system with hierarchical path organization
#[cfg_attr(feature = "typescript", derive(TS))]
#[cfg_attr(feature = "typescript", ts(export))]
#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
pub struct FileMeta {
pub id: FileMetaId,
pub filename: String,
pub post: PostId,
pub mime: String,
#[cfg_attr(feature = "typescript", ts(type = "Record<string, any>"))]
pub extra: HashMap<String, Value>,
}
impl FileMeta {
/// Returns relative path to the file.
/// it will be `<chunk>/<index>/<filename>`.
/// the `<chunk>` is `postId / POSTS_PRE_CHUNK`,
/// the `<index>` is `postId % POSTS_PRE_CHUNK`,
/// [`POSTS_PRE_CHUNK`] is a constant that defines how many posts are in one chunk. (default is 2048).
///
/// # Examples
/// ```rust
/// use post_archiver::{FileMeta, AuthorId, PostId, FileMetaId};
/// use std::collections::HashMap;
/// use std::path::PathBuf;
///
/// // You should never create a FileMeta struct
/// let file_meta = FileMeta {
/// id: FileMetaId::new(6),
/// post: PostId::new(2049),
/// filename: "example.txt".to_string(),
/// mime: "text/plain".to_string(),
/// extra: HashMap::new(),
/// };
///
/// let path = file_meta.path();
/// assert_eq!(path.to_str(), Some("1/1/example.txt"));
/// ```
pub fn path(&self) -> PathBuf {
let directory = Post::directory(self.post);
directory.join(&self.filename)
}
pub fn directory(&self) -> PathBuf {
Post::directory(self.post)
}
}
impl Hash for FileMeta {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.id.hash(state);
self.post.hash(state);
self.filename.hash(state);
self.mime.hash(state);
// We don't hash `extra` because it can be large.
}
}
#[cfg(feature = "utils")]
mod definitions {
use crate::utils::macros::as_table;
use super::*;
as_table!(
"file_metas" => FileMeta {
id: "id",
post: "post",
filename: "filename",
mime: "mime",
extra: "extra" => json,
}
);
}