/* This file is part of Post Maker. Post Maker is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Post Maker is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Post Maker. If not, see */ //! load, save configuration and parse cli args use crate::{config_picker::ConfigPicker, globals}; use clap::{ArgEnum, Parser}; use fltk::dialog; use fltk_theme::ThemeType; use lazy_static::lazy_static; use serde::{Deserialize, Serialize}; use std::{ collections::HashMap, fs::File, path::PathBuf, time::{Duration, SystemTime}, }; lazy_static! { /// Directory where all Configurations are present static ref CONFIG_DIR: PathBuf = { let dir = match dirs::config_dir() { Some(path) => path, None => std::env::current_exe() .unwrap() .parent() .unwrap() .to_owned(), } .join("post_maker"); if !dir.exists() { if let Err(e) = std::fs::create_dir(&dir) { dialog::alert_default("Failed to create config dir!"); panic!("Failed to create config dir!\n{:?}", e); } } dir }; /// Configuration File static ref CONFIG_FILE: PathBuf = CONFIG_DIR.join("post_maker.config"); /// Log File static ref LOG_FILE: PathBuf = CONFIG_DIR.join("post_maker.log"); } /// Simple program calculate size of stuff in quote image #[derive(Parser, Debug)] #[clap(about, version, author)] pub(crate) struct Args { /// Theme to use for gui #[clap(short, long, arg_enum)] pub(crate) theme: Option, } #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, ArgEnum)] pub(crate) enum Themes { Classic, /// Windows 7 Aero, /// Windows 8 Metro, /// Classic MacOS AquaClassic, /// Xfce Greybird, /// Windows 2000 Blue, /// Dark Dark, /// High Contrast HighContrast, /// Get from System System, } impl Into for Themes { fn into(self) -> ThemeType { match self { Self::Classic => ThemeType::Classic, Self::Aero => ThemeType::Aero, Self::Metro => ThemeType::Metro, Self::AquaClassic => ThemeType::AquaClassic, Self::Greybird => ThemeType::Greybird, Self::Blue => ThemeType::Blue, Self::Dark => ThemeType::Dark, Self::HighContrast => ThemeType::HighContrast, Self::System => { if cfg!(windows) { ThemeType::Metro } else if cfg!(unix) { ThemeType::Greybird } else { ThemeType::Classic } } } } } pub(crate) fn args() -> Args { let args = Args::parse(); args } /// Configuation file #[derive(Debug, Clone, Serialize, Deserialize)] pub(crate) struct ConfigFile { pub(crate) quote_font: String, pub(crate) subquote_font: String, pub(crate) subquote2_font: String, pub(crate) tag_font: String, pub(crate) tag2_font: String, pub(crate) quote_font_ratio: f64, pub(crate) subquote_font_ratio: f64, pub(crate) subquote2_font_ratio: f64, pub(crate) tag_font_ratio: f64, pub(crate) tag2_font_ratio: f64, pub(crate) quote_position_ratio: f64, pub(crate) subquote_position_ratio: f64, pub(crate) subquote2_position_ratio: f64, pub(crate) tag_position_ratio: f64, pub(crate) tag2_position_ratio: f64, pub(crate) image_ratio: (f64, f64), pub(crate) color_layer: [u8; 4], } impl Default for ConfigFile { fn default() -> Self { Self { quote_font: String::new(), subquote_font: String::new(), subquote2_font: String::new(), tag_font: String::new(), tag2_font: String::new(), quote_font_ratio: 230.0, subquote_font_ratio: 230.0, subquote2_font_ratio: 230.0, tag_font_ratio: 150.0, tag2_font_ratio: 150.0, quote_position_ratio: 0.7, subquote_position_ratio: 0.8, subquote2_position_ratio: 0.9, tag_position_ratio: 0.5, tag2_position_ratio: 0.95, image_ratio: (4.0, 5.0), color_layer: [20, 22, 25, 197], } } } impl ConfigFile { pub(crate) fn load() -> Self { if CONFIG_FILE.exists() { let map = get_configs(); let map = match map { Some(m) => m, None => HashMap::new(), }; let default_config = (&*globals::CONFIG_NAME.read().unwrap()).to_string(); let config_name = if (map.len() > 1 || !map.contains_key(&default_config)) && map.len() != 0 { let picked = ConfigPicker::new(map.keys().map(|a| a.to_owned()).collect()).selected; let picked = picked.borrow(); match &*picked { Some(v) => v.to_owned(), None => std::process::exit(0), } } else { default_config }; if let Some(config) = map.get(&config_name) { *globals::CONFIG_NAME.write().unwrap() = config_name; return config.to_owned(); } } let config = Self::default(); let mut configs = HashMap::new(); configs.insert( (&*globals::CONFIG_NAME.read().unwrap()).to_owned(), config.clone(), ); save_configs(configs); config } } /// Get parsed configs from file pub(crate) fn get_configs() -> Option> { match std::fs::read_to_string(&*CONFIG_FILE) { Ok(r) => serde_json::from_str::>(&r).ok(), Err(_) => None, } } /// Save configs pub(crate) fn save_configs(configs: HashMap) { if let Err(e) = std::fs::write(&*CONFIG_FILE, serde_json::to_string(&configs).unwrap()) { dialog::alert_default("Can't write config!"); error!("Can't write config!\n{:?}", e); panic!("Can't write config!\n{:?}", e); } } pub(crate) fn log_file() -> File { match File::open(&*LOG_FILE) { Ok(mut file) => { if is_file_30_days_old(&file) { match File::create(&*LOG_FILE) { Ok(f) => file = f, Err(e) => { dialog::alert_default("Can't open log file!"); panic!("{:?}", e); } } } file } Err(_) => match File::create(&*LOG_FILE) { Ok(f) => f, Err(e) => { dialog::alert_default("Can't open log file!"); panic!("{:?}", e); } }, } } pub(crate) fn is_file_30_days_old(file: &File) -> bool { if let Ok(meta) = file.metadata() { if let Ok(time) = meta.created() { if let Ok(dur) = SystemTime::now().duration_since(time) { if dur > Duration::from_secs(60 * 60 * 24 * 30) { return true; } } } } false }