1use std::collections::HashMap;
7use std::fs;
8use std::path::PathBuf;
9
10pub use squigit_storage::*;
11
12pub(crate) fn profile_store() -> Result<ProfileStore> {
13 ProfileStore::new()
14}
15
16pub(crate) fn thread_store() -> Result<ThreadStorage> {
17 ThreadStorage::new()
18}
19
20pub(crate) fn version_store() -> Result<VersionStore> {
21 VersionStore::new()
22}
23
24pub(crate) fn config_path(file_name: &str) -> Option<PathBuf> {
25 paths::base_config_dir().map(|directory| directory.join(file_name))
26}
27
28pub fn rules_path() -> Option<PathBuf> {
29 rules::rules_path()
30}
31
32pub fn load_rules() -> std::result::Result<String, String> {
33 let path =
34 rules_path().ok_or_else(|| "Could not locate Squigit's RULES.md path".to_string())?;
35 match fs::read_to_string(path) {
36 Ok(content) => Ok(content),
37 Err(error) if error.kind() == std::io::ErrorKind::NotFound => Ok(String::new()),
38 Err(error) => Err(error.to_string()),
39 }
40}
41
42pub fn save_rules(content: &str) -> std::result::Result<(), String> {
43 if rules_path().is_none() {
44 return Err("Could not locate Squigit's RULES.md path".to_string());
45 }
46 rules::save_rules(content)
47}
48
49pub fn store_image(bytes: &[u8], explicit_tone: Option<String>) -> Result<StoredImage> {
51 thread_store()?.store_image(bytes, explicit_tone)
52}
53
54pub fn store_file(
56 bytes: &[u8],
57 extension: &str,
58 explicit_tone: Option<String>,
59) -> Result<StoredImage> {
60 thread_store()?.store_file(bytes, extension, explicit_tone)
61}
62
63#[derive(Clone, Debug)]
64pub struct GalleryThread {
65 pub thread_id: String,
66 pub title: String,
67 pub updated_at: String,
68}
69
70#[derive(Clone, Debug)]
71pub struct GalleryImage {
72 pub hash: String,
73 pub path: String,
74 pub updated_at: String,
75 pub threads: Vec<GalleryThread>,
76}
77
78pub fn list_gallery(offset: u32, limit: u32) -> Result<Vec<GalleryImage>> {
80 let storage = thread_store()?;
81 let mut grouped: HashMap<String, Vec<GalleryThread>> = HashMap::new();
82
83 for thread in storage.list_threads()? {
84 if thread.image_hash.is_empty() || thread.image_hash == EMPTY_STATE_ASSET_ID {
85 continue;
86 }
87 grouped
88 .entry(thread.image_hash)
89 .or_default()
90 .push(GalleryThread {
91 thread_id: thread.id,
92 title: thread.title,
93 updated_at: thread.updated_at.to_rfc3339(),
94 });
95 }
96
97 let mut images = grouped
98 .into_iter()
99 .filter_map(|(hash, mut threads)| {
100 threads.sort_by(|left, right| right.updated_at.cmp(&left.updated_at));
101 let updated_at = threads.first()?.updated_at.clone();
102 let path = storage.get_image_path(&hash).ok()?;
103 Some(GalleryImage {
104 hash,
105 path,
106 updated_at,
107 threads,
108 })
109 })
110 .collect::<Vec<_>>();
111 images.sort_by(|left, right| right.updated_at.cmp(&left.updated_at));
112 Ok(images
113 .into_iter()
114 .skip(offset as usize)
115 .take((limit as usize).clamp(1, 100))
116 .collect())
117}