diff --git a/src/color/color.rs b/src/color/color.rs index 9c4db84..6be914b 100644 --- a/src/color/color.rs +++ b/src/color/color.rs @@ -16,14 +16,14 @@ use std::str::FromStr; use crate::{color::math::get_color_distance_lab, scheme::SchemeTypes}; -#[derive(clap::Parser, Debug)] +#[derive(clap::Parser, Debug, Clone)] pub enum ColorFormat { Hex { string: String }, Rgb { string: String }, Hsl { string: String }, } -#[derive(clap::Subcommand, Debug)] +#[derive(clap::Subcommand, Debug, Clone)] pub enum Source { /// The image to use for generating a color scheme Image { path: String }, @@ -37,13 +37,13 @@ pub enum Source { Color(crate::color::color::ColorFormat), } -#[derive(serde::Serialize, serde::Deserialize, Debug)] +#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] pub struct ColorDefinition { pub name: String, pub color: String, } -#[derive(serde::Serialize, serde::Deserialize, Debug)] +#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] #[serde(untagged)] pub enum OwnCustomColor { Color(String), diff --git a/src/helpers.rs b/src/helpers.rs index a5e97ed..60d9924 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -56,7 +56,7 @@ pub fn setup_logging(args: &Cli) -> Result<(), Report> { Ok(()) } -pub fn set_wallpaper(source: &Source, wallpaper_cfg: wallpaper::Wallpaper) -> Result<(), Report> { +pub fn set_wallpaper(source: &Source) -> Result<(), Report> { let path = match &source { Source::Image { path } => path, Source::Color { .. } => return Ok(()), diff --git a/src/main.rs b/src/main.rs index 9307376..24f9325 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,6 +3,8 @@ extern crate pretty_env_logger; #[macro_use] extern crate paris_log; +use std::path::PathBuf; + use material_colors::theme::ThemeBuilder; mod helpers; @@ -12,9 +14,11 @@ mod wallpaper; use helpers::{set_wallpaper, setup_logging}; use matugen::{ - color::color::get_source_color, + color::color::{get_source_color, Source}, scheme::{get_custom_color_schemes, get_schemes}, + template_util::template::get_render_data, }; +use template::{build_engine_syntax, TemplateFile}; use crate::template::Template; use crate::util::{arguments::Cli, color::show_color, config::ConfigFile}; @@ -24,66 +28,136 @@ use color_eyre::{eyre::Result, Report}; use matugen::scheme::{Schemes, SchemesEnum}; -fn main() -> Result<(), Report> { - color_eyre::install()?; - let args = Cli::parse(); +use material_colors::{color::Argb, theme::Theme}; +use upon::Value; +pub struct State { + pub args: Cli, + pub config_file: ConfigFile, + pub config_path: Option, + pub source_color: Argb, + pub theme: Theme, + pub schemes: Schemes, + pub default_scheme: SchemesEnum, +} - setup_logging(&args)?; +impl State { + pub fn new(args: Cli) -> Self { + let (config_file, config_path) = ConfigFile::read(&args).unwrap(); + + let source_color = get_source_color(&args.source).unwrap(); + let theme = ThemeBuilder::with_source(source_color).build(); + let (scheme_dark, scheme_light) = get_schemes(source_color, &args.r#type, &args.contrast); + + let default_scheme = args + .mode + .expect("Something went wrong while parsing the mode"); + + let schemes = get_custom_color_schemes( + source_color, + scheme_dark, + scheme_light, + &config_file.config.custom_colors, + &args.r#type, + &args.contrast, + ); + + Self { + args, + config_file, + config_path, + source_color, + theme, + schemes, + default_scheme, + } + } + + fn init_in_term(&self) -> Result<(), Report> { + color_eyre::install()?; + setup_logging(&self.args)?; - let (config, config_path) = ConfigFile::read(&args)?; + #[cfg(feature = "update-informer")] + if self.config_file.config.version_check == Some(true) { + use crate::helpers::check_version; + check_version(); + } - #[cfg(feature = "update-informer")] - if config.config.version_check == Some(true) { - use crate::helpers::check_version; - check_version(); + Ok(()) } - - let source_color = get_source_color(&args.source).unwrap(); - - let theme = ThemeBuilder::with_source(source_color).build(); + pub fn run_in_term(&self) -> Result<(), Report> { + self.init_in_term()?; + + if self.args.show_colors == Some(true) { + show_color(&self.schemes, &self.source_color); + } + + #[cfg(feature = "dump-json")] + if let Some(ref format) = self.args.json { + use crate::util::color::dump_json; + dump_json( + &self.schemes, + &self.source_color, + format, + &self.theme.palettes, + ); + } - let (scheme_dark, scheme_light) = get_schemes(source_color, &args.r#type, &args.contrast); + if self.args.dry_run == Some(true) { + return Ok(()); + } - let default_scheme = args - .mode - .expect("Something went wrong while parsing the mode"); + let mut engine = self.init_engine(); + let mut render_data = self.init_render_data()?; + let mut template = TemplateFile::new(self, &mut engine, &mut render_data); + + template.generate()?; + + // Template::generate( + // &self.schemes, + // &self.config_file.templates, + // &self.args.source, + // &self.source_color, + // &self.default_scheme, + // &self.config_file.config.custom_keywords, + // &self.args.prefix, + // &self.config_path, + // )?; + + if let Some(_wallpaper_cfg) = &self.config_file.config.wallpaper { + set_wallpaper(&self.args.source)?; + } - let schemes = get_custom_color_schemes( - source_color, - scheme_dark, - scheme_light, - &config.config.custom_colors, - &args.r#type, - &args.contrast, - ); + Ok(()) + } - if args.show_colors == Some(true) { - show_color(&schemes, &source_color); + fn init_engine(&self) -> upon::Engine { + let syntax = build_engine_syntax(self); + upon::Engine::with_syntax(syntax) } - #[cfg(feature = "dump-json")] - if let Some(ref format) = args.json { - use crate::util::color::dump_json; - dump_json(&schemes, &source_color, format, theme.palettes); + fn init_render_data(&self) -> Result { + let image = match &self.args.source { + Source::Image { path } => Some(path), + #[cfg(feature = "web-image")] + Source::WebImage { .. } => None, + Source::Color { .. } => None, + }; + + get_render_data( + &self.schemes, + &self.source_color, + &self.default_scheme, + &self.config_file.config.custom_keywords, + image, + ) } +} - if args.dry_run == Some(false) { - Template::generate( - &schemes, - &config.templates, - &args.source, - &source_color, - &default_scheme, - &config.config.custom_keywords, - &args.prefix, - config_path, - )?; +fn main() -> Result<(), Report> { + let args = Cli::parse(); - if let Some(wallpaper_cfg) = config.config.wallpaper { - set_wallpaper(&args.source, wallpaper_cfg)?; - } - } + let prog = State::new(args.clone()); - Ok(()) + prog.run_in_term() } diff --git a/src/template.rs b/src/template.rs index f3ffaf4..8789277 100644 --- a/src/template.rs +++ b/src/template.rs @@ -3,7 +3,6 @@ use color_eyre::eyre::WrapErr; use color_eyre::Help; use color_eyre::{eyre::Result, Report}; -use material_colors::color::Argb; use matugen::template_util::template::add_engine_filters; use matugen::template_util::template::get_render_data; use matugen::template_util::template::render_template; @@ -16,7 +15,6 @@ use matugen::exec::hook::format_hook; use std::path::Path; use std::str; -use std::collections::HashMap; use std::fs::create_dir_all; use std::fs::read_to_string; use std::fs::OpenOptions; @@ -26,11 +24,12 @@ use std::path::PathBuf; use matugen::color::color::Source; use resolve_path::PathResolveExt; -use crate::{Schemes, SchemesEnum}; +use crate::SchemesEnum; +use crate::State; use upon::{Engine, Syntax}; -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct Template { pub input_path: PathBuf, pub output_path: PathBuf, @@ -39,85 +38,43 @@ pub struct Template { pub compare_to: Option, pub pre_hook: Option, pub post_hook: Option, - pub expr_prefix: Option, - pub expr_postfix: Option, - pub block_prefix: Option, - pub block_postfix: Option, } -#[allow(clippy::manual_strip)] -pub trait StripCanonicalization -where - Self: AsRef, -{ - #[cfg(not(target_os = "windows"))] - fn strip_canonicalization(&self) -> PathBuf { - self.as_ref().to_path_buf() - } +pub struct TemplateFile<'a> { + state: &'a State, + engine: &'a mut Engine<'a>, + render_data: &'a mut Value, +} - #[cfg(target_os = "windows")] - fn strip_canonicalization(&self) -> PathBuf { - const VERBATIM_PREFIX: &str = r#"\\?\"#; - let p = self.as_ref().display().to_string(); - if p.starts_with(VERBATIM_PREFIX) { - PathBuf::from(&p[VERBATIM_PREFIX.len()..]) - } else { - self.as_ref().to_path_buf() +impl TemplateFile<'_> { + pub fn new<'a>( + state: &'a State, + engine: &'a mut Engine<'a>, + render_data: &'a mut Value, + ) -> TemplateFile<'a> { + TemplateFile { + state, + engine, + render_data, } } -} - -impl StripCanonicalization for PathBuf {} -impl Template { - pub fn generate( - schemes: &Schemes, - templates: &HashMap, - source: &Source, - source_color: &Argb, - default_scheme: &SchemesEnum, - custom_keywords: &Option>, - path_prefix: &Option, - config_path: Option, - ) -> Result<(), Report> { - info!("Loaded {} templates.", &templates.len()); - - let image = match &source { - Source::Image { path } => Some(path), - #[cfg(feature = "web-image")] - Source::WebImage { .. } => None, - Source::Color { .. } => None, - }; - - let mut render_data = get_render_data( - schemes, - source_color, - default_scheme, - custom_keywords, - image, - )?; - - for (i, (name, template)) in templates.iter().enumerate() { - let expr_prefix = template.expr_prefix.as_deref().unwrap_or("{{"); - let expr_postfix = template.expr_postfix.as_deref().unwrap_or("}}"); - let block_prefix = template.block_prefix.as_deref().unwrap_or("<*"); - let block_postfix = template.block_postfix.as_deref().unwrap_or("*>"); - - let syntax = Syntax::builder() - .expr(expr_prefix, expr_postfix) - .block(block_prefix, block_postfix) - .build(); - let mut engine = Engine::with_syntax(syntax); + pub fn generate(&mut self) -> Result<(), Report> { + info!( + "Loaded {} templates.", + &self.state.config_file.templates.len() + ); - add_engine_filters(&mut engine); + for (i, (name, template)) in self.state.config_file.templates.iter().enumerate() { + add_engine_filters(self.engine); let (input_path_absolute, output_path_absolute) = - get_absolute_paths(&config_path, template)?; + get_absolute_paths(&self.state.config_path, template)?; if template.pre_hook.is_some() { format_hook( - &engine, - &mut render_data, + self.engine, + self.render_data, template.pre_hook.as_ref().unwrap(), &template.colors_to_compare, &template.compare_to, @@ -134,7 +91,7 @@ impl Template { .wrap_err(format!("Could not read the {} template.", name)) .suggestion("Try converting the file to use UTF-8 encoding.")?; - engine.add_template(name, data).map_err(|error| { + self.engine.add_template(name, data).map_err(|error| { let message = format!( "[{} - {}]\n{:#}", name, @@ -150,21 +107,18 @@ impl Template { output_path_absolute.display() ); - export_template( - &engine, + self.export_template( name, - &render_data, - path_prefix, + self.render_data, output_path_absolute, input_path_absolute, i, - templates, )?; if template.post_hook.is_some() { format_hook( - &engine, - &mut render_data, + self.engine, + self.render_data, template.post_hook.as_ref().unwrap(), &template.colors_to_compare, &template.compare_to, @@ -174,8 +128,85 @@ impl Template { } Ok(()) } + + fn export_template( + &self, + name: &String, + render_data: &Value, + output_path_absolute: PathBuf, + input_path_absolute: PathBuf, + i: usize, + ) -> Result<(), Report> { + let data = render_template(self.engine, name, render_data, input_path_absolute.to_str())?; + + let out = if self.state.args.prefix.is_some() && !cfg!(windows) { + let mut prefix_path = PathBuf::from(self.state.args.prefix.as_ref().unwrap()); + + // remove the root from the output_path so that we can push it onto the prefix + let output_path = output_path_absolute + .strip_prefix("/") + .expect("output_path_absolute is not an absolute path."); + + prefix_path.push(output_path); + + prefix_path + } else { + output_path_absolute.to_path_buf() + }; + + create_missing_folders(&out)?; + + debug!("out: {:?}", out); + let mut output_file = OpenOptions::new() + .create(true) + .truncate(true) + .write(true) + .open(out)?; + + if output_file.metadata()?.permissions().readonly() { + error!( + "The {} file is Read-Only", + &output_path_absolute.display() + ); + } + + output_file.write_all(data.as_bytes())?; + success!( + "[{}/{}] Exported the {} template to {}", + i + 1, + &self.state.config_file.templates.len(), + name, + output_path_absolute.display() + ); + + Ok(()) + } +} + +#[allow(clippy::manual_strip)] +pub trait StripCanonicalization +where + Self: AsRef, +{ + #[cfg(not(target_os = "windows"))] + fn strip_canonicalization(&self) -> PathBuf { + self.as_ref().to_path_buf() + } + + #[cfg(target_os = "windows")] + fn strip_canonicalization(&self) -> PathBuf { + const VERBATIM_PREFIX: &str = r#"\\?\"#; + let p = self.as_ref().display().to_string(); + if p.starts_with(VERBATIM_PREFIX) { + PathBuf::from(&p[VERBATIM_PREFIX.len()..]) + } else { + self.as_ref().to_path_buf() + } + } } +impl StripCanonicalization for PathBuf {} + fn create_missing_folders(output_path_absolute: &Path) -> Result<(), Report> { let parent_folder = &output_path_absolute .parent() @@ -221,57 +252,35 @@ fn get_absolute_paths( Ok((input_path_absolute, output_path_absolute)) } -fn export_template( - engine: &Engine, - name: &String, - render_data: &Value, - path_prefix: &Option, - output_path_absolute: PathBuf, - input_path_absolute: PathBuf, - i: usize, - templates: &HashMap, -) -> Result<(), Report> { - let data = render_template(engine, name, render_data, input_path_absolute.to_str())?; - - let out = if path_prefix.is_some() && !cfg!(windows) { - let mut prefix_path = PathBuf::from(path_prefix.as_ref().unwrap()); - - // remove the root from the output_path so that we can push it onto the prefix - let output_path = output_path_absolute - .strip_prefix("/") - .expect("output_path_absolute is not an absolute path."); - - prefix_path.push(output_path); - - prefix_path - } else { - output_path_absolute.to_path_buf() - }; - - create_missing_folders(&out)?; - - debug!("out: {:?}", out); - let mut output_file = OpenOptions::new() - .create(true) - .truncate(true) - .write(true) - .open(out)?; - - if output_file.metadata()?.permissions().readonly() { - error!( - "The {} file is Read-Only", - &output_path_absolute.display() - ); - } - - output_file.write_all(data.as_bytes())?; - success!( - "[{}/{}] Exported the {} template to {}", - i + 1, - &templates.len(), - name, - output_path_absolute.display() - ); - - Ok(()) +pub fn build_engine_syntax(state: &State) -> Syntax { + Syntax::builder() + .expr( + state + .config_file + .config + .expr_prefix + .as_deref() + .unwrap_or("{{"), + state + .config_file + .config + .expr_postfix + .as_deref() + .unwrap_or("}}"), + ) + .block( + state + .config_file + .config + .block_prefix + .as_deref() + .unwrap_or("<*"), + state + .config_file + .config + .block_postfix + .as_deref() + .unwrap_or("*>"), + ) + .build() } diff --git a/src/util/arguments.rs b/src/util/arguments.rs index 5a78d5a..1563e36 100644 --- a/src/util/arguments.rs +++ b/src/util/arguments.rs @@ -3,7 +3,7 @@ use std::path::PathBuf; use crate::SchemesEnum; -#[derive(Parser)] +#[derive(Parser, Clone)] #[command(version, long_about = None)] pub struct Cli { /// Optional name to operate on diff --git a/src/util/color.rs b/src/util/color.rs index d7ae267..7fccf52 100644 --- a/src/util/color.rs +++ b/src/util/color.rs @@ -39,7 +39,7 @@ pub fn show_color(schemes: &Schemes, source_color: &Argb) { } #[cfg(feature = "dump-json")] -pub fn dump_json(schemes: &Schemes, source_color: &Argb, format: &Format, palettes: Palettes) { +pub fn dump_json(schemes: &Schemes, source_color: &Argb, format: &Format, palettes: &Palettes) { use std::collections::HashMap; let mut colors_normal_light: HashMap<&str, String> = HashMap::new(); @@ -71,7 +71,7 @@ pub fn dump_json(schemes: &Schemes, source_color: &Argb, format: &Format, palett } #[cfg(feature = "dump-json")] -fn format_palettes(palettes: Palettes, format: &Format) -> serde_json::Value { +fn format_palettes(palettes: &Palettes, format: &Format) -> serde_json::Value { let primary = format_single_palette(palettes.primary, format); let secondary = format_single_palette(palettes.secondary, format); let tertiary = format_single_palette(palettes.tertiary, format); diff --git a/src/util/config.rs b/src/util/config.rs index 25d26c1..9feb156 100644 --- a/src/util/config.rs +++ b/src/util/config.rs @@ -11,7 +11,7 @@ use super::arguments::Cli; use crate::wallpaper::Wallpaper; use crate::Template; -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct Config { pub version_check: Option, pub wallpaper: Option, @@ -19,9 +19,13 @@ pub struct Config { pub prefix: Option, pub custom_keywords: Option>, pub custom_colors: Option>, + pub expr_prefix: Option, + pub expr_postfix: Option, + pub block_prefix: Option, + pub block_postfix: Option, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct ConfigFile { pub config: Config, pub templates: HashMap,