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}