#![allow(clippy::type_complexity)]
#![allow(clippy::useless_conversion)]
pub mod args;
pub mod backends;
pub mod cache;
pub mod colors;
pub mod colorspaces;
pub mod config;
pub mod palettes;
pub mod template;
pub mod themes;
pub mod sequences;
use std::path::Path;
use anyhow::Result;
use spinners::{Spinner, Spinners};
use owo_colors::OwoColorize;
use self::colors::Colors;
use self::colorspaces::FallbackGenerator;
use self::args::Globals;
pub struct SpiWrap {
s: Option<Spinner>,
}
impl SpiWrap {
pub fn new(quiet: bool) -> Self {
let s = match quiet {
false => Some(Spinner::with_timer(Spinners::Pong, "Generating color scheme..".into())),
true => None
};
Self { s }
}
pub fn stop_warn(&mut self, gen: &FallbackGenerator) {
if let Some(sp) = &mut self.s {
let symbol = "[ 🗸 🗸 ]";
sp.stop_with_symbol(symbol);
print!("[{info}] Not enough colors in the image, artificially generating new colors...\n[{info}] {method}: Using {g} to fill the palette\n",
g = gen.to_string().color(gen.col()),
info = "I".blue().bold(),
method = "fallback generation method".magenta().bold()
);
}
}
pub fn stop(&mut self) {
if let Some(sp) = &mut self.s {
let symbol = "[ 🗸 ]";
sp.stop_with_symbol(symbol);
print!("[{info}] Color scheme palette generated!", info = "I".blue().bold());
}
}
pub fn stop_cache(&mut self, cname: &Path) {
if let Some(sp) = &mut self.s {
let symbol = "[ 🗸 ]";
let cname = cname.display();
sp.stop_with_symbol(symbol);
print!("[{info}] Color scheme palette generated!\n[{info}] Using cache at {cname}", info = "I".blue().bold());
}
}
}
impl Globals {
pub fn set_seq(&self, colors: &Colors, cache_path: &Path) -> Result<()> {
let info = "I".blue();
let info = info.bold();
let g = self;
if !g.skip_sequences && !g.update_current {
if !g.quiet { println!("[{info}] {}: Setting terminal colors.", "sequences".magenta().bold()); }
colors.sequences(cache_path, g.ignore_sequence.as_deref())?;
}
Ok(())
}
pub fn update_cur(&self, colors: &Colors) -> Result<()> {
let info = "I".blue();
let info = info.bold();
let g = self;
if g.update_current {
if !g.quiet { println!("[{info}] {seq}: Setting colors {b} in the current terminal.", seq = "sequences".magenta().bold(), b = "only".bold()); }
print!("{}", colors.to_seq(g.ignore_sequence.as_deref()));
}
Ok(())
}
}
pub fn gen_colors(file: &std::path::Path, c: &crate::config::Config, dynamic_th: bool, cache_path: &std::path::Path, no_cache: bool, quiet: bool, overwrite_cache: bool) -> anyhow::Result<crate::colors::Colors> {
let gen = &c.fallback_generator.unwrap_or_default();
let ord = &c.palette.sort_ord();
let dynamic = if c.threshold.is_some() && !dynamic_th { false } else { true };
let cache = cache::Cache::new(file, c, cache_path)?;
use cache::IsCached as C;
let quiet = quiet || (matches!(cache.is_cached_all(), C::BackendnCSnPalette) && !overwrite_cache);
let mut spi = SpiWrap::new(quiet);
if overwrite_cache {
let rgb8s = c.backend.main()(file)?;
if !no_cache { cache.write_backend(&rgb8s)? }
let cs = match c.color_space.run(dynamic, &rgb8s, c.threshold.unwrap_or_default(), gen, ord) {
Some(s) => s,
None => anyhow::bail!("Not enough colors!"),
};
let (ref top, ref orig, warn) = cs;
if !no_cache { cache.write_cs(&cs)? }
let mut colors = c.palette.run(top.to_vec(), orig.to_vec());
if !no_cache { cache.write_palette(&colors)? } postcolor(c, &mut colors);
if warn { spi.stop_warn(gen) } else { spi.stop() }
Ok(colors)
} else {
match cache.is_cached_all() {
C::BackendnCSnPalette => { let mut colors = cache.read_palette()?;
postcolor(c, &mut colors);
spi.stop_cache(&cache.name);
Ok(colors)
},
C::BackendnCS => { let (top, orig, warn) = cache.read_cs()?;
let mut colors = c.palette.run(top, orig);
if !no_cache { cache.write_palette(&colors)? } postcolor(c, &mut colors);
if warn { spi.stop_warn(gen) } else { spi.stop_cache(&cache.name) }
Ok(colors)
},
C::Backend => { let rgb8s = cache.read_backend()?;
let cs = match c.color_space.run(dynamic, &rgb8s, c.threshold.unwrap_or_default(), gen, ord) {
Some(s) => s,
None => anyhow::bail!("Not enough colors!"),
};
let (ref top, ref orig, warn) = cs;
if !no_cache { cache.write_cs(&cs)? }
let mut colors = c.palette.run(top.to_vec(), orig.to_vec());
if !no_cache { cache.write_palette(&colors)? } postcolor(c, &mut colors);
if warn { spi.stop_warn(gen); } else { spi.stop(); } Ok(colors)
},
C::None => { let rgb8s = c.backend.main()(file)?;
if !no_cache { cache.write_backend(&rgb8s)? }
let cs = match c.color_space.run(dynamic, &rgb8s, c.threshold.unwrap_or_default(), gen, ord) {
Some(s) => s,
None => anyhow::bail!("Not enough colors!"),
};
let (ref top, ref orig, warn) = cs;
if !no_cache { cache.write_cs(&cs)? }
let mut colors = c.palette.run(top.to_vec(), orig.to_vec());
if !no_cache { cache.write_palette(&colors)? } postcolor(c, &mut colors);
if warn { spi.stop_warn(gen) } else { spi.stop() }
Ok(colors)
},
}
}
}
pub fn postcolor(c: &crate::config::Config, colors: &mut crate::colors::Colors) {
if c.check_contrast.unwrap_or(false) {
colors.check_contrast_all();
}
if let Some(s) = c.saturation {
colors.saturate_colors(f32::from(s) / 100.0);
}
}