flogging/handlers/
file_handler.rs

1//
2// File Name:    file_handler.rs
3// Project Name: flogging
4//
5// Copyright (C) 2025 Bradley Willcott
6//
7// SPDX-License-Identifier: GPL-3.0-or-later
8//
9// This library (crate) is free software: you can redistribute it and/or modify
10// it under the terms of the GNU General Public License as published by
11// the Free Software Foundation, either version 3 of the License, or
12// (at your option) any later version.
13//
14// This library (crate) is distributed in the hope that it will be useful,
15// but WITHOUT ANY WARRANTY; without even the implied warranty of
16// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17// GNU General Public License for more details.
18//
19// You should have received a copy of the GNU General Public License
20// along with this library (crate).  If not, see <https://www.gnu.org/licenses/>.
21//
22
23//!
24//! # FileHandler
25//!
26
27#![allow(unused)]
28
29use std::{
30    fmt,
31    fs::{File, exists},
32    io::{Error, ErrorKind::InvalidInput, Write},
33};
34
35use crate::{
36    handlers::{
37        formatter::{FormatType, Formatter},
38        handler::HandlerTrait,
39    },
40    logger::{Level, LogEntry},
41};
42
43///
44/// Publishes log entries to the file whose name was provided during
45/// initialization.
46///
47#[derive(Debug, Default)]
48pub struct FileHandler {
49    filename: String,
50    formatter: Formatter,
51    file: Option<File>,
52}
53
54impl FileHandler {
55    fn create(filename: &str) -> Result<Self, Error> {
56        if filename.is_empty() {
57            return Err(Error::new(InvalidInput, "'filename' must not be empty"));
58        }
59
60        let fh = FileHandler {
61            filename: filename.to_string(),
62            formatter: FormatType::Iso8601.create(None),
63            file: {
64                let f = File::options().append(true).create(true).open(filename)?;
65
66                Some(f)
67            },
68        };
69
70        Ok(fh)
71    }
72}
73
74impl fmt::Display for FileHandler {
75    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76        write!(f, "{} : {}", self.filename, self.formatter)
77    }
78}
79
80impl HandlerTrait for FileHandler {
81    ///
82    /// Create a new handler instance.
83    ///
84    /// ## Parameters
85    /// - `name` - This the `filename` of the log file.
86    ///
87    fn create(name: &str) -> Result<Self, Error> {
88        FileHandler::create(name)
89    }
90
91    fn close(&mut self) {
92        self.flush();
93        self.file = None;
94    }
95
96    fn flush(&mut self) {
97        if let Some(f) = &self.file {
98            f.sync_all();
99        }
100    }
101
102    fn get_formatter(&self) -> Formatter {
103        self.formatter.clone()
104    }
105
106    fn get_log(&self) -> String {
107        String::new()
108    }
109
110    fn is_open(&self) -> bool {
111        self.file.is_some()
112    }
113
114    #[allow(private_interfaces)]
115    fn publish(&mut self, log_entry: &LogEntry) {
116        if self.is_open() {
117            let mut buf = self.formatter.format(log_entry);
118            buf.push('\n');
119
120            self.file.as_mut().unwrap().write_all(buf.as_bytes());
121        }
122    }
123
124    fn set_formatter(&mut self, formatter: Formatter) {
125        self.formatter = formatter;
126    }
127}
128
129#[cfg(test)]
130mod tests{
131    use super::*;
132
133    use crate::{logger, Logger};
134
135    #[test]
136    fn handler_trait(){
137        let mut log = Logger::file_logger(module_path!(), "test.log");
138
139        log.info("trait methods");
140
141        let handler = log.get_handler(crate::Handler::File).unwrap();
142        assert!(handler.is_open());
143        assert_eq!(handler.get_formatter().to_string(), "dt_fmt: \"%+\" - fmt_string: \"{dt:35} |{mod_path}->{fn_name}| [{level:7}] {message}\"".to_string());
144        assert_eq!(handler.get_log(),"".to_string());
145        handler.flush();
146        handler.close();
147    }
148
149    #[test]
150    #[should_panic(expected = "'filename' must not be empty")]
151    fn filename_empty(){
152        let mut log = Logger::file_logger(module_path!(), "");
153    }
154
155
156}