use serde::{Deserialize, Serialize};
use std::{
fs::{self, File},
io::{BufReader, BufWriter, Write},
path::{Path, PathBuf},
};
use crate::{
Arguments, MyResult,
WSError::{self, *},
ENVIRON,
};
#[derive(Debug, Serialize, Deserialize)]
pub struct Config {
pub desktop: String,
pub min_dimension: u64,
pub max_dimension: u64,
pub min_size: u64,
pub max_size: u64,
pub dirs: Vec<PathBuf>,
pub extensions: Vec<String>,
pub interval: u64,
pub monitor: u8,
pub sort: bool,
pub verbose: bool,
pub wallpaper: PathBuf,
}
impl Default for Config {
fn default() -> Self {
let extensions: Vec<String> = ["avif", "jpg", "jpeg", "png", "svg", "tif", "webp"]
.iter()
.map(ToString::to_string)
.collect();
let interval: u64 = 30 * 60;
let min_dimension: u64 = 600;
let max_dimension: u64 = 128_000;
Config {
desktop: ENVIRON.desktop.to_string(),
min_dimension,
max_dimension,
min_size: u64::pow(1024, 1), max_size: u64::pow(1024, 3), dirs: get_dirs(),
extensions,
interval,
monitor: 2,
sort: false,
verbose: false,
wallpaper: get_wallpaper_path(),
}
}
}
fn config_boundary() -> Config {
Config {
interval: 5,
min_dimension: 10,
min_size: 1,
monitor: 1,
..Config::default()
}
}
impl Config {
pub fn new() -> MyResult<Self> {
let mut read_default_config = false;
let config_path: PathBuf = get_config_path()?;
let args = Arguments::build()?;
let config: Config = match read_config_file(&config_path) {
Ok(configuration) => configuration,
Err(_) => {
read_default_config = true;
Self::default()
}
}
.set_command_line_arguments(&args)?
.validate_config()
.map_err(|error| {
eprintln!("{error}");
error
})?
.write_config_file(&config_path, read_default_config)?;
Ok(config)
}
pub fn in_range(&self, value: u64) -> bool {
self.min_dimension <= value && value <= self.max_dimension
}
pub fn print(&self) -> MyResult<()> {
let json: String = serde_json::to_string_pretty(self)?;
println!("Config:\n{json}\n");
Ok(())
}
fn set_command_line_arguments(mut self, args: &Arguments) -> MyResult<Self> {
if let Some(min_dimension) = args.min_dimension {
self.min_dimension = min_dimension;
}
if let Some(max_dimension) = args.max_dimension {
self.max_dimension = max_dimension;
}
if let Some(min_size) = args.min_size {
self.min_size = min_size;
}
if let Some(max_size) = args.max_size {
self.max_size = max_size;
}
if let Some(interval) = args.interval {
self.interval = interval;
}
if let Some(monitor) = args.monitor {
self.monitor = monitor;
}
if args.sort {
self.sort = !self.sort;
}
if args.verbose {
self.verbose = !self.verbose;
}
self.desktop = ENVIRON.desktop.to_string(); Ok(self)
}
pub fn validate_config(self) -> Result<Self, WSError<'static>> {
let boundary: Config = config_boundary();
if self.interval < boundary.interval {
let value = self.interval.to_string();
return Err(AtLeastValue("--interval", value, boundary.interval));
}
if self.min_dimension < boundary.min_dimension {
let value = self.min_dimension.to_string();
return Err(AtLeastValue(
"--min_dimension",
value,
boundary.min_dimension,
));
}
if self.min_size < boundary.min_size {
let value = self.min_size.to_string();
return Err(AtLeastValue("--min_size", value, boundary.min_size));
}
if self.monitor < boundary.monitor {
let value = self.monitor.to_string();
return Err(AtLeastValue("--interval", value, boundary.monitor.into()));
}
if let Some(parent) = self.wallpaper.parent() {
if !parent.exists() {
let dir: PathBuf = parent.to_path_buf();
return Err(Parent(dir));
}
}
if self.min_dimension > self.max_dimension || self.min_size > self.max_size {
return Err(WSError::MinMaxValue);
}
Ok(self)
}
pub fn write_config_file(self, path: &PathBuf, read_default_config: bool) -> MyResult<Self> {
if read_default_config {
eprintln!("Create the configuration file: {path:?}\n");
}
if let Some(parent) = path.parent() {
fs::create_dir_all(parent)?
};
let file: File = fs::OpenOptions::new()
.read(true)
.write(true)
.create(true)
.truncate(true)
.open(path)
.map_err(|io_error| {
WSError::IOError(path.to_path_buf(), io_error.into())
})?;
let mut writer = BufWriter::new(file);
serde_json::to_writer_pretty(&mut writer, &self)?;
writer.flush()?;
Ok(self)
}
}
pub fn get_wallpaper_path() -> PathBuf {
let home = ENVIRON.get_home();
let pkg_name = ENVIRON.get_pkg_name();
let mut wallpaper_path: PathBuf = [home, pkg_name].iter().collect();
wallpaper_path.set_extension("jpg");
wallpaper_path
}
pub fn get_dirs() -> Vec<PathBuf> {
let home = ENVIRON.get_home(); let images = ["Figures", "Images", "Pictures", "Wallpapers", "Imagens"];
let dirs_home: Vec<PathBuf> = images
.into_iter()
.map(|image| Path::new(home).join(image)) .collect();
let sep: &str = std::path::MAIN_SEPARATOR_STR;
let path1: PathBuf = [sep, "usr", "share", "wallpapers"].iter().collect();
let path2: PathBuf = [sep, "usr", "share", "backgrounds"].iter().collect();
let path3: PathBuf = [sep, "tmp", "teste"].iter().collect();
let dirs_others: Vec<PathBuf> = vec![path1, path2, path3];
dirs_home.into_iter().chain(dirs_others).collect()
}
pub fn get_config_path() -> MyResult<PathBuf> {
let home = ENVIRON.get_home();
let hidden_dir = ".config";
let pkg_name = ENVIRON.get_pkg_name();
let mut config_path: PathBuf = [home, hidden_dir, pkg_name, pkg_name].iter().collect();
config_path.set_extension("json");
Ok(config_path)
}
pub fn read_config_file<P>(path: P) -> MyResult<Config>
where
P: AsRef<Path>,
{
let file = File::open(path)?;
let reader = BufReader::new(file);
let config: Config = serde_json::from_reader(reader)?;
Ok(config)
}