diff --git a/Cargo.lock b/Cargo.lock index 2b26e50..99e45b9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -67,6 +67,19 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "libc", + "num-integer", + "num-traits", + "time", + "winapi", +] + [[package]] name = "clap" version = "3.0.5" @@ -402,6 +415,15 @@ version = "0.2.112" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125" +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if", +] + [[package]] name = "matrixmultiply" version = "0.1.15" @@ -589,9 +611,11 @@ dependencies = [ "image", "imageproc", "lazy_static", + "log", "rusttype", "serde", "serde_json", + "simplelog", ] [[package]] @@ -811,6 +835,17 @@ dependencies = [ "serde", ] +[[package]] +name = "simplelog" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1348164456f72ca0116e4538bdaabb0ddb622c7d9f16387c725af3e96d6001c" +dependencies = [ + "chrono", + "log", + "termcolor", +] + [[package]] name = "strsim" version = "0.10.0" @@ -854,6 +889,16 @@ dependencies = [ "weezl", ] +[[package]] +name = "time" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "ttf-parser" version = "0.6.2" diff --git a/Cargo.toml b/Cargo.toml index 5ef5469..18daacb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,8 @@ edition = "2021" [dependencies] clap = { version = "3.0", features = ["derive"] } +log = "0.4" +simplelog = "0.11" fltk = "1.2" fltk-theme = "0.4" image = "0.23" diff --git a/src/config.rs b/src/config.rs index 8402b35..fcafed2 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,24 +1,33 @@ -//! load, save configuration and parse cli args - +///! 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, path::PathBuf}; +use std::{collections::HashMap, fs::File, path::PathBuf}; lazy_static! { - pub static ref CONFIG_PATH: PathBuf = { - match dirs::config_dir() { - Some(path) => path.join("post_maker.config"), + static ref CONFIG_DIR: PathBuf = { + let dir = match dirs::config_dir() { + Some(path) => path, None => std::env::current_exe() .unwrap() .parent() .unwrap() - .join("post_maker.config"), + .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 }; + static ref CONFIG_FILE: PathBuf = CONFIG_DIR.join("post_maker.config"); + static ref LOG_PATH: PathBuf = CONFIG_DIR.join("post_maker.log"); } /// Simple program calculate size of stuff in quote image @@ -31,7 +40,7 @@ pub(crate) struct Args { } #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, ArgEnum)] -pub enum Themes { +pub(crate) enum Themes { Classic, /// Windows 7 Aero, @@ -77,24 +86,24 @@ impl Into for Themes { /// Configuation file #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ConfigFile { - pub quote_font_ttf: String, - pub subquote_font_ttf: String, - pub subquote2_font_ttf: String, - pub tag_font_ttf: String, - pub tag2_font_ttf: String, - pub quote_font_ratio: f64, - pub subquote_font_ratio: f64, - pub subquote2_font_ratio: f64, - pub tag_font_ratio: f64, - pub tag2_font_ratio: f64, - pub quote_position_ratio: f64, - pub subquote_position_ratio: f64, - pub subquote2_position_ratio: f64, - pub tag_position_ratio: f64, - pub tag2_position_ratio: f64, - pub image_ratio: (f64, f64), - pub color_layer: [u8; 4], +pub(crate) struct ConfigFile { + pub(crate) quote_font_ttf: String, + pub(crate) subquote_font_ttf: String, + pub(crate) subquote2_font_ttf: String, + pub(crate) tag_font_ttf: String, + pub(crate) tag2_font_ttf: 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 { @@ -112,9 +121,9 @@ impl Default for ConfigFile { tag2_font_ratio: 150.0, quote_position_ratio: 0.7, subquote_position_ratio: 0.8, - subquote2_position_ratio: 0.8, + subquote2_position_ratio: 0.9, tag_position_ratio: 0.5, - tag2_position_ratio: 0.5, + tag2_position_ratio: 0.95, image_ratio: (4.0, 5.0), color_layer: [20, 22, 25, 197], } @@ -123,8 +132,7 @@ impl Default for ConfigFile { impl ConfigFile { pub(crate) fn load() -> Self { - // config_picker::ConfigPicker::new(); - if CONFIG_PATH.exists() { + if CONFIG_FILE.exists() { let map = get_configs(); let map = match map { @@ -160,16 +168,17 @@ impl ConfigFile { } pub(crate) fn get_configs() -> Option> { - match std::fs::read_to_string(&*CONFIG_PATH) { + match std::fs::read_to_string(&*CONFIG_FILE) { Ok(r) => serde_json::from_str::>(&r).ok(), Err(_) => None, } } pub(crate) fn save_configs(configs: HashMap) { - if let Err(_) = std::fs::write(&*CONFIG_PATH, serde_json::to_string(&configs).unwrap()) { + if let Err(e) = std::fs::write(&*CONFIG_FILE, serde_json::to_string(&configs).unwrap()) { dialog::alert_default("Can't write config!"); - eprintln!("Can't write config!"); + error!("Can't write config!\n{:?}", e); + panic!("Can't write config!\n{:?}", e); } } @@ -177,3 +186,16 @@ pub(crate) fn config() -> Args { let args = Args::parse(); args } + +pub(crate) fn log_file() -> File { + match File::open(&*LOG_PATH) { + Ok(f) => f, + Err(_) => match File::create(&*LOG_PATH) { + Ok(f) => f, + Err(e) => { + dialog::alert_default("Can't open log file!"); + panic!("{:?}", e) + } + }, + } +} diff --git a/src/crop_window.rs b/src/crop_window.rs index d289650..2697110 100644 --- a/src/crop_window.rs +++ b/src/crop_window.rs @@ -16,7 +16,7 @@ use std::{ /// Window to crop the existing image pub(crate) struct CropWindow { - pub win: Window, + pub(crate) win: Window, apply_btn: Button, container: Rc>>, page: Page, diff --git a/src/draw_thread.rs b/src/draw_thread.rs index ac9bded..acc08a6 100644 --- a/src/draw_thread.rs +++ b/src/draw_thread.rs @@ -8,7 +8,7 @@ use crate::{ use fltk::{ app, button::Button, - enums, + dialog, enums, frame::Frame, input::{Input, MultilineInput}, menu, @@ -250,63 +250,70 @@ fn load_image( let mut use_defaults = true; if conf.exists() { let mut prop = properties.write().unwrap(); - let read = fs::read_to_string(&conf).unwrap(); - if let Ok(saved_prop) = serde_json::from_str::(&read) { - utils::set_color_btn_rgba(saved_prop.rgba, layer_rgb); - layer_alpha.set_value(saved_prop.rgba[3] as f64); - quote.set_value(&saved_prop.quote); - subquote.set_value(&saved_prop.subquote); - subquote2.set_value(&saved_prop.subquote2); - tag.set_value(&saved_prop.tag); - tag2.set_value(&saved_prop.tag2); - quote_position.set_range(0.0, prop.original_dimension.1); - quote_position.set_value(saved_prop.quote_position); - subquote_position.set_range(0.0, prop.original_dimension.1); - subquote_position.set_value(saved_prop.subquote_position); - subquote2_position.set_range(0.0, prop.original_dimension.1); - subquote2_position.set_value(saved_prop.subquote2_position); - tag_position.set_range(0.0, prop.original_dimension.1); - tag_position.set_value(saved_prop.tag_position); - tag2_position.set_range(0.0, prop.original_dimension.1); - tag2_position.set_value(saved_prop.tag2_position); - quote_position_slider.set_range(0.0, prop.original_dimension.1); - subquote_position_slider.set_range(0.0, prop.original_dimension.1); - subquote2_position_slider.set_range(0.0, prop.original_dimension.1); - quote_position_slider.set_value(saved_prop.quote_position); - subquote_position_slider.set_value(saved_prop.subquote_position); - subquote2_position_slider.set_value(saved_prop.subquote2_position); - tag_position_slider.set_range(0.0, prop.original_dimension.1); - tag_position_slider.set_value(saved_prop.tag_position); - tag2_position_slider.set_range(0.0, prop.original_dimension.1); - tag2_position_slider.set_value(saved_prop.tag2_position); - dimension.set_label(&format!( - "[{}x{}]", - prop.original_dimension.0, prop.original_dimension.1 - )); + match fs::read_to_string(&conf) { + Ok(read) => { + if let Ok(saved_prop) = serde_json::from_str::(&read) { + utils::set_color_btn_rgba(saved_prop.rgba, layer_rgb); + layer_alpha.set_value(saved_prop.rgba[3] as f64); + quote.set_value(&saved_prop.quote); + subquote.set_value(&saved_prop.subquote); + subquote2.set_value(&saved_prop.subquote2); + tag.set_value(&saved_prop.tag); + tag2.set_value(&saved_prop.tag2); + quote_position.set_range(0.0, prop.original_dimension.1); + quote_position.set_value(saved_prop.quote_position); + subquote_position.set_range(0.0, prop.original_dimension.1); + subquote_position.set_value(saved_prop.subquote_position); + subquote2_position.set_range(0.0, prop.original_dimension.1); + subquote2_position.set_value(saved_prop.subquote2_position); + tag_position.set_range(0.0, prop.original_dimension.1); + tag_position.set_value(saved_prop.tag_position); + tag2_position.set_range(0.0, prop.original_dimension.1); + tag2_position.set_value(saved_prop.tag2_position); + quote_position_slider.set_range(0.0, prop.original_dimension.1); + subquote_position_slider.set_range(0.0, prop.original_dimension.1); + subquote2_position_slider.set_range(0.0, prop.original_dimension.1); + quote_position_slider.set_value(saved_prop.quote_position); + subquote_position_slider.set_value(saved_prop.subquote_position); + subquote2_position_slider.set_value(saved_prop.subquote2_position); + tag_position_slider.set_range(0.0, prop.original_dimension.1); + tag_position_slider.set_value(saved_prop.tag_position); + tag2_position_slider.set_range(0.0, prop.original_dimension.1); + tag2_position_slider.set_value(saved_prop.tag2_position); + dimension.set_label(&format!( + "[{}x{}]", + prop.original_dimension.0, prop.original_dimension.1 + )); - prop.quote = saved_prop.quote; - prop.subquote = saved_prop.subquote; - prop.subquote2 = saved_prop.subquote2; - prop.tag = saved_prop.tag; - prop.tag2 = saved_prop.tag2; - prop.quote_position = saved_prop.quote_position; - prop.subquote_position = saved_prop.subquote_position; - prop.subquote2_position = saved_prop.subquote2_position; - prop.tag_position = saved_prop.tag_position; - prop.tag2_position = saved_prop.tag2_position; - prop.rgba = saved_prop.rgba; - prop.is_saved = true; - use_defaults = false; - drop(prop); + prop.quote = saved_prop.quote; + prop.subquote = saved_prop.subquote; + prop.subquote2 = saved_prop.subquote2; + prop.tag = saved_prop.tag; + prop.tag2 = saved_prop.tag2; + prop.quote_position = saved_prop.quote_position; + prop.subquote_position = saved_prop.subquote_position; + prop.subquote2_position = saved_prop.subquote2_position; + prop.tag_position = saved_prop.tag_position; + prop.tag2_position = saved_prop.tag2_position; + prop.rgba = saved_prop.rgba; + prop.is_saved = true; + use_defaults = false; + drop(prop); - match crop { - Some((x, y)) => cont.apply_crop_pos(x, y), - None => match saved_prop.crop_position { - Some((x, y)) => cont.apply_crop_pos(x, y), - None => cont.apply_crop(), - }, + match crop { + Some((x, y)) => cont.apply_crop_pos(x, y), + None => match saved_prop.crop_position { + Some((x, y)) => cont.apply_crop_pos(x, y), + None => cont.apply_crop(), + }, + } + } } - } + Err(e) => { + dialog::alert_default("Failed to open config file!"); + warn!("Failed to open config file!\n{:?}", e); + } + }; } if use_defaults { diff --git a/src/globals.rs b/src/globals.rs index e17098d..df43a49 100644 --- a/src/globals.rs +++ b/src/globals.rs @@ -4,10 +4,12 @@ use rusttype::Font; use std::{ffi::OsString, io::Read, sync::RwLock}; lazy_static! { - pub static ref THEME: config::Themes = config::config().theme.unwrap_or(config::Themes::System); - pub static ref CONFIG_NAME: RwLock = RwLock::new("default".to_owned()); - pub static ref CONFIG: RwLock = RwLock::new(config::ConfigFile::load()); - pub static ref FONT_QUOTE: Font<'static> = { + pub(crate) static ref THEME: config::Themes = + config::config().theme.unwrap_or(config::Themes::System); + pub(crate) static ref CONFIG_NAME: RwLock = RwLock::new("default".to_owned()); + pub(crate) static ref CONFIG: RwLock = + RwLock::new(config::ConfigFile::load()); + pub(crate) static ref FONT_QUOTE: Font<'static> = { let mut buffer = Vec::new(); if let Ok(mut file) = std::fs::File::open(CONFIG.read().unwrap().quote_font_ttf.as_str()) { if let Ok(_) = file.read_to_end(&mut buffer) { @@ -19,7 +21,7 @@ lazy_static! { rusttype::Font::try_from_vec(include_bytes!("../ReenieBeanie-Regular.ttf").to_vec()) .unwrap() }; - pub static ref FONT_SUBQUOTE: Font<'static> = { + pub(crate) static ref FONT_SUBQUOTE: Font<'static> = { let mut buffer = Vec::new(); if let Ok(mut file) = std::fs::File::open(CONFIG.read().unwrap().subquote_font_ttf.as_str()) { @@ -31,7 +33,7 @@ lazy_static! { } rusttype::Font::try_from_vec(include_bytes!("../Rajdhani-Regular.ttf").to_vec()).unwrap() }; - pub static ref FONT_SUBQUOTE2: Font<'static> = { + pub(crate) static ref FONT_SUBQUOTE2: Font<'static> = { let mut buffer = Vec::new(); if let Ok(mut file) = std::fs::File::open(CONFIG.read().unwrap().subquote2_font_ttf.as_str()) @@ -44,7 +46,7 @@ lazy_static! { } rusttype::Font::try_from_vec(include_bytes!("../Rajdhani-Regular.ttf").to_vec()).unwrap() }; - pub static ref FONT_TAG: Font<'static> = { + pub(crate) static ref FONT_TAG: Font<'static> = { let mut buffer = Vec::new(); if let Ok(mut file) = std::fs::File::open(&CONFIG.read().unwrap().tag_font_ttf.as_str()) { if let Ok(_) = file.read_to_end(&mut buffer) { @@ -55,7 +57,7 @@ lazy_static! { } rusttype::Font::try_from_vec(include_bytes!("../Kalam-Regular.ttf").to_vec()).unwrap() }; - pub static ref FONT_TAG2: Font<'static> = { + pub(crate) static ref FONT_TAG2: Font<'static> = { let mut buffer = Vec::new(); if let Ok(mut file) = std::fs::File::open(&CONFIG.read().unwrap().tag2_font_ttf.as_str()) { if let Ok(_) = file.read_to_end(&mut buffer) { @@ -66,8 +68,8 @@ lazy_static! { } rusttype::Font::try_from_vec(include_bytes!("../Kalam-Regular.ttf").to_vec()).unwrap() }; - pub static ref ICON: OsString = include_str!("../icon.svg").into(); - pub static ref RELOAD_ICON: OsString = { + pub(crate) static ref ICON: OsString = include_str!("../icon.svg").into(); + pub(crate) static ref RELOAD_ICON: OsString = { let img = include_str!("../reload.svg"); if *THEME == config::Themes::Dark || *THEME == config::Themes::HighContrast { return img.replace("fill=\"black\"", "fill=\"white\"").into(); diff --git a/src/main.rs b/src/main.rs index a66e927..e7335ab 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,9 @@ #![windows_subsystem = "windows"] +#[macro_use] +extern crate log; +extern crate simplelog; + mod config; mod config_picker; mod config_window; @@ -9,12 +13,13 @@ mod globals; mod main_window; mod utils; -// use crop_window::CropWindow; use fltk::{ app::{channel, App}, + dialog, prelude::*, }; use fltk_theme::WidgetTheme; +use simplelog::*; use main_window::MainWindow; use std::sync::{Arc, RwLock}; @@ -27,6 +32,16 @@ pub(crate) enum AppMessage { fn main() { let app = App::default(); WidgetTheme::new(globals::THEME.clone().into()).apply(); + + if let Err(e) = CombinedLogger::init(vec![WriteLogger::new( + LevelFilter::Info, + Config::default(), + config::log_file(), + )]) { + dialog::alert_default("Failed to start logger"); + panic!("Failed to start logger\n{:?}", e); + } + lazy_static::initialize(&globals::CONFIG); let draw_buff: Arc>>> = Arc::new(RwLock::new(None)); diff --git a/src/main_window.rs b/src/main_window.rs index 833f548..ea8b453 100644 --- a/src/main_window.rs +++ b/src/main_window.rs @@ -396,8 +396,9 @@ impl MainWindow { } let expost_dir = path.join("export"); if !expost_dir.exists() { - if let Err(_) = fs::create_dir(expost_dir) { - fltk::dialog::alert_default("Failed: create export folder!"); + if let Err(e) = fs::create_dir(expost_dir) { + fltk::dialog::alert_default("Failed to create export folder!"); + warn!("Failed to create export folder!\n{:?}", e); return; } } diff --git a/src/utils.rs b/src/utils.rs index 10c9353..1680556 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -59,9 +59,10 @@ impl ImageContainer { pub(crate) fn new(path: &PathBuf, properties: Arc>) -> Self { let img = match image::open(path) { Ok(i) => i, - Err(_) => { + Err(e) => { dialog::alert_default("Failed to open image!"); - panic!("Failed to open image"); + error!("Failed to open image\n{:?}", e); + panic!("Failed to open image\n{:?}", e); } }; @@ -181,8 +182,9 @@ impl ImageContainer { let mut prop = prop.clone(); prop.path = None; - if fs::write(&path_conf, serde_json::to_string(&prop).unwrap()).is_err() { + if let Err(e) = fs::write(&path_conf, serde_json::to_string(&prop).unwrap()) { dialog::alert_default("Failed to save conf!"); + warn!("Failed to save conf!\n{:?}", e); } let mut img = image::open(&path_original).unwrap(); @@ -212,11 +214,9 @@ impl ImageContainer { prop.original_dimension.1, ); - if img - .save_with_format(&export, image::ImageFormat::Png) - .is_err() - { + if let Err(e) = img.save_with_format(&export, image::ImageFormat::Png) { dialog::alert_default("Failed to export png!"); + warn!("Failed to export png!\n{:?}", e); } } @@ -238,14 +238,19 @@ impl ImageContainer { let path_conf = path.with_extension("conf"); let path_conf_new = new_path.with_extension("conf"); - if path.exists() && fs::copy(path, &new_path).is_err() { - dialog::alert_default("Failed to clone image!"); - return None; + if path.exists() { + if let Err(e) = fs::copy(path, &new_path) { + dialog::alert_default("Failed to clone image!"); + warn!("Failed to clone image!\n{:?}", e); + return None; + } } - if path_conf.exists() && fs::copy(path_conf, &path_conf_new).is_err() { - dialog::alert_default("Failed to clone image!"); - return None; + if path_conf.exists() { + if let Err(e) = fs::copy(path_conf, &path_conf_new) { + dialog::alert_default("Failed to clone image!"); + warn!("Failed to clone image!\n{:?}", e); + } } Some(new_path) } @@ -270,16 +275,25 @@ impl ImageContainer { .unwrap(), ); - if path_original.exists() && fs::remove_file(path_original).is_err() { - dialog::alert_default("Failed to delete image!"); + if path_original.exists() { + if let Err(e) = fs::remove_file(path_original) { + dialog::alert_default("Failed to delete image!"); + warn!("Failed to delete image!\n{:?}", e); + } } - if path_conf.exists() && fs::remove_file(path_conf).is_err() { - dialog::alert_default("Failed to delete image conf!"); + if path_conf.exists() { + if let Err(e) = fs::remove_file(path_conf) { + dialog::alert_default("Failed to delete image conf!"); + warn!("Failed to delete image conf!\n{:?}", e); + } } - if export.exists() && fs::remove_file(export).is_err() { - dialog::alert_default("Failed to delete exported image!"); + if export.exists() { + if let Err(e) = fs::remove_file(export) { + dialog::alert_default("Failed to delete exported image!"); + warn!("Failed to delete exported image!\n{:?}", e); + } } } }