use crate::MyResult;
use serde::{Deserialize, Serialize};
use std::{
env,
fs::{self, File},
io::{BufReader, BufWriter, Write},
path::{Path, PathBuf},
process::Command,
};
#[derive(Debug, Serialize, Deserialize)]
pub struct Config {
pub interval: u64,
pub min_dimension: u32,
pub wallpaper: String,
pub desktop: String,
pub dirs: Vec<String>,
}
impl Default for Config {
fn default() -> Self {
let interval: u64 = 30 * 60; let min_dimension: u32 = 800; let home = get_home();
let pictures = format!("{home}/Pictures");
let imagens = format!("{home}/Imagens");
let wallpaper = format!("{home}/wallswitch.jpg");
let dirs: Vec<String> = [
&pictures,
&imagens,
"/usr/share/wallpapers",
"/usr/share/backgrounds",
"/usr/share/antergos/wallpapers",
"/tmp/teste",
]
.iter()
.map(ToString::to_string)
.collect();
Config {
interval,
min_dimension,
wallpaper,
desktop: get_desktop(),
dirs,
}
}
}
impl Config {
pub fn new() -> MyResult<Self> {
let config_path: PathBuf = get_config_path()?;
let mut config: Config = match read_config_file(&config_path) {
Ok(configuration) => configuration,
Err(_) => Self::default(),
};
config.desktop = get_desktop(); config.write_config_file(&config_path)?;
Ok(config)
}
pub fn write_config_file(&self, path: &PathBuf) -> MyResult<()> {
if let Some(parent) = path.parent() {
fs::create_dir_all(parent)?
};
let file: File = fs::OpenOptions::new()
.read(true)
.write(true)
.create(true)
.truncate(false)
.open(path)
.map_err(|error| {
eprintln!("Failed to create file {path:?}");
eprintln!("Perhaps lack of permission!");
error
})?;
let mut writer = BufWriter::new(file);
serde_json::to_writer_pretty(&mut writer, &self)?;
writer.flush()?;
Ok(())
}
}
fn get_config_path() -> MyResult<PathBuf> {
let home = env::var("HOME").map_err(|error| {
eprintln!("env HOME not found!");
eprintln!("echo $HOME");
error
})?;
let hidden_dir = ".config".to_string();
let pkg_name = "wallswitch".to_string();
let config_file = format!("{pkg_name}.json");
let config_path: PathBuf = [home, hidden_dir, pkg_name, config_file].iter().collect();
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)
}
pub fn get_home() -> String {
match env::var("HOME") {
Ok(home) => home,
Err(why) => {
eprintln!("echo $HOME");
panic!("Error: Unable to get home path! {why}");
}
}
}
pub fn get_desktop() -> String {
match env::var("DESKTOP_SESSION") {
Ok(desktop) => desktop,
Err(why) => {
eprintln!("echo $DESKTOP_SESSION");
panic!("Error: Unable to get desktop type! {why}");
}
}
}
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
pub struct FileInfo {
pub resolution: Resolution,
pub valid: bool,
pub size: u64,
pub path: PathBuf,
}
impl FileInfo {
pub fn path_contains(&self, string: &str) -> bool {
match self.path.to_str() {
Some(p) => p.contains(string),
None => false,
}
}
pub fn is_valid(&self, config: &Config) -> bool {
let width = self.resolution.width;
let height = self.resolution.height;
let min = width.min(height);
min > config.min_dimension
}
pub fn update_info(&mut self, config: &Config) -> MyResult<()> {
let identify = Command::new("identify")
.arg("-format")
.arg("%wx%h") .arg(&self.path)
.output()?;
let sdt_output = String::from_utf8(identify.stdout)?;
let resolution = Resolution::new(&sdt_output);
self.resolution = resolution;
self.valid = self.is_valid(config);
println!("file_info: {self:?}");
Ok(())
}
}
pub trait SortFiles {
fn unique(&mut self);
}
impl SortFiles for Vec<FileInfo> {
fn unique(&mut self) {
self.sort();
self.dedup();
}
}
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
pub struct Resolution {
pub width: u32,
pub height: u32,
}
impl Resolution {
pub fn new(string: &str) -> Resolution {
let (width, height) = split_str(string);
Resolution { width, height }
}
}
fn split_str(string: &str) -> (u32, u32) {
let numbers: Vec<u32> = string
.trim()
.split('x')
.flat_map(|number| number.parse::<u32>())
.collect();
if numbers.len() != 2 {
eprintln!("fn split_str()");
panic!("Error: split '{string}' for Vec<u32>");
}
let width = numbers[0];
let height = numbers[1];
(width, height)
}
pub trait PrintSlice {
fn print_slice(&self, spaces: &str);
}
impl<T> PrintSlice for [T]
where
T: std::fmt::Display,
{
fn print_slice(&self, spaces: &str) {
for dir in self {
println!("{spaces}'{dir}'");
}
}
}