diff --git a/Cargo.lock b/Cargo.lock index f2f2e6f2..e09f906b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + [[package]] name = "anyhow" version = "1.0.86" @@ -49,11 +58,13 @@ dependencies = [ "anyhow", "bpaf", "indexmap", + "regex", "rgb", "shell-words", "strum", "tracing", "tracing-subscriber", + "unicode-normalization", ] [[package]] @@ -84,6 +95,12 @@ version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -136,6 +153,35 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "regex" +version = "1.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" + [[package]] name = "rgb" version = "0.8.37" @@ -221,6 +267,21 @@ dependencies = [ "once_cell", ] +[[package]] +name = "tinyvec" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c55115c6fbe2d2bef26eb09ad74bde02d8255476fc0c7b515ef09fbb35742d82" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "tracing" version = "0.1.40" @@ -284,6 +345,15 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "unicode-normalization" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +dependencies = [ + "tinyvec", +] + [[package]] name = "valuable" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 2c7e9d79..d36a6712 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,8 +17,10 @@ anyhow = { version = "1.0.86", default-features = false } bpaf = { version = "0.9.12", default-features = false } derive_more = { version = "1.0.0-beta.6", default-features = false } indexmap = { version = "2.2.6", default-features = false } +regex = { version = "1.10.5", default-features = false } rgb = { version = "0.8.37", default-features = false } shell-words = { version = "1.1.0", default-features = false } strum = { version = "0.26.3", default-features = false } tracing = { version = "0.1.40", default-features = false } tracing-subscriber = { version = "0.3.18", default-features = false } +unicode-normalization = { version = "0.1.23", default-features = false } diff --git a/crates/hyfetch/Cargo.toml b/crates/hyfetch/Cargo.toml index 5490ad66..2ff282ae 100644 --- a/crates/hyfetch/Cargo.toml +++ b/crates/hyfetch/Cargo.toml @@ -20,6 +20,11 @@ strum = { workspace = true, features = ["derive", "std"] } tracing = { workspace = true, features = ["attributes", "std"] } tracing-subscriber = { workspace = true, features = ["ansi", "fmt", "smallvec", "std", "tracing-log"] } +[build-dependencies] +indexmap = { workspace = true, features = ["std"] } +regex = { workspace = true, features = ["perf", "std", "unicode"] } +unicode-normalization = { workspace = true, features = ["std"] } + [features] default = ["autocomplete", "color"] autocomplete = ["bpaf/autocomplete"] diff --git a/crates/hyfetch/build.rs b/crates/hyfetch/build.rs new file mode 100644 index 00000000..b67fd0a5 --- /dev/null +++ b/crates/hyfetch/build.rs @@ -0,0 +1,173 @@ +use std::path::Path; +use std::{env, fs}; + +use indexmap::IndexMap; +use regex::Regex; +use unicode_normalization::UnicodeNormalization; + +#[derive(Debug)] +struct AsciiDistro { + pattern: String, + art: String, +} + +impl AsciiDistro { + fn friendly_name(&self) -> String { + self.pattern + .split('|') + .next() + .expect("invalid distro pattern") + .trim_matches(|c: char| c.is_ascii_punctuation() || c == ' ') + .replace(['"', '*'], "") + } +} + +fn main() { + let neofetch_path = Path::new(env!("CARGO_WORKSPACE_DIR")).join("neofetch"); + + println!("cargo:rerun-if-changed={}", neofetch_path.display()); + + let out_dir = env::var("OUT_DIR").unwrap(); + let out_path = Path::new(&out_dir); + + export_distros(neofetch_path, out_path); +} + +fn export_distros

(neofetch_path: P, out_path: &Path) +where + P: AsRef, +{ + let distros = parse_ascii_distros(neofetch_path); + let mut variants = IndexMap::with_capacity(distros.len()); + + for distro in &distros { + let variant = distro + .friendly_name() + .replace(|c: char| c.is_ascii_punctuation() || c == ' ', "_") + .nfc() + .collect::(); + variants.insert(variant, distro); + } + + let mut buf = " +#[derive(Clone, Eq, PartialEq, Hash, Debug)] +pub enum Distro { +" + .to_owned(); + + for (variant, distro) in &variants { + buf.push_str(&format!( + " + // {}) + {variant}, +", + distro.pattern + )); + } + + buf.push_str( + " +} +", + ); + + buf.push_str( + " +impl Distro { + pub fn ascii_art(&self) -> &str { + let art = match self { +", + ); + + let quotes = "#".repeat(80); + for (variant, distro) in &variants { + buf.push_str(&format!( + " + Self::{variant} => r{quotes}\" +{} +\"{quotes}, +", + distro.art + )); + } + + buf.push_str( + " + }; + &art[1..(art.len() - 1)] + } +} +", + ); + + fs::write(out_path.join("distros.rs"), buf).expect("couldn't write distros.rs"); +} + +/// Parses ascii distros from neofetch script. +fn parse_ascii_distros

(neofetch_path: P) -> Vec +where + P: AsRef, +{ + let neofetch_path = neofetch_path.as_ref(); + + let nf = { + let nf = fs::read_to_string(neofetch_path).expect("couldn't read neofetch script"); + + // Get the content of "get_distro_ascii" function + let (_, nf) = nf + .split_once("get_distro_ascii() {\n") + .expect("couldn't find get_distro_ascii function"); + let (nf, _) = nf + .split_once("\n}\n") + .expect("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"); + } + nf + }; + + let case_re = Regex::new(r"case .*? in\n").expect("couldn't compile case regex"); + let eof_re = Regex::new(r"EOF[ \n]*?;;").expect("couldn't compile eof regex"); + + // Split by blocks + let mut blocks = vec![]; + 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(')'))?; + + Some(AsciiDistro { + pattern: pattern.to_owned(), + art: art.to_owned(), + }) + } + blocks + .iter() + .filter_map(|block| parse_block(block)) + .collect() +} diff --git a/crates/hyfetch/src/distros.rs b/crates/hyfetch/src/distros.rs new file mode 100644 index 00000000..a41e9ea8 --- /dev/null +++ b/crates/hyfetch/src/distros.rs @@ -0,0 +1,3 @@ +#![allow(non_camel_case_types)] + +include!(concat!(env!("OUT_DIR"), "/distros.rs")); diff --git a/crates/hyfetch/src/lib.rs b/crates/hyfetch/src/lib.rs index 579ff006..6a1fcfb8 100644 --- a/crates/hyfetch/src/lib.rs +++ b/crates/hyfetch/src/lib.rs @@ -1,5 +1,6 @@ pub mod cli_options; pub mod color_util; +pub mod distros; pub mod neofetch_util; pub mod presets; pub mod types;