ferris_chats_data/
lib.rs

1use chrono::{DateTime, Utc};
2use core::error::Error;
3use dirs::home_dir;
4use serde::{Deserialize, Serialize};
5use serde_json::{from_str, to_vec};
6use std::ffi::OsString;
7use std::fs::{create_dir_all, read_to_string, write};
8use std::path::PathBuf;
9use std::sync::{Arc, Mutex};
10static FILENAME: &str = "messages.json";
11
12#[derive(Clone)]
13pub struct AppState {
14    pub data: Arc<Mutex<Messages>>,
15}
16#[derive(Default, Deserialize, Serialize, Clone)]
17pub struct Message {
18    content: String,
19    author: Option<String>,
20    time: DateTime<Utc>,
21}
22#[derive(Deserialize, Serialize, Clone)]
23pub struct IncomingMessage {
24    pub content: String,
25    pub author: Option<String>,
26}
27impl Message {
28    pub fn new(content: String, author: Option<String>) -> Self {
29        Self {
30            content,
31            author: Some(author.unwrap_or_else(|| String::from("Unknown"))),
32            time: Utc::now(),
33        }
34    }
35}
36#[derive(Deserialize, Serialize, Clone)]
37pub struct Messages {
38    pub messages: Vec<Message>,
39}
40impl Default for Messages {
41    fn default() -> Self {
42        Self::new()
43    }
44}
45
46impl Messages {
47    pub const fn new() -> Self {
48        Self {
49            messages: Vec::new(),
50        }
51    }
52    pub fn from_existing_else_new() -> Self {
53        Self::from_messages().unwrap_or_default()
54    }
55    fn from_messages() -> Result<Self, Box<dyn Error>> {
56        Ok(from_str(
57            read_to_string(file_in_path(String::from(FILENAME)))?.as_str(),
58        )?)
59    }
60    #[expect(clippy::unwrap_used)]
61    pub fn save_messages(&self) {
62        _ = create_dir_all(file_in_path(String::new())).is_ok();
63        write(file_in_path(String::from(FILENAME)), to_vec(&self).unwrap())
64            .expect("Unable to write file");
65    }
66
67    pub fn add(&mut self, content: String, author: Option<String>) {
68        self.messages.push(Message::new(content, author));
69    }
70    pub fn add_message(&mut self, message: Message) {
71        self.messages.push(message);
72    }
73    pub fn add_messages(&mut self, new: &mut Self) {
74        self.messages.append(&mut new.messages);
75    }
76    pub fn concat_message(self, message: Message) -> Self {
77        let mut messages = self.messages;
78        messages.push(message);
79        Self { messages }
80    }
81    pub fn get_range(self, start: usize, end: usize) -> Option<Self> {
82        let message_slice = self.messages.get(start..end);
83        message_slice.map(|messages| Self {
84            messages: messages.to_owned(),
85        })
86    }
87    pub fn message_count(&self) -> usize {
88        self.messages.len()
89    }
90    pub fn last_index_at_time(&self, time: DateTime<Utc>) -> Option<usize> {
91        self.messages
92            .clone()
93            .into_iter()
94            .enumerate()
95            .filter(|x: &(usize, Message)| x.1.time <= time)
96            .map(|x: (usize, Message)| x.0)
97            .last()
98    }
99}
100pub fn file_in_path(file_name: String) -> OsString {
101    PathBuf::from(
102        &[
103            home_dir()
104                .unwrap_or_else(|| "".into())
105                .display()
106                .to_string(),
107            String::from("/.ferris_chats/"),
108            file_name,
109        ]
110        .join(""),
111    )
112    .into_os_string()
113}