1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
use super::{msg::LogMessage, LogLevel};
use std::{fs::{self, File}, io::Write};

/// A writer for buffering the log and writing them into the suitable files.
pub struct Writer {
    // where stores the log files.
    dir_path: String,
    // the maximum number of logs in a single file.
    single_length: usize,
    // the current index of the file.
    current_index: usize,
    // a buffer for the log that are waiting to be written.
    log_buffer: Vec<LogMessage>,
    // define the minimum level of the log that should be written. (inclusive)
    file_record: LogLevel,
    terminal_print: LogLevel,
    // define to write the detailed time or not.
    time_details: bool,
    used_length: usize,
    time_prefix: String,
    file: Option<File>,
    time_zone: i32,
    print_out: bool,
}

impl Writer {
    /// Customize and initialize the log writer.
    pub fn new(dir_path: &str, single_length: usize, file_record: Option<LogLevel>, terminal_print: Option<LogLevel>, time_zone: i32, time_details: bool, print_out: bool) -> Writer {
        let time_prefix = format!("{}", chrono::Utc::now().format("%Y-%m-%d"));
        let mut buffer = Self {
            dir_path: dir_path.to_string(),
            single_length,
            current_index: 1,
            file_record: file_record.unwrap_or(LogLevel::Trace),
            terminal_print: terminal_print.unwrap_or(LogLevel::Debug),
            log_buffer: Vec::with_capacity(single_length),
            time_details,
            time_prefix,
            used_length: 0,
            file: None,
            time_zone,
            print_out
        };
        buffer.current_index = buffer.get_index(&buffer.time_prefix);
        buffer.file = Some(buffer.get_file());
        buffer
    }
  
    /// Initialize the log writer with the default settings.
    pub fn default(dir_path: &str) -> Writer { Writer::new(dir_path, 200, None, None, 0, false, true) }

    /// Push a log into the buffer.
    pub fn push(&mut self, log_level: LogLevel, message: &str) {
        let log_message = LogMessage::new(log_level, message.to_string(), self.time_zone);
        self.log_buffer.push(log_message);
    }

    pub fn clear_dir(&mut self) {
        fs::remove_dir_all(&self.dir_path).expect("Cannot remove the dir.");
        fs::create_dir(&self.dir_path).expect("Cannot create the dir.");
        self.current_index = 0;
        self.used_length = 0;
        self.file = Some(self.get_file())
    }
    /// Write all the logs in the buffer into the files.
    /// and also clear the buffer.
    pub fn write_all(&mut self) {
        for msg in self.log_buffer.clone().iter_mut() {
            if self.time_details { msg.set_detailed_time() } else { msg.set_rough_time() }
            self.write_single(msg);
            if self.used_length >= self.single_length && self.single_length != 0 { 
                self.current_index += 1;
                self.used_length = 0;
                self.file = Some(self.get_file());
            }
        }
        self.log_buffer.clear();
    }

    /// Combine the push and write_all methods.
    pub fn record(&mut self, log_level: LogLevel, message: &str) {
        self.push(log_level, message);
        self.write_all();
    }

    /// Record an info log.
    pub fn info(&mut self, message: &str) {
        self.record(LogLevel::Info, message);
    }

    /// Record a debug log.
    pub fn debug(&mut self, message: &str) {
        self.record(LogLevel::Debug, message);
    }

    /// Record a warn log.
    pub fn warn(&mut self, message: &str) {
        self.record(LogLevel::Warn, message);
    }

    /// Record an error log.
    pub fn error(&mut self, message: &str) {
        self.record(LogLevel::Error, message);
    }

    /// Record a trace log.
    pub fn trace(&mut self, message: &str) {
        self.record(LogLevel::Trace, message);
    }

    fn write_single(&mut self, msg: &LogMessage) {
        for i in msg.split_enter() {
            let time_prefix = format!("{}", chrono::Utc::now().format("%Y-%m-%d"));
            if self.time_prefix != time_prefix {
                self.time_prefix = time_prefix;
                self.current_index = 0;
                self.used_length = 0;
                self.file = Some(self.get_file());
            };
            if self.print_out && self.terminal_print.get_level() <= i.get_level() { println!("{}", i.print()) };
            if self.file_record.get_level() <= i.get_level() {
                self.file.as_mut().unwrap().write_all((i.print() + "\n").as_bytes()).expect("Cannot write into the log file.");
                self.used_length += 1;
            };
        }
    }

    fn get_index(&self, time_prefix: &str) -> usize {
        let mut count = 1;
        loop {
            let path = self.get_path(time_prefix, count);
            if let Ok(_) = File::open(path) { count += 1 }
            else {
                // let _ = File::create(self.get_path(&self.time_prefix, count)).expect("Cannot create the log file.");
                return count
            }
        }
    }

    fn get_path(&self, time_prefix: &str, index: usize) -> String {
        format!("{}\\{}-{}.log", self.dir_path, time_prefix, index)
    }

    fn get_file(&self) -> File {
        let path = self.get_path(&self.time_prefix, self.current_index);
        File::options().read(true).write(true).create_new(true).open(path).expect("Cannot create the log file.")
    }
}