diff --git a/crates/hyfetch/src/bin/hyfetch.rs b/crates/hyfetch/src/bin/hyfetch.rs index d2d0655f..f8d36e8e 100644 --- a/crates/hyfetch/src/bin/hyfetch.rs +++ b/crates/hyfetch/src/bin/hyfetch.rs @@ -9,7 +9,7 @@ use hyfetch::cli_options::options; use hyfetch::models::Config; #[cfg(windows)] use hyfetch::neofetch_util::ensure_git_bash; -use hyfetch::neofetch_util::{self, get_distro_ascii}; +use hyfetch::neofetch_util::{self, get_distro_ascii, ColorAlignment}; use hyfetch::presets::AssignLightness; use hyfetch::utils::get_cache_path; use tracing::debug; @@ -33,10 +33,8 @@ fn main() -> Result<()> { ensure_git_bash().context("failed to find git bash")?; if options.test_print { - println!( - "{}", - get_distro_ascii(distro).context("failed to get distro ascii")? - ); + let (asc, _) = get_distro_ascii(distro).context("failed to get distro ascii")?; + println!("{asc}"); return Ok(()); } @@ -94,13 +92,28 @@ fn main() -> Result<()> { }; debug!(?color_profile, "lightened color profile"); - let asc = if let Some(path) = options.ascii_file { - fs::read_to_string(&path).with_context(|| format!("failed to read ascii from {path:?}"))? + let (asc, fore_back) = if let Some(path) = options.ascii_file { + ( + fs::read_to_string(&path) + .with_context(|| format!("failed to read ascii from {path:?}"))?, + None, + ) } else { get_distro_ascii(distro).context("failed to get distro ascii")? }; - let asc = config - .color_align + let color_align = if fore_back.is_some() { + match config.color_align { + ca @ ColorAlignment::Horizontal { .. } | ca @ ColorAlignment::Vertical { .. } => { + ca.with_fore_back(fore_back).context( + "failed to create color alignment with foreground-background configuration", + )? + }, + ca @ ColorAlignment::Custom { .. } => ca, + } + } else { + config.color_align + }; + let asc = color_align .recolor_ascii(asc, color_profile, color_mode, config.light_dark) .context("failed to recolor ascii")?; neofetch_util::run(asc, backend, args).context("failed to run")?; diff --git a/crates/hyfetch/src/neofetch_util.rs b/crates/hyfetch/src/neofetch_util.rs index 08eee897..dadedff6 100644 --- a/crates/hyfetch/src/neofetch_util.rs +++ b/crates/hyfetch/src/neofetch_util.rs @@ -27,15 +27,19 @@ use crate::types::{AnsiMode, Backend, LightDark}; const NEOFETCH_COLOR_PATTERNS: [&str; 6] = ["${c1}", "${c2}", "${c3}", "${c4}", "${c5}", "${c6}"]; static NEOFETCH_COLORS_AC: OnceLock = OnceLock::new(); +type ForeBackColorPair = (NeofetchAsciiIndexedColor, NeofetchAsciiIndexedColor); + #[derive(Clone, Eq, PartialEq, Debug, Deserialize, Serialize)] #[serde(tag = "mode")] #[serde(rename_all = "lowercase")] pub enum ColorAlignment { Horizontal { - fore_back: Option<(NeofetchAsciiIndexedColor, NeofetchAsciiIndexedColor)>, + #[serde(skip)] + fore_back: Option, }, Vertical { - fore_back: Option<(NeofetchAsciiIndexedColor, NeofetchAsciiIndexedColor)>, + #[serde(skip)] + fore_back: Option, }, Custom { #[serde(rename = "custom_colors")] @@ -45,8 +49,35 @@ pub enum ColorAlignment { } impl ColorAlignment { + /// Creates a new color alignment, with the specified foreground-background + /// configuration. + pub fn with_fore_back(&self, fore_back: Option) -> Result { + match self { + Self::Horizontal { .. } => Ok(Self::Horizontal { fore_back }), + Self::Vertical { .. } => { + if fore_back.is_some() { + debug!( + "foreground-background configuration not implemented for vertical color \ + alignment; ignoring" + ); + } + Ok(Self::Vertical { fore_back: None }) + }, + Self::Custom { colors } => { + if fore_back.is_some() { + return Err(anyhow!( + "foreground-background configuration not supported for custom colors" + )); + } + Ok(Self::Custom { + colors: colors.clone(), + }) + }, + } + } + /// Uses the color alignment to recolor an ascii art. - #[tracing::instrument(level = "debug")] + #[tracing::instrument(level = "debug", skip(asc))] pub fn recolor_ascii( &self, asc: String, @@ -54,19 +85,20 @@ impl ColorAlignment { color_mode: AnsiMode, term: LightDark, ) -> Result { - let asc = fill_starting(asc).context("failed to fill in starting neofetch color codes")?; - let reset = color("&~&*", color_mode).expect("color reset should not be invalid"); let asc = match self { - Self::Horizontal { + &Self::Horizontal { fore_back: Some((fore, back)), } - | Self::Vertical { + | &Self::Vertical { fore_back: Some((fore, back)), } => { - let fore: u8 = (*fore).into(); - let back: u8 = (*back).into(); + let fore: u8 = fore.into(); + let back: u8 = back.into(); + + let asc = fill_starting(asc) + .context("failed to fill in starting neofetch color codes")?; // Replace foreground colors let asc = asc.replace( @@ -191,6 +223,9 @@ impl ColorAlignment { Self::Custom { colors: custom_colors, } => { + let asc = fill_starting(asc) + .context("failed to fill in starting neofetch color codes")?; + let ColorProfile { colors } = color_profile.unique_colors(); // Apply colors @@ -215,6 +250,39 @@ impl ColorAlignment { Ok(asc) } + + /// Gets recommended foreground-background configuration for distro, or + /// `None` if the distro ascii is not suitable for fore-back configuration. + pub fn fore_back(distro: Distro) -> Option { + match distro { + Distro::Anarchy + | Distro::ArchStrike + | Distro::Astra_Linux + | Distro::Chapeau + | Distro::Fedora + | Distro::GalliumOS + | Distro::KrassOS + | Distro::Kubuntu + | Distro::Lubuntu + | Distro::openEuler + | Distro::Peppermint + | Distro::Pop__OS + | Distro::Ubuntu_Cinnamon + | Distro::Ubuntu_Kylin + | Distro::Ubuntu_MATE + | Distro::Ubuntu_old + | Distro::Ubuntu_Studio + | Distro::Ubuntu_Sway + | Distro::Ultramarine_Linux + | Distro::Univention + | Distro::Vanilla + | Distro::Xubuntu => Some((2u8.try_into().unwrap(), 1u8.try_into().unwrap())), + + Distro::Antergos => Some((1u8.try_into().unwrap(), 2u8.try_into().unwrap())), + + _ => None, + } + } } /// Gets the absolute path of the neofetch command. @@ -318,7 +386,7 @@ pub fn ensure_git_bash() -> Result { /// Gets the distro ascii of the current distro. Or if distro is specified, get /// the specific distro's ascii art instead. #[tracing::instrument(level = "debug")] -pub fn get_distro_ascii(distro: Option) -> Result +pub fn get_distro_ascii(distro: Option) -> Result<(String, Option)> where S: AsRef + fmt::Debug, { @@ -333,7 +401,10 @@ where // Try new codegen-based detection method if let Some(distro) = Distro::detect(&distro) { - return Ok(normalize_ascii(distro.ascii_art())); + return Ok(( + normalize_ascii(distro.ascii_art()), + ColorAlignment::fore_back(distro), + )); } debug!(%distro, "could not find a match for distro; falling back to neofetch"); @@ -346,10 +417,10 @@ where // printf let asc = asc.replace(r"\\", r"\"); - Ok(normalize_ascii(asc)) + Ok((normalize_ascii(asc), None)) } -#[tracing::instrument(level = "debug")] +#[tracing::instrument(level = "debug", skip(asc))] pub fn run(asc: String, backend: Backend, args: Option<&Vec>) -> Result<()> { match backend { Backend::Neofetch => { @@ -432,7 +503,7 @@ where let mut matches = ac.find_iter(line).peekable(); match matches.peek() { - Some(m) if m.start() == 0 => { + Some(m) if m.start() == 0 || line[0..m.start()].trim_end_matches(' ').is_empty() => { // line starts with neofetch color code, do nothing }, _ => { @@ -545,7 +616,7 @@ fn get_distro_name() -> Result { } /// Runs neofetch with colors. -#[tracing::instrument(level = "debug")] +#[tracing::instrument(level = "debug", skip(asc))] fn run_neofetch(asc: String, args: Option<&Vec>) -> Result<()> { // Escape backslashes here because backslashes are escaped in neofetch for // printf diff --git a/crates/hyfetch/src/presets.rs b/crates/hyfetch/src/presets.rs index 40cec46a..ac92547d 100644 --- a/crates/hyfetch/src/presets.rs +++ b/crates/hyfetch/src/presets.rs @@ -59,7 +59,6 @@ pub enum Preset { Gendervoid, Girlflux, Greygender, - #[serde(alias = "biromantic2")] Greysexual, Gynesexual, Intergender, @@ -433,9 +432,10 @@ impl ColorProfile { pub fn with_length(&self, length: u8) -> Result { let orig_len = self.colors.len(); let orig_len: u8 = orig_len.try_into().expect("`orig_len` should fit in `u8`"); - if length < orig_len { - unimplemented!("compressing length of color profile not implemented"); - } + // TODO: I believe weird things can happen because of this... + // if length < orig_len { + // unimplemented!("compressing length of color profile not implemented"); + // } let center_i = (orig_len as f32 / 2.0).floor() as usize; // How many copies of each color should be displayed at least? @@ -448,21 +448,16 @@ impl ColorProfile { // If there is an odd space left, extend the center by one space if extras % 2 == 1 { - extras -= 1; weights[center_i] += 1; + extras -= 1; } // Add weight to border until there's no space left (extras must be even at this // point) - // TODO: this gives a horrible result when `extras` is still large relative to - // `orig_len` - we should probably distribute even if slightly uneven - let mut border_i = 0; - while extras > 0 { - extras -= 2; + let weights_len = weights.len(); + for border_i in 0..(extras / 2) as usize { weights[border_i] += 1; - let weights_len = weights.len(); weights[weights_len - border_i - 1] += 1; - border_i += 1; } self.with_weights(weights)