log_t/
file_logger.rs

1pub mod logging_implementations {
2
3    extern crate chrono;
4    use crate::logging_abstraction::Logger;
5    use contracts::*; 
6    use std::{fs::File, io::Write};
7
8    const UNINITIALIZED_FILE_HANDLE_EXCEPTION: &str = "The log file was not initialized before writing to the log.";
9
10    pub struct FileLogger {
11        ///The path to the disk stored log file.
12        pub logfile_path: String,
13
14        ///Our actual handle to manipulate the file we use for logging.
15        file_handle: Option<File>
16        }
17
18        impl FileLogger {
19
20            /// Initializes a new FileLogger, and calls the open() function.
21            #[debug_requires(!file_path.trim().is_empty(), "Empty or whitespace filepaths are not allowed")]
22            pub fn new_from_string(file_path: String) -> Result<FileLogger, std::io::Error> {
23                let mut new_logger = FileLogger {
24                    logfile_path: file_path,
25                    file_handle: None };
26
27                //I need to specify that open() is only borrowing "self" to return ownership back, instead of it moving
28                match new_logger.open() {
29                    Some(error) => Err(error),
30                    None => return Ok(new_logger)
31                }
32            }
33
34            /// Initializes a new FileLogger, and calls the open() function.
35            pub fn new_from_static_string(file_path: &str) -> Result<FileLogger, std::io::Error> {
36                return FileLogger::new_from_string(String::from(file_path));
37            }
38
39            ///Formats a log entry by creating a timestamp, then adding the data to it.
40            #[debug_requires(!to_write.to_string().is_empty(), "We shouldn't try to write empty data to the log")]
41            #[debug_ensures(!ret.trim().is_empty(), "The formatted log entry shouldn't be empty")]
42            fn format_entry<T>(&self, to_write: T) -> String
43                where T: std::fmt::Display
44            {
45                let current_time = chrono::Local::now().format("%Y-%m-%d %H:%M:%S");
46                return std::format!("[{}]: {}\n", current_time, to_write);
47            }
48        }
49
50        impl Logger for FileLogger {
51
52            fn open(&mut self) -> Option<std::io::Error>
53            {
54                let created_file = File::create(&self.logfile_path);
55                
56                match created_file {
57                    Ok(created_file_handle) => self.file_handle = Some(created_file_handle),
58                    Err(error) => return Some(error)
59                }
60
61                None
62            }
63
64            fn write<T: std::fmt::Display>(&self, to_write: T) -> Option<std::io::Error>
65            {
66                //Verify that the log file has been opened already
67                if self.file_handle.is_none() {
68                    return Some(std::io::Error::new(std::io::ErrorKind::NotFound, UNINITIALIZED_FILE_HANDLE_EXCEPTION));
69                }
70                
71                let mut log_file = self.file_handle.as_ref().unwrap();
72                //Format our data and timestamp together
73                let to_write = self.format_entry(to_write);
74                println!("{}", to_write);
75                let write_result = log_file.write_all(to_write.as_bytes());
76                
77                if write_result.is_err() {
78                    return write_result.err();
79                }
80
81                None
82            }
83    
84            fn write_slice<T>(&self, to_write: &[&T]) -> std::option::Option<std::io::Error>
85                where T: std::fmt::Display
86            {
87                //Write each item to our file destination
88                for item in to_write {
89                    let write_result = self.write(item);
90                    if write_result.is_some() {
91                        return write_result;
92                    }
93                }
94
95                None
96            }
97        }
98}