use crate::thread::log_thread_enabled;
use crate::thread::launch_task;
use chrono::{Datelike, Timelike};
use std::io::{ErrorKind, Write};
#[cfg(feature="log_server")]
use curl::easy::Easy;
static mut GLOBAL_LOG_FILE: String = String::new();
static mut ALLOW_CONSOLE_LOG: bool = false;
static mut MESSAGE_BOX_TRIGGER: Option<String> = None;
static mut LOG_SERVER_URL: String = String::new();
pub fn set_message_box_trigger(trigger: Option<String>) {
unsafe {
MESSAGE_BOX_TRIGGER = trigger;
}
}
pub fn set_allow_console_log(allowed: bool) {
unsafe {
ALLOW_CONSOLE_LOG = allowed;
}
}
pub fn set_log_server(url: String) {
unsafe {
LOG_SERVER_URL = url;
}
}
pub fn set_or_create_global_log_file(file: &str) {
unsafe {
GLOBAL_LOG_FILE = file.to_owned();
}
}
#[derive(Clone)]
pub enum FileFormat {
CSV,
#[cfg(feature="message_box")]
INI
}
static mut FILE_FORMAT: FileFormat = FileFormat::CSV;
pub fn set_file_format(file_format: FileFormat) {
unsafe { FILE_FORMAT = file_format }
}
pub fn get_file_format() -> FileFormat {
unsafe { FILE_FORMAT.clone() }
}
pub fn simple_log(location: Vec<&str>, content: &str) {
log(unsafe { &GLOBAL_LOG_FILE }, location, content)
}
pub fn log(file: &str, location: Vec<&str>, content: &str) {
if log_thread_enabled() {
let location_owned: Vec<String> = location.iter().map(|s| s.to_string()).collect();
let file = file.to_string().clone();
let content = content.to_string().clone();
launch_task(Box::new(move || {
let location_str: Vec<&str> = location_owned.iter().map(|s| s.as_str()).collect();
log_internal(file.as_str(), location_str, content.as_str()).unwrap_or_else(|e| {
eprintln!("Failed to log to file '{}': {}", file, e);
});
}), 1);
} else {
log_internal(file, location, content).unwrap_or_else(|e| {
eprintln!("Failed to log to file '{}': {}", file, e);
});
}
}
fn log_internal(file: &str, location: Vec<&str>, content: &str) -> std::io::Result<()> {
let now: chrono::DateTime<chrono::Local> = chrono::Local::now();
let log_name = format!(
"Y{}_M{}_D{}_H{}_M{}_S{}_ML{}",
now.year(),
now.month(),
now.day(),
now.hour(),
now.minute(),
now.second(),
now.timestamp_millis()
);
let _ = now;
#[cfg(feature="message_box")]
match unsafe { &MESSAGE_BOX_TRIGGER } {
Some(trigger) => {
if location.contains(&trigger.as_str()) {
fltk::dialog::message_title(&log_name);
fltk::dialog::alert(100, 100, content);
}
}
None => (),
}
if file.is_empty() {
return Err(std::io::Error::new(
ErrorKind::NotFound,
format!("Unable to create a log for {}", file),
));
}
if unsafe { ALLOW_CONSOLE_LOG } {
println!("[{}] {}", log_name, content);
}
#[cfg(feature="log_server")]
if unsafe { !LOG_SERVER_URL.is_empty() } {
let request = format!(
"{}/log/{}/{}/{}",
unsafe { LOG_SERVER_URL.clone() },
"full-logger",
location.first().unwrap_or(&"debug"),
content
);
let mut easy = Easy::new();
easy.url(request.as_str()).unwrap();
match easy.perform() {
Ok(_) => {
}
Err(e) => {
eprintln!("Failed to send log to server: {}", e);
unsafe { LOG_SERVER_URL.clear(); } }
}
}
match get_file_format() {
FileFormat::CSV => {
let mut file = std::fs::File::options().append(true).open(file).unwrap();
let mut line = format!("{};", chrono::Local::now().to_string());
for loc in location {
line.push_str(&format!("{};", loc));
}
line.push_str(&format!("{};", content));
writeln!(&mut file, "{}", line)?;
}
#[cfg(feature="message_box")]
FileFormat::INI => {
use pretty_ini::{ini, ini_file, variable::Variable};
let mut ini_file = ini_file::IniFile::default();
ini_file.set_path(&file);
let mut ini = ini::Ini::default();
ini.load(&mut ini_file).unwrap();
match ini.get_table_ref_mut(location.first().unwrap()) {
Ok(table) => {
let mut log = Variable::default();
log.key = log_name;
log.value = String::from(content);
table.add_variable(log);
}
Err(_) => {
ini.create_table(location.first().unwrap());
match ini.get_table_ref_mut(location.first().unwrap()) {
Ok(table) => {
let mut log = Variable::default();
log.key = log_name;
log.value = String::from(content);
table.add_variable(log);
}
Err(_) => (),
}
}
}
ini_file.save(&mut ini, None).unwrap();
}
}
Ok(())
}
pub fn simple_log_result<O: std::fmt::Debug, E: std::fmt::Debug>(
location: Vec<&str>,
content: Result<O, E>,
) -> Result<O, E> {
log_result(unsafe { &GLOBAL_LOG_FILE }, location, content)
}
pub fn log_result<O: std::fmt::Debug, E: std::fmt::Debug>(
file: &str,
location: Vec<&str>,
content: Result<O, E>,
) -> Result<O, E> {
let log_content;
match &content {
Ok(log) => {
log_content = format!("RESULT_OK_{:?}", log);
}
Err(log) => {
log_content = format!("RESULT_ERR_{:?}", log);
}
}
log(file, location, &log_content);
content
}
pub fn simple_log_option<O: std::fmt::Debug>(location: Vec<&str>, content: Option<O>) -> Option<O> {
log_option(unsafe { &GLOBAL_LOG_FILE }, location, content)
}
pub fn log_option<O: std::fmt::Debug>(
file: &str,
location: Vec<&str>,
content: Option<O>,
) -> Option<O> {
let log_content;
match &content {
Some(log) => {
log_content = format!("Some({:?})", log);
}
None => {
log_content = "None".to_owned();
}
}
log(file, location, &log_content);
content
}