1use serde::{Deserialize, Serialize};
6use std::collections::HashMap;
7
8pub const DIR_USER_ROOT: &str = "/";
14
15pub const DIR_ARCHIVE: &str = "archive";
17
18pub const DIR_MEDIA: &str = "media";
20
21pub const DIR_JOURNAL: &str = "journal";
23
24pub const DIR_HABITS: &str = "habits";
26
27pub const DIR_INSIGHTS: &str = "insights";
29
30pub const CHAT_FILENAME: &str = "Chat.md";
32
33pub const LATER_FILENAME: &str = "Later.md";
35
36pub const DONE_FILENAME: &str = "Done.md";
38
39pub const SHOP_FILENAME: &str = "Shop.md";
41
42pub const WATCH_FILENAME: &str = "Watch.md";
44
45pub const READ_FILENAME: &str = "Read.md";
47
48pub const POMODORO_TASK: &str = "Finished a break";
50
51pub const MD_EXT: &str = ".md";
53
54#[derive(Debug, Clone, Serialize, Deserialize)]
60pub struct FileEntry {
61 pub name: String,
63 pub hash: String,
65 pub display_name: String,
67 pub ctime: i64,
69 pub has_content: bool,
71 pub is_dir: bool,
73 pub parent_dir: String,
75}
76
77impl FileEntry {
78 pub fn new(
80 name: String,
81 hash: String,
82 display_name: String,
83 ctime: i64,
84 has_content: bool,
85 is_dir: bool,
86 parent_dir: String,
87 ) -> Self {
88 Self {
89 name,
90 hash,
91 display_name,
92 ctime,
93 has_content,
94 is_dir,
95 parent_dir,
96 }
97 }
98}
99
100#[derive(Debug, thiserror::Error)]
106pub enum FsError {
107 #[error("storage quota exceeded")]
109 QuotaExceeded,
110 #[error("unsafe path, possible security issue")]
112 UnsafePath,
113 #[error("cannot unhash, maybe the file is missing")]
115 CannotUnhash,
116 #[error("{0}")]
118 Io(#[from] std::io::Error),
119}
120
121pub const STATUS_OK: &str = "ok";
127
128pub const STATUS_NOT_MODIFIED: &str = "notModified";
130
131pub const STATUS_UPDATED_ON_SERVER: &str = "updatedOnServer";
133
134pub const STATUS_MERGED: &str = "merged";
136
137pub const MAX_TEXT_SIZE: usize = 5 * 1024 * 1024;
139
140pub const MAX_TEXTS_SIZE: usize = 10 * 1024 * 1024;
142
143pub const MAX_MEDIA_SIZE: usize = 20 * 1024 * 1024;
145
146pub const MAX_MEDIAS_SIZE: usize = 512 * 1024;
148
149pub const MAX_TOKEN_SIZE: usize = 4 * 1024;
151
152#[derive(Debug, Clone, Serialize, Deserialize)]
154pub struct SyncFile {
155 pub status: String,
157 pub path: String,
159 #[serde(rename = "lastModified")]
161 pub last_modified: i64,
162 #[serde(rename = "clientLastModified", default)]
164 pub client_last_modified: i64,
165 #[serde(rename = "clientLastSynced", default)]
167 pub client_last_synced: i64,
168 #[serde(default)]
170 pub content: String,
171}
172
173#[derive(Debug, Clone, Serialize, Deserialize)]
175pub struct SyncRequest {
176 pub modified: Vec<SyncFile>,
178 pub deleted: Vec<String>,
180 pub timestamps: HashMap<String, i64>,
182}
183
184#[derive(Debug, Clone, Serialize, Deserialize)]
186pub struct SyncResponse {
187 pub status: String,
189 #[serde(default)]
191 pub files: Vec<SyncFile>,
192 #[serde(default)]
194 pub timestamps: HashMap<String, i64>,
195 #[serde(default)]
197 pub renames: HashMap<String, String>,
198}
199
200impl Default for SyncResponse {
201 fn default() -> Self {
202 SyncResponse {
203 status: STATUS_OK.to_string(),
204 files: vec![],
205 timestamps: HashMap::new(),
206 renames: HashMap::new(),
207 }
208 }
209}
210
211#[derive(Debug, thiserror::Error)]
213pub enum SyncError {
214 #[error("invalid JSON")]
216 InvalidJson,
217 #[error("file not found")]
219 NotFound,
220 #[error("quota exceeded")]
222 QuotaExceeded,
223 #[error("storage error: {0}")]
225 Storage(String),
226 #[error("internal error: {0}")]
228 Internal(String),
229}
230
231impl From<FsError> for SyncError {
232 fn from(err: FsError) -> Self {
233 match err {
234 FsError::QuotaExceeded => SyncError::QuotaExceeded,
235 _ => SyncError::Storage(err.to_string()),
236 }
237 }
238}
239
240pub type YearHabits = HashMap<i32, i32>;
246
247pub type Habits = HashMap<String, YearHabits>;
249
250pub const HABIT_SKIPPED: &str = "⚪️";
252
253pub const HABIT_COMPLETED: &str = "🟢";
255
256pub const HABIT_COMPLETED_AT_WEEKEND: &str = "🟡";
258
259pub const MOOD_HABIT: &str = "Mood";
261
262pub const MOOD_EMOJIS: &[&str] = &["⚪️", "🤕", "😔", "😐", "🙂", "😊"];
264
265#[derive(Debug, Clone, Default, Serialize, Deserialize)]
271pub struct Schedule {
272 pub filename: String,
274 pub scheduled_at: i64,
276 pub cron: String,
278 #[serde(default)]
280 pub cmd: String,
281}
282
283#[derive(Debug, Clone, Serialize, Deserialize)]
291pub struct KnowledgeConfig {
292 #[serde(default = "default_language")]
294 pub language: String,
295 #[serde(default = "default_timezone")]
297 pub timezone: String,
298 #[serde(default)]
300 pub move_to_commands: Vec<String>,
301 #[serde(default = "default_pomodoro_duration")]
303 pub pomodoro_duration_in_minutes: i64,
304 #[serde(default)]
306 pub schedules: Vec<Schedule>,
307 #[serde(default)]
309 pub quick_commands: Vec<String>,
310 #[serde(default)]
312 pub two_emojis_enabled: bool,
313 #[serde(default = "default_mode")]
315 pub mode: String,
316 #[serde(default)]
318 pub quick_habits_enabled: bool,
319 #[serde(default)]
321 pub channels: Vec<i64>,
322}
323
324fn default_language() -> String {
325 "en".to_string()
326}
327fn default_timezone() -> String {
328 "UTC".to_string()
329}
330fn default_pomodoro_duration() -> i64 {
331 50
332}
333fn default_mode() -> String {
334 "full".to_string()
335}
336
337impl Default for KnowledgeConfig {
338 fn default() -> Self {
339 Self {
340 language: default_language(),
341 timezone: default_timezone(),
342 move_to_commands: vec![],
343 pomodoro_duration_in_minutes: default_pomodoro_duration(),
344 schedules: vec![],
345 quick_commands: vec![],
346 two_emojis_enabled: false,
347 mode: default_mode(),
348 quick_habits_enabled: false,
349 channels: vec![],
350 }
351 }
352}
353
354pub const MODE_CHAT: &str = "chat";
356pub const MODE_FULL: &str = "full";
358pub const MODE_TASKS: &str = "tasks";
360pub const MODE_NOTES: &str = "notes";
362pub const MODE_JOURNAL: &str = "journal";