use std::{
env,
fs::{read_to_string, OpenOptions},
io::{Error, ErrorKind, Write},
path::Path,
};
use colored::Colorize;
use log::LevelFilter;
use ron::ser::{to_string_pretty, PrettyConfig};
use serde::{Deserialize, Serialize};
const STDOUT_DEV: &str = "/dev/stdout";
const MAX_DIR_DEPTH: usize = 5;
const MAX_OPEN_FILES: usize = 1023;
const TAIL_BYTES: u64 = 2048;
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Config {
pub output: Option<String>,
pub log_level: Option<String>,
pub max_open_files: Option<usize>,
pub tail_bytes: Option<u64>,
pub follow_links: Option<bool>,
pub max_dir_depth: Option<usize>,
}
impl Default for Config {
fn default() -> Self {
Config {
output: Some(String::from(STDOUT_DEV)),
log_level: Some(String::from("INFO")),
max_open_files: Some(MAX_OPEN_FILES),
tail_bytes: Some(TAIL_BYTES),
max_dir_depth: Some(MAX_DIR_DEPTH),
follow_links: Some(true),
}
}
}
pub fn write_append(file_path: &str, contents: &str) {
if !contents.is_empty() {
match OpenOptions::new()
.create(true)
.append(true)
.open(&file_path)
{
Ok(mut file) => {
file.write_all(contents.as_bytes()).unwrap_or_else(|_| {
panic!("Access denied? File can't be written: {}", &file_path)
});
debug!("Atomically written data to file: {}", &file_path);
}
Err(err) => {
error!(
"Atomic write to: {} has failed! Cause: {}",
&file_path,
err.to_string()
)
}
}
}
}
impl Config {
pub fn load() -> Config {
let config = Config::get_or_create();
read_to_string(&config)
.and_then(|file_contents| {
ron::from_str(&*file_contents).map_err(|err| {
let config_error = Error::new(ErrorKind::InvalidInput, err.to_string());
error!(
"Configuration error: {} in file: {}",
err.to_string().red(),
config.cyan()
);
config_error
})
})
.unwrap_or_default()
}
pub fn get_or_create() -> String {
let config_paths = [
&format!("{}/.lw.conf", env::var("HOME").unwrap_or_default()),
&format!("{}/.config/lw.conf", env::var("HOME").unwrap_or_default()),
"lw.conf",
];
let config: String = config_paths
.iter()
.filter(|file| Path::new(file).exists())
.take(1)
.cloned()
.collect();
if config.is_empty() {
let first_conf = config_paths[0].to_string();
let new_conf = Config::default();
write_append(
&first_conf,
&to_string_pretty(&new_conf, PrettyConfig::new().new_line("\n".to_string()))
.unwrap_or_default(),
);
first_conf
} else {
config
}
}
pub fn get_log_level(&self) -> LevelFilter {
let level = self.log_level.clone().unwrap_or_default();
match &level[..] {
"OFF" => LevelFilter::Off,
"ERROR" => LevelFilter::Error,
"WARN" => LevelFilter::Warn,
"INFO" => LevelFilter::Info,
"DEBUG" => LevelFilter::Debug,
"TRACE" => LevelFilter::Trace,
_ => LevelFilter::Info,
}
}
}