diff --git a/crates/hyfetch/build.rs b/crates/hyfetch/build.rs index 4bcc6965..3fc78e53 100644 --- a/crates/hyfetch/build.rs +++ b/crates/hyfetch/build.rs @@ -7,16 +7,27 @@ use anyhow::{Context, Result}; use fs_extra::dir::CopyOptions; use heck::ToUpperCamelCase; use indexmap::IndexMap; -use regex::Regex; use serde::Deserialize; use unicode_normalization::UnicodeNormalization as _; #[derive(Debug)] struct AsciiDistro { pattern: String, + color: String, + foreground: Vec, + background: Option, art: String, } +#[derive(Deserialize, Debug)] +struct DistroHeader { + #[serde(rename = "match")] + pattern: String, + color: serde_json::Value, + foreground: Option>, + background: Option, +} + impl AsciiDistro { fn friendly_name(&self) -> String { self.pattern @@ -37,31 +48,26 @@ fn main() -> Result<()> { let dir = PathBuf::from(env::var_os("CARGO_WORKSPACE_DIR").unwrap_or_else(|| env::var_os("CARGO_MANIFEST_DIR").unwrap())); let o = PathBuf::from(env::var_os("OUT_DIR").unwrap()); - for file in &["neofetch", "hyfetch/data"] { - let src = anything_that_exist(&[ - &dir.join(file), - &dir.join("../../").join(file), - ]).context("couldn't find neofetch")?; - let dst = o.join(file); - println!("cargo:rerun-if-changed={}", src.display()); - - // Copy either file or directory - if src.is_dir() { - let opt = CopyOptions { overwrite: true, copy_inside: true, ..CopyOptions::default() }; - println!("copying {} to {}", src.display(), dst.display()); - fs_extra::dir::copy(&src, &dst, &opt)?; - } - else { fs::copy(&src, &dst)?; } - } + let data_dir = anything_that_exist(&[ + &dir.join("hyfetch/data"), + &dir.join("../../hyfetch/data"), + ]).context("couldn't find hyfetch/data")?; + + let dst_data = o.join("hyfetch/data"); + fs::create_dir_all(&dst_data)?; + + // Copy hyfetch/data + let opt = CopyOptions { overwrite: true, copy_inside: true, ..CopyOptions::default() }; + fs_extra::dir::copy(&data_dir, &dst_data, &opt)?; preset_codegen(&o.join("hyfetch/data/presets.json"), &o.join("presets.rs"))?; - export_distros(&o.join("neofetch"), &o)?; + export_distros(&data_dir.join("distros"), &o)?; Ok(()) } -fn export_distros(neofetch_path: &Path, out_path: &Path) -> Result<()> +fn export_distros(distro_dir: &Path, out_path: &Path) -> Result<()> { - let distros = parse_ascii_distros(neofetch_path)?; + let distros = parse_ascii_distros(distro_dir)?; let mut variants = IndexMap::with_capacity(distros.len()); for distro in &distros { @@ -164,6 +170,66 @@ impl Distro { None } + pub fn color(&self) -> &str { + match self { +"###, + ); + + for (variant, AsciiDistro { color, .. }) in &variants { + write!(buf, r###" + Self::{variant} => {color:?}, +"###, color = color)?; + } + + buf.push_str( + r###" + } + } + + pub fn foreground(&self) -> &[u8] { + match self { +"###, + ); + + for (variant, AsciiDistro { foreground, .. }) in &variants { + if foreground.is_empty() { + write!(buf, r###" + Self::{variant} => &[], +"###)?; + } else { + write!(buf, r###" + Self::{variant} => &{:?}, +"###, foreground)?; + } + } + + buf.push_str( + r###" + } + } + + pub fn background(&self) -> Option { + match self { +"###, + ); + + for (variant, AsciiDistro { background, .. }) in &variants { + if let Some(b) = background { + write!(buf, r###" + Self::{variant} => Some({b}), +"###)?; + } else { + write!(buf, r###" + Self::{variant} => None, +"###)?; + } + } + + buf.push_str( + r###" + } + } + pub fn ascii_art(&self) -> &str { let art = match self { "###, @@ -191,67 +257,35 @@ impl Distro { Ok(()) } -/// Parses ascii distros from neofetch script. -fn parse_ascii_distros(neofetch_path: &Path) -> Result> +fn parse_ascii_distros(distro_dir: &Path) -> Result> { - let nf = { - let nf = fs::read_to_string(neofetch_path)?; + let mut distros = Vec::new(); + let mut paths: Vec<_> = fs::read_dir(distro_dir)? + .filter_map(|e| e.ok()) + .map(|e| e.path()) + .collect(); + paths.sort(); - // Get the content of "get_distro_ascii" function - let (_, nf) = nf - .split_once("get_distro_ascii() {\n") - .context("couldn't find get_distro_ascii function")?; - let (nf, _) = nf - .split_once("\n}\n") - .context("couldn't find end of get_distro_ascii function")?; - - let mut nf = nf.replace('\t', &" ".repeat(4)); - - // Remove trailing spaces - while nf.contains(" \n") { - nf = nf.replace(" \n", "\n"); + for path in paths { + if path.extension().and_then(|s| s.to_str()) == Some("ascii") { + let content = fs::read_to_string(&path)?; + let (header_line, art) = content.split_once('\n').context("invalid distro file")?; + let header: DistroHeader = serde_json::from_str(header_line)?; + let color = match header.color { + serde_json::Value::String(s) => s, + serde_json::Value::Number(n) => n.to_string(), + _ => "7".to_owned(), + }; + distros.push(AsciiDistro { + pattern: header.pattern, + color, + foreground: header.foreground.unwrap_or_default(), + background: header.background, + art: art.to_owned(), + }); } - nf - }; - - let case_re = Regex::new(r"case .*? in\n")?; - let eof_re = Regex::new(r"EOF[ \n]*?;;")?; - - // Split by blocks - let mut blocks = Vec::new(); - for b in case_re.split(&nf) { - blocks.extend(eof_re.split(b).map(|sub| sub.trim())); } - - // Parse blocks - fn parse_block(block: &str) -> Option { - let (block, art) = block.split_once("'EOF'\n")?; - - // Join \ - // - // > A that is not quoted shall preserve the literal value of the - // > following character, with the exception of a . If a - // > follows the , the shell shall interpret this as line - // > continuation. The and shall be removed before - // > splitting the input into tokens. Since the escaped is removed - // > entirely from the input and is not replaced by any white space, it cannot - // > serve as a token separator. - // See https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_02_01 - let block = block.replace("\\\n", ""); - - // Get case pattern - let pattern = block - .split('\n') - .next() - .and_then(|pattern| pattern.trim().strip_suffix(')'))?; - - // Unescape backslashes here because backslashes are escaped in neofetch - // for printf - let art = art.replace(r"\\", r"\"); - - Some(AsciiDistro { pattern: pattern.to_owned(), art }) - } - Ok(blocks.iter().filter_map(|block| parse_block(block)).collect()) + Ok(distros) } // Preset parsing diff --git a/crates/hyfetch/src/ascii.rs b/crates/hyfetch/src/ascii.rs index ebb74f1a..4f86a384 100644 --- a/crates/hyfetch/src/ascii.rs +++ b/crates/hyfetch/src/ascii.rs @@ -372,12 +372,7 @@ impl NormalizedAsciiArt { // 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])); }, } @@ -385,7 +380,6 @@ impl NormalizedAsciiArt { // Get the last placeholder for the next line if let Some(m) = matches.last() { - last.context("non-space character seen before first color code")?; last = Some(&line[m.span()]); } diff --git a/crates/hyfetch/src/neofetch_util.rs b/crates/hyfetch/src/neofetch_util.rs index 3357a11c..466040b4 100644 --- a/crates/hyfetch/src/neofetch_util.rs +++ b/crates/hyfetch/src/neofetch_util.rs @@ -241,27 +241,27 @@ pub fn get_distro_ascii(distro: Option, backend: Backend) -> Result + fmt::Debug, { - let distro: Cow<_> = if let Some(distro) = distro.as_ref() { + let distro_name: Cow<_> = if let Some(distro) = distro.as_ref() { distro.as_ref().into() } else { get_distro_name(backend) .context("failed to get distro name")? .into() }; - debug!(%distro, "distro name"); + debug!(%distro_name, "distro name"); // Try new codegen-based detection method - if let Some(distro) = Distro::detect(&distro) { + if let Some(distro) = Distro::detect(&distro_name) { let asc = distro.ascii_art().to_owned(); let fg = ascii_foreground(&distro); return Ok(RawAsciiArt { asc, fg }); } - debug!(%distro, "could not find a match for distro; falling back to neofetch"); + debug!(%distro_name, "could not find a match for distro; falling back to neofetch"); // Old detection method that calls neofetch - let asc = run_neofetch_command_piped(&["print_ascii", "--ascii_distro", distro.as_ref()]) + let asc = run_neofetch_command_piped(&["print_ascii", "--ascii_distro", distro_name.as_ref()]) .context("failed to get ascii art from neofetch")?; // Unescape backslashes here because backslashes are escaped in neofetch for @@ -728,65 +728,8 @@ fn run_macchina(asc: String, args: Option<&Vec>) -> Result<()> { /// 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::Android => vec![2], - Distro::Antergos => vec![1], - Distro::ArchStrike => vec![2], - Distro::Arkane => vec![1], - Distro::Asahi => vec![5], - Distro::Astra_Linux => vec![2], - Distro::BlackArch => vec![3], - Distro::CelOS => vec![3], - Distro::Chapeau => vec![2], - Distro::Chrom => vec![5], - Distro::Clear_Linux_OS => vec![2], - Distro::Container_Linux_by_CoreOS => vec![3], - Distro::CRUX => vec![3], - Distro::EuroLinux => vec![2], - Distro::eweOS => vec![3], - Distro::Fedora => vec![2], - Distro::Fedora_Sericea => vec![2], - Distro::Fedora_Silverblue => vec![2], - Distro::GalliumOS => vec![2], - Distro::Gentoo => vec![1], - Distro::HarDClanZ => vec![2], - Distro::Kibojoe => vec![3], - Distro::KrassOS => vec![2], - Distro::Kubuntu => vec![2], - Distro::Linux => vec![1], - Distro::LinuxFromScratch => vec![1, 3], - Distro::Lubuntu => vec![2], - Distro::openEuler => vec![2], - Distro::orchid => vec![1], - Distro::Panwah => vec![1], - Distro::Peppermint => vec![2], - Distro::PNM_Linux => vec![2], - Distro::Pop__OS => vec![2], - Distro::Reborn_OS => vec![1], - Distro::SalentOS => vec![4], - Distro::Septor => 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::uwuntu => vec![2], - Distro::Vanilla => vec![2], - Distro::VNux => vec![3, 5], - Distro::Void => vec![2], - Distro::Xray_OS => vec![2, 3], - Distro::Xubuntu => vec![2], - _ => Vec::new(), - }; - - fg.into_iter() - .map(|fore| { - fore.try_into() - .expect("`fore` should be a valid neofetch color index") - }) + distro.foreground() + .iter() + .map(|&f| f.try_into().expect("neofetch color index should be valid")) .collect() }