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 std::{
env,
fmt::Debug,
fs,
io::{self, Write},
};
use once_cell::sync::Lazy;
///
/// pub for access in log! macro
#[doc(hidden)]
pub static LOGGER: Lazy<Index> = Lazy::new(Index::default);
#[macro_export]
macro_rules! log {
($name:tt $ext:tt, $($arg:tt)*) => {
file_log::LOGGER.write_log($name, $ext, format!($($arg)*)).unwrap();
};
($name:tt, $($arg:tt)*) => {
file_log::LOGGER.write_log($name, "log", format!($($arg)*)).unwrap();
};
}
const INDEX: &str = "log_index";
const FILE_LOG_INDEX_ENV_VAR: &str = "FILE_LOG_INDEX";
#[doc(hidden)]
#[derive(Debug)]
pub struct Index(usize);
/// Represents an index used for file logging.
///
/// The `Index` struct provides methods for managing and manipulating the index value.
impl Index {
/// Increments the index value by 1.
pub fn next(&mut self) {
self.0 += 1;
}
/// Retrieves the index value from the environment variable, if it exists.
/// If the environment variable is not set, it retrieves the index value from the index file.
///
/// # Returns
///
/// The `Index` value obtained from the environment or index file.
fn get() -> Index {
match env::var(FILE_LOG_INDEX_ENV_VAR) {
// There is an env, prioritize its value
Ok(index) => Index(index.parse().unwrap_or_default()),
// There is no env, create use index_file
Err(_) => Index(
fs::read_to_string(INDEX)
.map(|i| i.parse().unwrap_or_default())
.unwrap_or_default(),
),
}
}
///
/// Save the index to the index file
fn save(&self) {
fs::write(INDEX, format!("{}", self.0)).unwrap();
}
/// Returns a copy of the index value.
///
/// # Returns
///
/// The index value.
pub fn index(&self) -> usize {
self.0
}
/// Writes a log entry to a file with the specified extension.
///
/// # Parameters
///
/// - `log`: The name of the log file.
/// - `extension`: The file extension.
/// - `data`: The data to be written to the file.
///
/// # Returns
///
/// An `io::Result` indicating the success or failure of the write operation.
pub fn write_log<C: AsRef<[u8]>>(&self, log: &str, extension: &str, data: C) -> io::Result<()> {
let mut file = fs::OpenOptions::new()
.create(true)
.append(true)
.open(format!("{log}_{}.{extension}", self.0))?;
file.write_all(data.as_ref())?;
file.write_all("\n".as_bytes())
}
}
impl Default for Index {
fn default() -> Self {
// Get the index from the env, if it exists, else get it from the index file.
let mut index = Index::get();
// If the env var is not set, increase the index and save the index to the index file
if env::var(FILE_LOG_INDEX_ENV_VAR).is_err() {
// Increment the index
index.next();
// Save the index to the index file
index.save();
}
index
}
}
pub fn index() -> usize {
LOGGER.index()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_index_next() {
let mut index = Index::default();
let initial_value = index.index();
index.next();
assert_eq!(index.index(), initial_value + 1);
}
#[test]
fn test_index_write_log() {
let index = Index::default();
let log_name = "test_log";
let extension = "txt";
let data = "Test log data";
let result = index.write_log(log_name, extension, data);
assert!(result.is_ok());
// Verify that the log file was created
let file_path = format!(
"{log}_{}.{extension}",
index.index(),
log = log_name,
extension = extension
);
assert!(fs::metadata(&file_path).is_ok());
// Clean up the log file
fs::remove_file(file_path).unwrap();
}
}