From bcdc720d8a578126eb5e7596bf374cd0a43b207f Mon Sep 17 00:00:00 2001 From: Teoh Han Hui Date: Sun, 28 Jul 2024 02:35:32 +0800 Subject: [PATCH] Track only fg color indices in ascii art; the rest are bg colors * Fix vertical fore-back coloring when there are non-ASCII chars * Allow 0-width / 0-height ascii art (again) Co-authored-by: Luna --- crates/hyfetch/src/ascii.rs | 338 +++++++++++++--------------- crates/hyfetch/src/bin/hyfetch.rs | 65 +++--- crates/hyfetch/src/neofetch_util.rs | 105 ++++----- crates/hyfetch/src/pride_month.rs | 49 ++-- 4 files changed, 261 insertions(+), 296 deletions(-) diff --git a/crates/hyfetch/src/ascii.rs b/crates/hyfetch/src/ascii.rs index 7214f548..f50d6a69 100644 --- a/crates/hyfetch/src/ascii.rs +++ b/crates/hyfetch/src/ascii.rs @@ -1,11 +1,12 @@ use std::borrow::Cow; use std::fmt::Write as _; -use std::num::NonZeroU8; +use std::ops::Range; use aho_corasick::AhoCorasick; use anyhow::{Context as _, Result}; -use itertools::Itertools as _; +use indexmap::IndexMap; use tracing::debug; +use unicode_segmentation::UnicodeSegmentation; use crate::color_util::{ color, ForegroundBackground, NeofetchAsciiIndexedColor, ToAnsiString as _, @@ -21,25 +22,23 @@ use crate::types::{AnsiMode, TerminalTheme}; pub struct RawAsciiArt { pub asc: String, pub fg: Vec, - pub bg: Vec, } /// Normalized ascii art where every line has the same width. #[derive(Clone, Debug)] pub struct NormalizedAsciiArt { pub lines: Vec, - pub w: NonZeroU8, - pub h: NonZeroU8, + pub w: u8, + pub h: u8, pub fg: Vec, - pub bg: Vec, } /// Recolored ascii art with all color codes replaced. #[derive(Clone, Debug)] pub struct RecoloredAsciiArt { pub lines: Vec, - pub w: NonZeroU8, - pub h: NonZeroU8, + pub w: u8, + pub h: u8, } impl RawAsciiArt { @@ -55,7 +54,7 @@ impl RawAsciiArt { .lines() .map(|line| { let (line_w, _) = ascii_size(line).unwrap(); - let pad = " ".repeat(usize::from(w.get().checked_sub(line_w.get()).unwrap())); + let pad = " ".repeat(usize::from(w.checked_sub(line_w).unwrap())); format!("{line}{pad}") }) .collect(); @@ -65,7 +64,6 @@ impl RawAsciiArt { w, h, fg: self.fg.clone(), - bg: self.bg.clone(), }) } } @@ -82,12 +80,18 @@ impl NormalizedAsciiArt { ) -> Result { debug!("recolor ascii"); + if self.lines.is_empty() { + return Ok(RecoloredAsciiArt { + lines: self.lines.clone(), + w: 0, + h: 0, + }); + } + let reset = color("&~&*", color_mode).expect("color reset should not be invalid"); let lines = match (color_align, self) { - (ColorAlignment::Horizontal, Self { fg, bg, .. }) - if !fg.is_empty() || !bg.is_empty() => - { + (ColorAlignment::Horizontal, Self { fg, .. }) => { let Self { lines, .. } = self .fill_starting() .context("failed to fill in starting neofetch color codes")?; @@ -117,156 +121,160 @@ impl NormalizedAsciiArt { // Add new colors let lines = { - let ColorProfile { colors } = - color_profile.with_length(self.h).with_context(|| { + let ColorProfile { colors } = color_profile + .with_length(self.h.try_into().expect("`h` should not be 0")) + .with_context(|| { format!("failed to spread color profile to length {h}", h = self.h) })?; lines.enumerate().map(move |(i, line)| { - let mut replacements = NEOFETCH_COLOR_PATTERNS; - let bg_color = colors[i].to_ansi_string(color_mode, { - // This is "background" in the ascii art, but foreground text in - // terminal - ForegroundBackground::Foreground - }); - for &back in bg { - replacements[usize::from(u8::from(back)).checked_sub(1).unwrap()] = - &bg_color; - } + let bg_color = + colors[i].to_ansi_string(color_mode, ForegroundBackground::Foreground); + const N: usize = NEOFETCH_COLOR_PATTERNS.len(); + let replacements = [&bg_color; N]; ac.replace_all(line, &replacements) }) }; - // Remove existing colors - let asc = { - let mut lines = lines; - let asc = lines.join("\n"); - const N: usize = NEOFETCH_COLOR_PATTERNS.len(); - let replacements: [&str; N] = [&reset; N]; - ac.replace_all(&asc, &replacements) - }; - let lines = asc.lines(); - // Reset colors at end of each line to prevent color bleeding - let lines = lines.map(|line| format!("{line}{reset}")); - - lines.collect() + lines.map(|line| format!("{line}{reset}")).collect() }, - (ColorAlignment::Vertical, Self { fg, bg, .. }) if !fg.is_empty() || !bg.is_empty() => { + (ColorAlignment::Vertical, Self { fg, .. }) if !fg.is_empty() => { + if self.w == 0 { + return Ok(RecoloredAsciiArt { + lines: self.lines.clone(), + w: 0, + h: self.h, + }); + } + let Self { lines, .. } = self .fill_starting() .context("failed to fill in starting neofetch color codes")?; - let color_profile = color_profile.with_length(self.w).with_context(|| { - format!("failed to spread color profile to length {w}", w = self.w) - })?; + let color_profile = color_profile + .with_length(self.w.try_into().expect("`w` should not be 0")) + .with_context(|| { + format!("failed to spread color profile to length {w}", w = self.w) + })?; // Apply colors - let lines: Vec<_> = { - let ac = NEOFETCH_COLORS_AC - .get_or_init(|| AhoCorasick::new(NEOFETCH_COLOR_PATTERNS).unwrap()); - lines - .into_iter() - .map(|line| { - let line: &str = line.as_ref(); + let ac = NEOFETCH_COLORS_AC + .get_or_init(|| AhoCorasick::new(NEOFETCH_COLOR_PATTERNS).unwrap()); + lines + .into_iter() + .map(|line| { + let line: &str = line.as_ref(); - let mut matches = ac.find_iter(line).peekable(); - let mut dst = String::new(); - let mut offset: u8 = 0; - loop { - let current = matches.next(); - let next = matches.peek(); - let (neofetch_color_idx, span, done) = match (current, next) { - (Some(m), Some(m_next)) => { - let ai_start = m.start().checked_add(3).unwrap(); - let ai_end = m.end().checked_sub(1).unwrap(); - let neofetch_color_idx: NeofetchAsciiIndexedColor = line - [ai_start..ai_end] - .parse() - .expect("neofetch color index should be valid"); - offset = offset - .checked_add(u8::try_from(m.len()).unwrap()) - .unwrap(); - let mut span = m.span(); - span.start = m.end(); - span.end = m_next.start(); - (neofetch_color_idx, span, false) - }, - (Some(m), None) => { - // Last color code - let ai_start = m.start().checked_add(3).unwrap(); - let ai_end = m.end().checked_sub(1).unwrap(); - let neofetch_color_idx: NeofetchAsciiIndexedColor = line - [ai_start..ai_end] - .parse() - .expect("neofetch color index should be valid"); - offset = offset - .checked_add(u8::try_from(m.len()).unwrap()) - .unwrap(); - let mut span = m.span(); - span.start = m.end(); - span.end = line.len(); - (neofetch_color_idx, span, true) - }, - (None, _) => { - // No color code in the entire line - unreachable!( - "`fill_starting` ensured each line of ascii art \ - starts with neofetch color code" - ); - }, - }; - let txt = &line[span]; + // `AhoCorasick` operates on bytes; we need to map that back to grapheme + // clusters (i.e. a character as seen on the terminal) + // See https://github.com/BurntSushi/aho-corasick/issues/72#issuecomment-821128859 + let byte_idx_to_grapheme_idx: IndexMap = { + let mut m: IndexMap<_, _> = line + .grapheme_indices(true) + .enumerate() + .map(|(gr_idx, (byte_idx, _))| (byte_idx, gr_idx)) + .collect(); + // Add an extra entry at the end, to support lookup using exclusive + // range end + m.insert(line.len(), m.len()); + m + }; - if fg.contains(&neofetch_color_idx) { - let fore = color( - match theme { - TerminalTheme::Light => "&0", - TerminalTheme::Dark => "&f", - }, - color_mode, - ) - .expect("foreground color should not be invalid"); - write!(dst, "{fore}{txt}{reset}").unwrap(); - } else if bg.contains(&neofetch_color_idx) { - let adjusted_start = - span.start.checked_sub(usize::from(offset)).unwrap(); - let adjusted_end = - span.end.checked_sub(usize::from(offset)).unwrap(); - dst.push_str( - &ColorProfile::new(Vec::from( - &color_profile.colors[adjusted_start..adjusted_end], - )) + let mut matches = ac.find_iter(line).peekable(); + let mut dst = String::new(); + let mut offset: u8 = 0; + loop { + let current = matches.next(); + let next = matches.peek(); + let (neofetch_color_idx, span, done) = match (current, next) { + (Some(m), Some(m_next)) => { + let ai_start = m.start().checked_add(3).unwrap(); + let ai_end = m.end().checked_sub(1).unwrap(); + let neofetch_color_idx: NeofetchAsciiIndexedColor = line + [ai_start..ai_end] + .parse() + .expect("neofetch color index should be valid"); + if offset == 0 && m.start() > 0 { + dst.push_str(&line[..m.start()]); + } + offset = + offset.checked_add(u8::try_from(m.len()).unwrap()).unwrap(); + let mut span = m.span(); + span.start = m.end(); + span.end = m_next.start(); + (neofetch_color_idx, span, false) + }, + (Some(m), None) => { + // Last color code + let ai_start = m.start().checked_add(3).unwrap(); + let ai_end = m.end().checked_sub(1).unwrap(); + let neofetch_color_idx: NeofetchAsciiIndexedColor = line + [ai_start..ai_end] + .parse() + .expect("neofetch color index should be valid"); + if offset == 0 && m.start() > 0 { + dst.push_str(&line[..m.start()]); + } + offset = + offset.checked_add(u8::try_from(m.len()).unwrap()).unwrap(); + let mut span = m.span(); + span.start = m.end(); + span.end = line.len(); + (neofetch_color_idx, span, true) + }, + (None, _) => { + // No color code in the entire line + unreachable!( + "`fill_starting` ensured each line of ascii art starts \ + with neofetch color code" + ); + }, + }; + let txt = &line[span]; + + if fg.contains(&neofetch_color_idx) { + let fore = color( + match theme { + TerminalTheme::Light => "&0", + TerminalTheme::Dark => "&f", + }, + color_mode, + ) + .expect("foreground color should not be invalid"); + write!(dst, "{fore}{txt}{reset}").unwrap(); + } else { + let mut c_range: Range = span.into(); + c_range.start = byte_idx_to_grapheme_idx + .get(&c_range.start) + .unwrap() + .checked_sub(usize::from(offset)) + .unwrap(); + c_range.end = byte_idx_to_grapheme_idx + .get(&c_range.end) + .unwrap() + .checked_sub(usize::from(offset)) + .unwrap(); + dst.push_str( + &ColorProfile::new(Vec::from(&color_profile.colors[c_range])) .color_text( txt, color_mode, - { - // This is "background" in the ascii art, but - // foreground text in terminal - ForegroundBackground::Foreground - }, + ForegroundBackground::Foreground, false, ) .context("failed to color text using color profile")?, - ); - } else { - dst.push_str(txt); - } - - if done { - break; - } + ); } - Ok(dst) - }) - .collect::>()? - }; - lines + if done { + break; + } + } + Ok(dst) + }) + .collect::>()? }, - (ColorAlignment::Horizontal, Self { fg, bg, .. }) - | (ColorAlignment::Vertical, Self { fg, bg, .. }) - if fg.is_empty() && bg.is_empty() => - { + (ColorAlignment::Vertical, Self { fg, .. }) if fg.is_empty() => { // Remove existing colors let asc = { let asc = self.lines.join("\n"); @@ -279,38 +287,14 @@ impl NormalizedAsciiArt { let lines = asc.lines(); // Add new colors - match color_align { - ColorAlignment::Horizontal => { - let ColorProfile { colors } = - color_profile.with_length(self.h).with_context(|| { - format!("failed to spread color profile to length {h}", h = self.h) - })?; - lines - .enumerate() - .map(|(i, line)| { - let fore = colors[i] - .to_ansi_string(color_mode, ForegroundBackground::Foreground); - format!("{fore}{line}{reset}") - }) - .collect() - }, - ColorAlignment::Vertical => lines - .map(|line| { - let line = color_profile - .color_text( - line, - color_mode, - ForegroundBackground::Foreground, - false, - ) - .context("failed to color text using color profile")?; - Ok(line) - }) - .collect::>()?, - _ => { - unreachable!(); - }, - } + lines + .map(|line| { + let line = color_profile + .color_text(line, color_mode, ForegroundBackground::Foreground, false) + .context("failed to color text using color profile")?; + Ok(line) + }) + .collect::>()? }, ( ColorAlignment::Custom { @@ -344,9 +328,7 @@ impl NormalizedAsciiArt { let lines = asc.lines(); // Reset colors at end of each line to prevent color bleeding - let lines = lines.map(|line| format!("{line}{reset}")); - - lines.collect() + lines.map(|line| format!("{line}{reset}")).collect() }, _ => { unreachable!() @@ -382,19 +364,24 @@ impl NormalizedAsciiArt { if m.start() == 0 || line[0..m.start()].trim_end_matches(' ').is_empty() => { - // line starts with neofetch color code, do nothing + // Line starts with neofetch color code + last = Some(&line[m.span()]); }, - _ => { + Some(_) => { new.push_str(last.context( "failed to find neofetch color code from a previous line", )?); }, + None => { + new.push_str(last.unwrap_or(NEOFETCH_COLOR_PATTERNS[0])); + }, } new.push_str(line); // Get the last placeholder for the next line if let Some(m) = matches.last() { - last = Some(&line[m.span()]) + last.context("non-space character seen before first color code")?; + last = Some(&line[m.span()]); } Ok(new) @@ -404,7 +391,6 @@ impl NormalizedAsciiArt { Ok(Self { lines, fg: self.fg.clone(), - bg: self.bg.clone(), ..*self }) } diff --git a/crates/hyfetch/src/bin/hyfetch.rs b/crates/hyfetch/src/bin/hyfetch.rs index 9954bab1..cca71649 100644 --- a/crates/hyfetch/src/bin/hyfetch.rs +++ b/crates/hyfetch/src/bin/hyfetch.rs @@ -4,7 +4,7 @@ use std::fmt::Write as _; use std::fs::{self, File}; use std::io::{self, IsTerminal as _, Read as _, Write as _}; use std::iter::zip; -use std::num::{NonZeroU16, NonZeroU8, NonZeroUsize}; +use std::num::NonZeroU8; use std::path::{Path, PathBuf}; use aho_corasick::AhoCorasick; @@ -22,8 +22,8 @@ use hyfetch::models::Config; #[cfg(feature = "macchina")] use hyfetch::neofetch_util::macchina_path; use hyfetch::neofetch_util::{ - self, ascii_size, fastfetch_path, get_distro_ascii, literal_input, ColorAlignment, - NEOFETCH_COLORS_AC, NEOFETCH_COLOR_PATTERNS, TEST_ASCII, + self, fastfetch_path, get_distro_ascii, literal_input, ColorAlignment, NEOFETCH_COLORS_AC, + NEOFETCH_COLOR_PATTERNS, TEST_ASCII, }; use hyfetch::presets::{AssignLightness, Preset}; use hyfetch::pride_month; @@ -132,7 +132,6 @@ fn main() -> Result<()> { asc: fs::read_to_string(&path) .with_context(|| format!("failed to read ascii from {path:?}"))?, fg: Vec::new(), - bg: Vec::new(), } } else { get_distro_ascii(distro, backend).context("failed to get distro ascii")? @@ -267,14 +266,14 @@ fn create_config( let (Width(term_w), Height(term_h)) = terminal_size().context("failed to get terminal size")?; let (term_w_min, term_h_min) = ( - NonZeroU16::from(asc.w) - .checked_mul(NonZeroU16::new(2).unwrap()) + u16::from(asc.w) + .checked_mul(2) .unwrap() .checked_add(4) .unwrap(), - NonZeroU16::new(30).unwrap(), + 30, ); - if term_w < term_w_min.get() || term_h < term_h_min.get() { + if term_w < term_w_min || term_h < term_h_min { printc( format!( "&cWarning: Your terminal is too small ({term_w} * {term_h}).\nPlease resize \ @@ -612,9 +611,15 @@ fn create_config( ////////////////////////////// // 4. Dim/lighten colors - let test_ascii = &TEST_ASCII[1..TEST_ASCII.len().checked_sub(1).unwrap()]; - let (test_ascii_width, test_ascii_height) = - ascii_size(test_ascii).expect("test ascii should have valid width and height"); + let test_ascii = { + let asc = &TEST_ASCII[1..TEST_ASCII.len().checked_sub(1).unwrap()]; + let asc = RawAsciiArt { + asc: asc.to_owned(), + fg: Vec::new(), + }; + asc.to_normalized() + .expect("normalizing test ascii should not fail") + }; let select_lightness = || -> Result { clear_screen(Some(&title), color_mode, debug_mode).context("failed to clear screen")?; @@ -642,12 +647,7 @@ fn create_config( let (Width(term_w), _) = terminal_size().context("failed to get terminal size")?; let num_cols = cmp::max( 1, - term_w.div_euclid( - NonZeroU16::from(test_ascii_width) - .checked_add(2) - .unwrap() - .get(), - ), + term_w.div_euclid(u16::from(test_ascii.w).checked_add(2).unwrap()), ); let num_cols: u8 = num_cols.try_into().expect("`num_cols` should fit in `u8`"); const MIN: f32 = 0.15; @@ -661,20 +661,20 @@ fn create_config( }); let row: Vec> = ratios .map(|r| { - let asc = RawAsciiArt { - asc: test_ascii.replace( + let mut asc = test_ascii.clone(); + asc.lines = asc + .lines + .join("\n") + .replace( "{txt}", &format!( "{lightness:^5}", lightness = format!("{lightness:.0}%", lightness = r * 100.0) ), - ), - fg: Vec::new(), - bg: Vec::new(), - }; - let asc = asc - .to_normalized() - .expect("normalizing test ascii should not fail"); + ) + .lines() + .map(ToOwned::to_owned) + .collect(); let asc = asc .to_recolored( &color_align, @@ -690,7 +690,7 @@ fn create_config( asc.lines }) .collect(); - for i in 0..NonZeroUsize::from(test_ascii_height).get() { + for i in 0..usize::from(test_ascii.h) { let mut line = Vec::new(); for lines in &row { line.push(&*lines[i]); @@ -769,7 +769,7 @@ fn create_config( terminal_size().context("failed to get terminal size")?; let ascii_per_row = cmp::max( 1, - term_w.div_euclid(NonZeroU16::from(asc.w).checked_add(2).unwrap().get()), + term_w.div_euclid(u16::from(asc.w).checked_add(2).unwrap()), ); let ascii_per_row: u8 = ascii_per_row .try_into() @@ -778,7 +778,7 @@ fn create_config( 1, term_h .saturating_sub(8) - .div_euclid(NonZeroU16::from(asc.h).checked_add(1).unwrap().get()), + .div_euclid(u16::from(asc.h).checked_add(1).unwrap()), ); let ascii_rows: u8 = ascii_rows .try_into() @@ -866,10 +866,7 @@ fn create_config( .to_recolored(ca, &color_profile, color_mode, theme) .context("failed to recolor ascii")? .lines; - v.push(format!( - "{k:^asc_width$}", - asc_width = NonZeroUsize::from(asc.w).get() - )); + v.push(format!("{k:^asc_width$}", asc_width = usize::from(asc.w))); Ok(v) }) .collect::>()?; @@ -878,7 +875,7 @@ fn create_config( let row: Vec> = row.collect(); // Print by row - for i in 0..NonZeroUsize::from(asc.h).checked_add(1).unwrap().get() { + for i in 0..usize::from(asc.h).checked_add(1).unwrap() { let mut line = Vec::new(); for lines in &row { line.push(&*lines[i]); diff --git a/crates/hyfetch/src/neofetch_util.rs b/crates/hyfetch/src/neofetch_util.rs index 25fdda48..5e2396ae 100644 --- a/crates/hyfetch/src/neofetch_util.rs +++ b/crates/hyfetch/src/neofetch_util.rs @@ -5,7 +5,6 @@ use std::fs; #[cfg(windows)] use std::io; use std::io::{self, Write as _}; -use std::num::{NonZeroU8, NonZeroUsize}; use std::path::{Path, PathBuf}; use std::process::Command; use std::sync::OnceLock; @@ -290,9 +289,9 @@ where // Try new codegen-based detection method if let Some(distro) = Distro::detect(&distro) { let asc = distro.ascii_art().to_owned(); - let (fg, bg) = fore_back(&distro); + let fg = ascii_foreground(&distro); - return Ok(RawAsciiArt { asc, fg, bg }); + return Ok(RawAsciiArt { asc, fg }); } debug!(%distro, "could not find a match for distro; falling back to neofetch"); @@ -308,7 +307,6 @@ where Ok(RawAsciiArt { asc, fg: Vec::new(), - bg: Vec::new(), }) } @@ -333,12 +331,16 @@ pub fn run(asc: RecoloredAsciiArt, backend: Backend, args: Option<&Vec>) } /// Gets distro ascii width and height, ignoring color code. -pub fn ascii_size(asc: S) -> Result<(NonZeroU8, NonZeroU8)> +pub fn ascii_size(asc: S) -> Result<(u8, u8)> where S: AsRef, { let asc = asc.as_ref(); + if asc.is_empty() { + return Ok((0, 0)); + } + let asc = { let ac = NEOFETCH_COLORS_AC.get_or_init(|| AhoCorasick::new(NEOFETCH_COLOR_PATTERNS).unwrap()); @@ -347,21 +349,23 @@ where ac.replace_all(asc, &REPLACEMENTS) }; + if asc.is_empty() { + return Ok((0, 0)); + } + let width = asc .lines() .map(|line| line.graphemes(true).count()) .max() .expect("line iterator should not be empty"); - let width: NonZeroUsize = width.try_into().context("`asc` should not be empty")?; - let width: NonZeroU8 = width.try_into().with_context(|| { + let width: u8 = width.try_into().with_context(|| { format!( "`asc` should not have more than {limit} characters per line", limit = u8::MAX ) })?; let height = asc.lines().count(); - let height: NonZeroUsize = height.try_into().context("`asc` should not be empty")?; - let height: NonZeroU8 = height.try_into().with_context(|| { + let height: u8 = height.try_into().with_context(|| { format!( "`asc` should not have more than {limit} lines", limit = u8::MAX @@ -827,52 +831,41 @@ fn run_macchina(asc: String, args: Option<&Vec>) -> Result<()> { Ok(()) } -/// Gets recommended foreground-background configuration for distro. -fn fore_back( - distro: &Distro, -) -> ( - Vec, - Vec, -) { - let (fg, bg): (Vec, Vec) = match distro { - Distro::Anarchy => (vec![2], vec![1]), - Distro::Antergos => (vec![1], vec![2]), - Distro::ArchStrike => (vec![2], vec![1]), - Distro::Astra_Linux => (vec![2], vec![1]), - Distro::Chapeau => (vec![2], vec![1]), - Distro::Fedora => (vec![2], vec![1]), - Distro::Fedora_Silverblue => (vec![2], vec![1, 3]), - Distro::GalliumOS => (vec![2], vec![1]), - Distro::KrassOS => (vec![2], vec![1]), - Distro::Kubuntu => (vec![2], vec![1]), - Distro::Lubuntu => (vec![2], vec![1]), - Distro::openEuler => (vec![2], vec![1]), - Distro::Peppermint => (vec![2], vec![1]), - Distro::Pop__OS => (vec![2], vec![1]), - Distro::Ubuntu_Cinnamon => (vec![2], vec![1]), - Distro::Ubuntu_Kylin => (vec![2], vec![1]), - Distro::Ubuntu_MATE => (vec![2], vec![1]), - Distro::Ubuntu_old => (vec![2], vec![1]), - Distro::Ubuntu_Studio => (vec![2], vec![1]), - Distro::Ubuntu_Sway => (vec![2], vec![1]), - Distro::Ultramarine_Linux => (vec![2], vec![1]), - Distro::Univention => (vec![2], vec![1]), - Distro::Vanilla => (vec![2], vec![1]), - Distro::Xubuntu => (vec![2], vec![1]), - _ => (Vec::new(), Vec::new()), +/// Gets the color indices that should be considered as foreground, for a +/// particular distro's ascii art. +fn ascii_foreground(distro: &Distro) -> Vec { + let fg: Vec = match distro { + Distro::Anarchy => vec![2], + Distro::Antergos => vec![1], + Distro::ArchStrike => vec![2], + Distro::Astra_Linux => vec![2], + Distro::Chapeau => vec![2], + Distro::Fedora => vec![2], + Distro::Fedora_Silverblue => vec![2], + Distro::GalliumOS => vec![2], + Distro::KrassOS => vec![2], + Distro::Kubuntu => vec![2], + Distro::Lubuntu => vec![2], + Distro::openEuler => vec![2], + Distro::Peppermint => vec![2], + Distro::Pop__OS => vec![2], + Distro::Ubuntu_Cinnamon => vec![2], + Distro::Ubuntu_Kylin => vec![2], + Distro::Ubuntu_MATE => vec![2], + Distro::Ubuntu_old => vec![2], + Distro::Ubuntu_Studio => vec![2], + Distro::Ubuntu_Sway => vec![2], + Distro::Ultramarine_Linux => vec![2], + Distro::Univention => vec![2], + Distro::Vanilla => vec![2], + Distro::Xubuntu => vec![2], + _ => Vec::new(), }; - ( - fg.into_iter() - .map(|fore| { - fore.try_into() - .expect("`fore` should be a valid neofetch color index") - }) - .collect(), - bg.into_iter() - .map(|back| { - back.try_into() - .expect("`back` should be a valid neofetch color index") - }) - .collect(), - ) + + fg.into_iter() + .map(|fore| { + fore.try_into() + .expect("`fore` should be a valid neofetch color index") + }) + .collect() } diff --git a/crates/hyfetch/src/pride_month.rs b/crates/hyfetch/src/pride_month.rs index 2b0a13f9..7ee2efe4 100644 --- a/crates/hyfetch/src/pride_month.rs +++ b/crates/hyfetch/src/pride_month.rs @@ -1,5 +1,5 @@ use std::io::{self, Write as _}; -use std::num::{NonZeroU16, NonZeroU8, NonZeroUsize, Wrapping}; +use std::num::{NonZeroU16, NonZeroUsize, Wrapping}; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use std::time::Duration; @@ -48,54 +48,47 @@ pub fn start_animation(color_mode: AnsiMode) -> Result<()> { const TEXT_BORDER_WIDTH: u16 = 2; const NOTICE_BORDER_WIDTH: u16 = 1; const VERTICAL_MARGIN: u16 = 1; - let notice_w: NonZeroUsize = NOTICE - .len() - .try_into() - .expect("`NOTICE` should not be empty"); - let notice_w: NonZeroU8 = notice_w + let notice_w = NOTICE.len(); + let notice_w: u8 = notice_w .try_into() .expect("`NOTICE` width should fit in `u8`"); - let notice_h: NonZeroUsize = NOTICE - .lines() - .count() - .try_into() - .expect("`NOTICE` should not be empty"); - let notice_h: NonZeroU8 = notice_h + let notice_h = NOTICE.lines().count(); + let notice_h: u8 = notice_h .try_into() .expect("`NOTICE` height should fit in `u8`"); let term_w_min = cmp::max( - NonZeroU16::from(text_width) + u16::from(text_width) .checked_add(TEXT_BORDER_WIDTH.checked_mul(2).unwrap()) .unwrap(), - NonZeroU16::from(notice_w) + u16::from(notice_w) .checked_add(NOTICE_BORDER_WIDTH.checked_mul(2).unwrap()) .unwrap(), ); - let term_h_min = NonZeroU16::from(text_height) - .checked_add(notice_h.get().into()) + let term_h_min = u16::from(text_height) + .checked_add(notice_h.into()) .unwrap() .checked_add(VERTICAL_MARGIN.checked_mul(2).unwrap()) .unwrap(); - if w >= term_w_min && h >= term_h_min { + if w.get() >= term_w_min && h.get() >= term_h_min { (text, text_width, text_height) } else { let text = &TEXT_ASCII_SMALL[1..TEXT_ASCII_SMALL.len().checked_sub(1).unwrap()]; let (text_width, text_height) = ascii_size(text).expect("text ascii should have valid width and height"); let term_w_min = cmp::max( - NonZeroU16::from(text_width) + u16::from(text_width) .checked_add(TEXT_BORDER_WIDTH.checked_mul(2).unwrap()) .unwrap(), - NonZeroU16::from(notice_w) + u16::from(notice_w) .checked_add(NOTICE_BORDER_WIDTH.checked_mul(2).unwrap()) .unwrap(), ); - let term_h_min = NonZeroU16::from(text_height) - .checked_add(notice_h.get().into()) + let term_h_min = u16::from(text_height) + .checked_add(notice_h.into()) .unwrap() .checked_add(VERTICAL_MARGIN.checked_mul(2).unwrap()) .unwrap(); - if w < term_w_min || h < term_h_min { + if w.get() < term_w_min || h.get() < term_h_min { return Err(anyhow!( "terminal size should be at least ({term_w_min} * {term_h_min})" )); @@ -115,19 +108,15 @@ pub fn start_animation(color_mode: AnsiMode) -> Result<()> { let text_start_y = h .get() .div_euclid(2) - .checked_sub(u16::from(text_height.get() / 2)) - .unwrap(); - let text_end_y = text_start_y - .checked_add(NonZeroU16::from(text_height).get()) + .checked_sub((text_height / 2).into()) .unwrap(); + let text_end_y = text_start_y.checked_add(text_height.into()).unwrap(); let text_start_x = w .get() .div_euclid(2) - .checked_sub(u16::from(text_width.get() / 2)) - .unwrap(); - let text_end_x = text_start_x - .checked_add(NonZeroU16::from(text_width).get()) + .checked_sub((text_width / 2).into()) .unwrap(); + let text_end_x = text_start_x.checked_add(text_width.into()).unwrap(); let notice_start_x = w .get()