1use chrono::{DateTime, Utc};
2use log::info;
3use serde::{Deserialize, Serialize};
4use std::{io::Write, path::PathBuf};
5
6use crate::error::NanonisError;
7
8#[derive(Debug, Serialize, Deserialize)]
9struct LogEntry<T>
10where
11 T: Serialize,
12{
13 timestamp: DateTime<Utc>,
14 data: T,
15}
16
17#[derive(Debug)]
18pub struct Logger<T>
19where
20 T: Serialize,
21{
22 buffer: Vec<LogEntry<T>>,
23 buffer_size: usize,
24 file_path: PathBuf,
25}
26
27impl<T> Logger<T>
28where
29 T: Serialize,
30{
31 pub fn new<P: Into<PathBuf>>(file_path: P, buffer_size: usize) -> Self {
32 Self {
33 buffer: Vec::with_capacity(buffer_size),
34 buffer_size,
35 file_path: file_path.into(),
36 }
37 }
38
39 pub fn add(&mut self, data: T) -> Result<(), NanonisError> {
40 let entry = LogEntry {
41 timestamp: Utc::now(),
42 data,
43 };
44
45 self.buffer.push(entry);
46
47 if self.buffer.len() >= self.buffer_size {
48 self.flush()?;
49 }
50
51 Ok(())
52 }
53
54 pub fn flush(&mut self) -> Result<(), NanonisError> {
55 if self.buffer.is_empty() {
56 return Ok(());
57 }
58
59 let file = std::fs::OpenOptions::new()
60 .create(true)
61 .append(true)
62 .open(&self.file_path)
63 .map_err(|source| NanonisError::Io {
64 source,
65 context: format!(
66 "Logger could not create file at {:?}",
67 self.file_path
68 ),
69 })?;
70
71 let mut writer = std::io::BufWriter::new(file);
72
73 for entry in &self.buffer {
74 let json_line = serde_json::to_string(entry)?;
75 writeln!(writer, "{}", json_line)?;
76 }
77
78 writer.flush()?;
79 self.buffer.clear();
80 info!("Logger flushed successfully to file");
81 Ok(())
82 }
83
84 pub fn len(&self) -> usize {
85 self.buffer.len()
86 }
87
88 pub fn is_empty(&self) -> bool {
89 self.buffer.len() == 0
90 }
91}
92
93impl<T> Drop for Logger<T>
94where
95 T: Serialize,
96{
97 fn drop(&mut self) {
98 let _ = self.flush();
99 }
100}