1use std::str::FromStr;
2
3use tokio::{fs, prelude::*};
4
5use rayon::{prelude::*, str::Lines};
6
7use serde::{Deserialize, Serialize};
8
9use fs::OpenOptions;
10use uuid::Uuid;
11
12use crate::{get_system_millis, ApiActionError, ID};
13
14static HUB_DATA_FOLDER: &str = "data/hubs/data";
15
16#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
17pub struct Channel {
18 #[serde(skip)]
19 pub messages: Vec<Message>,
20 pub id: ID,
21 pub server_id: ID,
22 pub name: String,
23 pub created: u128,
24}
25
26impl Channel {
27 pub async fn new(name: String, id: ID, server_id: ID) -> Result<Self, ()> {
28 let new = Self {
29 name,
30 id,
31 server_id,
32 messages: Vec::new(),
33 created: crate::get_system_millis(),
34 };
35 if let Ok(_) = new.create_dir().await {
36 Ok(new)
37 } else {
38 Err(())
39 }
40 }
41
42 pub fn get_folder(&self) -> String {
43 format!("{}/{}/{}", HUB_DATA_FOLDER, self.server_id, self.id)
44 }
45
46 pub async fn create_dir(&self) -> tokio::io::Result<()> {
47 tokio::fs::create_dir_all(self.get_folder()).await
48 }
49
50 pub async fn add_message(&mut self, message: Message) -> Result<(), ApiActionError> {
51 let message_string = &message.to_string();
52 if let Ok(mut file) = OpenOptions::new()
53 .write(true)
54 .create(true)
55 .append(true)
56 .open(self.get_current_file().await)
57 .await
58 {
59 if let Ok(_) = file
60 .write((message_string.to_owned() + "\n").as_bytes())
61 .await
62 {
63 self.messages.push(message);
64 Ok(())
65 } else {
66 Err(ApiActionError::WriteFileError)
67 }
68 } else {
69 Err(ApiActionError::OpenFileError)
70 }
71 }
72
73 pub async fn get_last_messages(&self, max: usize) -> Vec<Message> {
74 let mut result: Vec<Message> = Vec::new();
75 if let Ok(mut dir) = fs::read_dir(self.get_folder()).await {
76 let mut files = Vec::new();
77 while let Ok(Some(entry)) = dir.next_entry().await {
78 if entry.path().is_file() {
79 files.push(entry)
80 }
81 }
82 files.par_sort_by_key(|f| f.file_name());
83 files.reverse();
84 let mut whole_file = String::new();
85 for file in files.iter() {
86 if let Ok(mut file) = fs::File::open(file.path()).await {
87 whole_file.clear();
88 if let Ok(_) = file.read_to_string(&mut whole_file).await {
89 let lines = whole_file.par_lines().collect::<Vec<&str>>();
90 let found = &mut lines
91 .par_iter()
92 .filter_map(|l| {
93 if let Ok(message) = l.parse::<Message>() {
94 Some(message)
95 } else {
96 None
97 }
98 })
99 .collect::<Vec<Message>>();
100 found.par_sort_by_key(|m| m.created);
101 found.reverse();
102 result.append(found);
103 if result.len() >= max {
104 result.truncate(max);
105 return result;
106 }
107 }
108 }
109 }
110 }
111 result
112 }
113
114 pub async fn get_messages(
115 &self,
116 from: u128,
117 to: u128,
118 invert: bool,
119 max: usize,
120 ) -> Vec<Message> {
121 let mut result: Vec<Message> = Vec::new();
122 if let Ok(mut dir) = fs::read_dir(self.get_folder()).await {
123 let mut files = Vec::new();
124 while let Ok(Some(entry)) = dir.next_entry().await {
125 if entry.path().is_file() {
126 files.push(entry)
127 }
128 }
129 files.par_sort_by_key(|f| f.file_name());
130 if invert {
131 files.reverse()
132 }
133 let mut whole_file = String::new();
134 for file in files.iter() {
135 if let Ok(mut file) = fs::File::open(file.path()).await {
136 whole_file.clear();
137 if let Ok(_) = file.read_to_string(&mut whole_file).await {
138 let lines = whole_file.par_lines();
139 let mut filtered: Vec<Message> = lines
140 .filter_map(|l| {
141 let created = l
142 .splitn(4, ',')
143 .skip(2)
144 .next()
145 .unwrap_or("0")
146 .parse::<u128>()
147 .unwrap_or(0);
148 if created >= from && created <= to {
149 if let Ok(message) = l.parse::<Message>() {
150 Some(message)
151 } else {
152 None
153 }
154 } else {
155 None
156 }
157 })
158 .collect();
159 if invert {
160 filtered.reverse()
161 }
162 filtered.truncate(max - result.len());
163 result.append(&mut filtered);
164 if result.len() >= max {
165 result.truncate(max);
166 return result;
167 }
168 }
169 }
170 }
171 }
172 result
173 }
174
175 pub async fn on_all_raw_lines<F: FnMut(Lines) -> ()>(&self, mut action: F) {
176 if let Ok(mut dir) = fs::read_dir(self.get_folder()).await {
177 let mut whole_file = String::new();
178 while let Ok(Some(entry)) = dir.next_entry().await {
179 if entry.path().is_file() {
180 if let Ok(mut file) = fs::File::open(entry.path()).await {
181 whole_file.clear();
182 if let Ok(_) = file.read_to_string(&mut whole_file).await {
183 let lines = whole_file.par_lines();
184 action(lines)
185 }
186 }
187 }
188 }
189 }
190 }
191
192 pub async fn find_messages_containing(
193 &self,
194 string: String,
195 case_sensitive: bool,
196 ) -> Vec<Message> {
197 let mut results: Vec<Message> = Vec::new();
198 let mut search = string.clone();
199 if !case_sensitive {
200 search.make_ascii_uppercase()
201 }
202 self.on_all_raw_lines(|lines| {
203 let mut result: Vec<Message> = lines
204 .filter_map(|l| {
205 let mut compare_string = l.splitn(4, ',').last().unwrap_or("").to_string();
206 if !case_sensitive {
207 compare_string.make_ascii_uppercase();
208 }
209 if compare_string.contains(&search) {
210 if let Ok(message) = l.parse::<Message>() {
211 Some(message)
212 } else {
213 None
214 }
215 } else {
216 None
217 }
218 })
219 .collect();
220 results.append(&mut result);
221 })
222 .await;
223 results
224 }
225
226 pub async fn get_message(&self, id: String) -> Option<Message> {
227 for message in self.messages.iter() {
228 if message.id.to_string() == id {
229 return Some(message.clone());
230 }
231 }
232 let id = id.as_str();
233 let mut result: Option<Message> = None;
234 self.on_all_raw_lines(|lines| {
235 let results: Vec<Message> = lines
236 .filter_map(|l| {
237 if l.starts_with(id) {
238 if let Ok(message) = l.parse::<Message>() {
239 Some(message)
240 } else {
241 None
242 }
243 } else {
244 None
245 }
246 })
247 .collect();
248 if let Some(message) = results.first() {
249 result = Some(message.clone());
250 }
251 })
252 .await;
253 return result;
254 }
255
256 pub async fn get_current_file(&mut self) -> String {
257 let now = get_system_millis() / 1000 / 60 / 60 / 24;
258 self.messages.reverse();
259 self.messages.truncate(100);
260 self.messages.reverse();
261 format!("{}/{}", self.get_folder(), now)
262 }
263}
264
265#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
266pub struct Message {
267 pub id: ID,
268 pub sender: ID,
269 pub created: u128,
270 pub content: String,
271}
272
273impl ToString for Message {
274 fn to_string(&self) -> String {
275 format!(
276 "{},{},{},{}",
277 self.id.as_u128(),
278 self.sender.as_u128(),
279 self.created,
280 self.content.replace('\n', r#"\n"#)
281 )
282 }
283}
284
285impl FromStr for Message {
286 type Err = ();
287
288 fn from_str(s: &str) -> Result<Self, Self::Err> {
289 let mut parts = s.splitn(4, ',');
290 if let Some(id_str) = parts.next() {
291 if let Ok(id) = uuid_from_num_string(id_str) {
292 if let Some(sender_str) = parts.next() {
293 if let Ok(sender) = uuid_from_num_string(sender_str) {
294 if let Some(created_str) = parts.next() {
295 if let Ok(created) = created_str.parse::<u128>() {
296 if let Some(content) = parts.next() {
297 return Ok(Self {
298 id,
299 sender,
300 created,
301 content: content.replace(r#"\n"#, "\n"),
302 });
303 }
304 }
305 }
306 }
307 }
308 }
309 }
310 return Err(());
311 }
312}
313
314fn uuid_from_num_string(string: &str) -> Result<Uuid, ()> {
315 if let Ok(num) = string.parse::<u128>() {
316 Ok(Uuid::from_u128(num))
317 } else {
318 Err(())
319 }
320}
321
322#[cfg(test)]
323mod tests {
324 use std::str::FromStr;
325
326 use uuid::Uuid;
327
328 use super::{Channel, Message};
329
330 fn new_test_message(variation: u128) -> Message {
331 Message {
332 id: Uuid::from_u128(123456789 + variation),
333 sender: Uuid::from_u128(0987654321),
334 created: 12222020,
335 content: "This is a test message.\nWith a newline.".to_string()
336 + &variation.to_string(),
337 }
338 }
339
340 async fn new_test_channel() -> Channel {
341 let _remove = tokio::fs::remove_dir_all("data/hubs/data/00000000-0000-0000-0000-0000075bcd15/00000000-0000-0000-0000-0000075bcd15").await;
342 let channel = Channel::new(
343 "test".to_string(),
344 Uuid::from_u128(123456789),
345 Uuid::from_u128(123456789),
346 )
347 .await
348 .expect("Could not create a channel with ID \"123456789\".");
349 channel
350 }
351
352 #[test]
353 fn message_serialize() {
354 let message_struct = new_test_message(0);
355 let message_string =
356 r#"123456789,987654321,12222020,This is a test message.\nWith a newline.0"#.to_string();
357 assert_eq!(message_struct.to_string(), message_string);
358 }
359
360 #[test]
361 fn message_deserialize() {
362 let message_struct = new_test_message(0);
363 let message_string =
364 r#"123456789,987654321,12222020,This is a test message.\nWith a newline.0"#.to_string();
365 assert_eq!(Message::from_str(&message_string).unwrap(), message_struct);
366 }
367
368 #[tokio::test]
369 async fn new_channel() {
370 new_test_channel().await;
371 assert!(std::path::Path::new("data/hubs/data/00000000-0000-0000-0000-0000075bcd15/00000000-0000-0000-0000-0000075bcd15").exists());
372 }
373
374 #[tokio::test]
375 #[serial]
376 async fn add_message() {
377 let mut channel = new_test_channel().await;
378 let message = new_test_message(0);
379 channel.add_message(message.clone()).await.unwrap();
380 let mut file_string = tokio::fs::read_to_string(channel.get_current_file().await)
381 .await
382 .unwrap();
383 if let Some(string) = file_string.strip_suffix('\n') {
384 file_string = string.to_string();
385 }
386 assert_eq!(Message::from_str(&file_string).unwrap(), message);
387 }
388
389 #[tokio::test]
390 #[serial]
391 async fn get_last_n_messages() {
392 let mut channel = new_test_channel().await;
393 let message_0 = new_test_message(1);
394 let message_1 = new_test_message(2);
395 let message_2 = new_test_message(3);
396 channel.add_message(message_0.clone()).await.unwrap();
397 channel.add_message(message_1.clone()).await.unwrap();
398 channel.add_message(message_2.clone()).await.unwrap();
399 assert_eq!(
400 channel.get_last_messages(2).await,
401 vec![message_2, message_1]
402 );
403 }
404
405 #[tokio::test]
406 #[serial]
407 async fn get_message() {
408 let mut channel = new_test_channel().await;
409 let message_0 = new_test_message(4);
410 let message_1 = new_test_message(5);
411 let message_2 = new_test_message(6);
412 channel.add_message(message_0.clone()).await.unwrap();
413 channel.add_message(message_1.clone()).await.unwrap();
414 channel.add_message(message_2.clone()).await.unwrap();
415 assert_eq!(
416 channel
417 .get_message("00000000-0000-0000-0000-0000075bcd19".to_string())
418 .await
419 .unwrap()
420 .content,
421 "This is a test message.\nWith a newline.4".to_string()
422 );
423 }
424
425 #[tokio::test]
426 #[serial]
427 async fn find_messages_containing() {
428 let mut channel = new_test_channel().await;
429 let message_0 = new_test_message(7);
430 let message_1 = new_test_message(8);
431 let message_2 = new_test_message(9);
432 channel.add_message(message_0.clone()).await.unwrap();
433 channel.add_message(message_1.clone()).await.unwrap();
434 channel.add_message(message_2.clone()).await.unwrap();
435 assert_eq!(
436 channel
437 .find_messages_containing("newline.7".to_string(), true)
438 .await,
439 vec![message_0.clone()]
440 );
441 assert_eq!(
442 channel
443 .find_messages_containing("NeWlInE.7".to_string(), true)
444 .await,
445 vec![]
446 );
447 assert_eq!(
448 channel
449 .find_messages_containing("newline.8".to_string(), false)
450 .await,
451 vec![message_1.clone()]
452 );
453 assert_eq!(
454 channel
455 .find_messages_containing("NeWlInE.8".to_string(), false)
456 .await,
457 vec![message_1.clone()]
458 );
459 let all = vec![message_0, message_1, message_2];
460 assert_eq!(
461 channel
462 .find_messages_containing("This".to_string(), true)
463 .await,
464 all.clone()
465 );
466 assert_eq!(
467 channel
468 .find_messages_containing("this".to_string(), true)
469 .await,
470 vec![]
471 );
472 assert_eq!(
473 channel
474 .find_messages_containing("This".to_string(), false)
475 .await,
476 all.clone()
477 );
478 assert_eq!(
479 channel
480 .find_messages_containing("this".to_string(), false)
481 .await,
482 all.clone()
483 );
484 }
485
486 #[tokio::test]
487 #[serial]
488 async fn messages_between_dates() {
489 let mut channel = new_test_channel().await;
490 let message_0 = Message {
491 id: Uuid::from_u128(134),
492 sender: Uuid::from_u128(0987654321),
493 created: 1,
494 content: "This is a test message.\nWith a newline.".to_string(),
495 };
496 let message_1 = Message {
497 id: Uuid::from_u128(135),
498 sender: Uuid::from_u128(0987654321),
499 created: 2,
500 content: "This is a test message.\nWith a newline.".to_string(),
501 };
502 let message_2 = Message {
503 id: Uuid::from_u128(136),
504 sender: Uuid::from_u128(0987654321),
505 created: 3,
506 content: "This is a test message.\nWith a newline.".to_string(),
507 };
508 let message_3 = Message {
509 id: Uuid::from_u128(137),
510 sender: Uuid::from_u128(0987654321),
511 created: 4,
512 content: "This is a test message.\nWith a newline.".to_string(),
513 };
514 channel.add_message(message_0.clone()).await.unwrap();
515 channel.add_message(message_1.clone()).await.unwrap();
516 channel.add_message(message_2.clone()).await.unwrap();
517 channel.add_message(message_3.clone()).await.unwrap();
518 assert_eq!(
519 channel.get_messages(2, 3, true, 2).await,
520 vec![message_2.clone(), message_1.clone()]
521 );
522 assert_eq!(
523 channel.get_messages(2, 3, false, 2).await,
524 vec![message_1.clone(), message_2.clone()]
525 );
526 assert_eq!(channel.get_messages(2, 3, true, 1).await, vec![message_2]);
527 assert_eq!(channel.get_messages(2, 3, false, 1).await, vec![message_1]);
528 }
529}