diff --git a/Cargo.lock b/Cargo.lock index c63011a..33e9ff2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -757,12 +757,22 @@ dependencies = [ "globset", "libmacchina", "pfetch-extractor", + "pfetch-logo-parser", "which", ] [[package]] name = "pfetch-extractor" version = "0.1.5" +dependencies = [ + "pfetch-logo-parser", + "proc-macro2", + "quote", +] + +[[package]] +name = "pfetch-logo-parser" +version = "0.1.0" dependencies = [ "proc-macro2", "quote", @@ -827,9 +837,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.8.1" +version = "1.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" +checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f" dependencies = [ "aho-corasick 1.0.1", "memchr", @@ -838,9 +848,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" +checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" [[package]] name = "rpm-pkg-count" diff --git a/Cargo.toml b/Cargo.toml index a6de44a..6693bca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["pfetch-extractor"] +members = ["pfetch-extractor", "pfetch-logo-parser"] [package] name = "pfetch" @@ -15,6 +15,7 @@ categories = ["command-line-utilities"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +pfetch-logo-parser = { path = "./pfetch-logo-parser", version = "0.1.0" } pfetch-extractor = { path = "./pfetch-extractor", version = "0.1.5" } globset = "0.4.10" dotenvy = "0.15.6" diff --git a/pfetch-extractor/Cargo.toml b/pfetch-extractor/Cargo.toml index 5e6c423..69c32c0 100644 --- a/pfetch-extractor/Cargo.toml +++ b/pfetch-extractor/Cargo.toml @@ -1,11 +1,11 @@ [package] name = "pfetch-extractor" version = "0.1.5" -edition = "2021" authors = ["Gobidev"] -description = "A rust proc-macro to extract pfetch logos at compile time" -license = "MIT" +edition = "2021" keywords = ["pfetch"] +license = "MIT" +description = "A rust proc-macro to extract pfetch logos at compile time" [lib] proc-macro = true @@ -13,6 +13,6 @@ proc-macro = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +pfetch-logo-parser = { version = "0.1.0", path = "../pfetch-logo-parser", features = ["proc-macro"] } proc-macro2 = "1.0.50" quote = "1.0.23" -regex = "1.7.1" diff --git a/pfetch-extractor/src/lib.rs b/pfetch-extractor/src/lib.rs index 590047c..69a1250 100644 --- a/pfetch-extractor/src/lib.rs +++ b/pfetch-extractor/src/lib.rs @@ -1,6 +1,5 @@ use proc_macro::TokenStream; use quote::quote; -use regex::Regex; #[proc_macro] pub fn parse_logos(_input: TokenStream) -> TokenStream { @@ -18,7 +17,7 @@ pub fn parse_logos(_input: TokenStream) -> TokenStream { let logos = raw_logos .split(";;\n") .filter_map(|raw_logo| { - let (is_tux, logo) = parse_logo(raw_logo)?; + let (is_tux, logo) = pfetch_logo_parser::parse_logo(raw_logo)?; if is_tux { tux = Some(logo.clone()); } @@ -30,69 +29,3 @@ pub fn parse_logos(_input: TokenStream) -> TokenStream { quote! { (#tux, [#(#logos),*]) }.into() } - -fn parse_logo(input: &str) -> Option<(bool, proc_macro2::TokenStream)> { - let input = input.trim().replace('\t', ""); - if input.is_empty() { - return None; - } - let regex = Regex::new(r"^\(?(.*)\)[\s\S]*read_ascii *(\d)? *(\d)?").unwrap(); - - let groups = regex - .captures(&input) - .expect("Error while parsing logos.sh"); - - let pattern = &groups[1]; - let primary_color = match groups.get(2) { - Some(color) => color.as_str().parse::().unwrap(), - None => 7, - }; - let secondary_color = match groups.get(3) { - Some(color) => color.as_str().parse::().unwrap(), - None => (primary_color + 1) % 8, - }; - - let logo = input - .split_once("EOF\n") - .expect("Could not find start of logo") - .1 - .split_once("\nEOF") - .expect("Could not find end of logo") - .0; - - let mut logo_parts = vec![]; - for logo_part in logo.split("${") { - if let Some((new_color, rest)) = logo_part.split_once('}') { - let new_color: u8 = new_color - .get(1..) - .and_then(|num| num.parse().ok()) - .unwrap_or_else(|| panic!("Invalid color: {new_color}")); - let rest = rest.replace("\\\\", "\\"); - let rest = rest.replace("\\`", "`"); - let lines = rest.split('\n').collect::>(); - let last_index = lines.len() - 1; - for (index, line) in lines.into_iter().enumerate() { - let mut line = line.to_owned(); - if index != last_index { - line += "\n"; - } - logo_parts.push(quote! { (Color(Some(#new_color)), #line) }); - } - } else if !logo_part.is_empty() { - let logo_part = logo_part.replace("\\\\", "\\"); - logo_parts.push(quote! { (Color(None), #logo_part) }); - } - } - - Some(( - pattern == "[Ll]inux*", - quote! { - Logo { - primary_color: Color(Some(#primary_color)), - secondary_color: Color(Some(#secondary_color)), - pattern: #pattern, - logo_parts: &[#(#logo_parts),*], - } - }, - )) -} diff --git a/pfetch-logo-parser/Cargo.toml b/pfetch-logo-parser/Cargo.toml new file mode 100644 index 0000000..17e1741 --- /dev/null +++ b/pfetch-logo-parser/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "pfetch-logo-parser" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[features] +proc-macro = ["dep:proc-macro2", "dep:quote"] + +[dependencies] +regex = "1.8.4" +proc-macro2 = { version = "1.0.50", optional = true } +quote = { version = "1.0.23", optional = true } diff --git a/pfetch-logo-parser/src/lib.rs b/pfetch-logo-parser/src/lib.rs new file mode 100644 index 0000000..801361c --- /dev/null +++ b/pfetch-logo-parser/src/lib.rs @@ -0,0 +1,170 @@ +use regex::Regex; + +use std::{borrow::Cow, fmt::Display, str::FromStr}; + +#[cfg(feature = "proc-macro")] +use proc_macro2::TokenStream; +#[cfg(feature = "proc-macro")] +use quote::{quote, ToTokens, TokenStreamExt}; + +#[derive(Clone, Copy, Debug)] +pub struct Color(pub Option); + +#[cfg(feature = "proc-macro")] +impl ToTokens for Color { + fn to_tokens(&self, tokens: &mut TokenStream) { + let value = match &self.0 { + Some(val) => quote! { Some(#val) }, + None => quote! { None }, + }; + tokens.append_all(quote! { + ::pfetch_logo_parser::Color(#value) + }); + } +} + +impl Display for Color { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self.0 { + Some(color @ 0..=7) => write!(f, "\x1b[3{color}m"), + Some(color) => write!(f, "\x1b[38;5;{color}m"), + None => write!(f, "\x1b[39m"), + } + } +} + +impl FromStr for Color { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + Ok(Color(s.parse::().ok())) + } +} + +#[derive(Clone, Debug)] +pub struct LogoPart { + pub color: Color, + pub content: Cow<'static, str>, +} + +#[cfg(feature = "proc-macro")] +impl ToTokens for LogoPart { + fn to_tokens(&self, tokens: &mut TokenStream) { + let color = &self.color; + let content = &self.content; + tokens.append_all(quote! { + ::pfetch_logo_parser::LogoPart { + color: #color, + content: ::std::borrow::Cow::Borrowed(#content), + } + }); + } +} + +#[derive(Clone, Debug)] +pub struct Logo { + pub primary_color: Color, + pub secondary_color: Color, + pub pattern: Cow<'static, str>, + pub logo_parts: Cow<'static, [LogoPart]>, +} + +#[cfg(feature = "proc-macro")] +impl ToTokens for Logo { + fn to_tokens(&self, tokens: &mut TokenStream) { + let primary_color = &self.primary_color; + let secondary_color = &self.secondary_color; + let pattern = &self.pattern; + let logo_parts = &self.logo_parts; + + tokens.append_all(quote! { + ::pfetch_logo_parser::Logo { + primary_color: #primary_color, + secondary_color: #secondary_color, + pattern: ::std::borrow::Cow::Borrowed(#pattern), + logo_parts: ::std::borrow::Cow::Borrowed(&[#(#logo_parts),*]), + } + }); + } +} + +impl Display for Logo { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}", + self.logo_parts + .iter() + .map(|LogoPart { color, content }| format!("{color}{content}")) + .collect::() + ) + } +} + +/// Parses a logo in pfetch formant and returns wether it is the linux (tux) logo and the logo itself +pub fn parse_logo(input: &str) -> Option<(bool, Logo)> { + let input = input.trim().replace('\t', ""); + if input.is_empty() { + return None; + } + let regex = Regex::new(r"^\(?(.*)\)[\s\S]*read_ascii *(\d)? *(\d)?").unwrap(); + + let groups = regex.captures(&input).expect("Error while parsing logo"); + + let pattern = &groups[1]; + let primary_color = match groups.get(2) { + Some(color) => color.as_str().parse::().unwrap(), + None => 7, + }; + let secondary_color = match groups.get(3) { + Some(color) => color.as_str().parse::().unwrap(), + None => (primary_color + 1) % 8, + }; + let logo = input + .split_once("EOF\n") + .expect("Could not find start of logo, make sure to include the `<<- EOF` and to use tabs for indentation") + .1 + .split_once("\nEOF") + .expect("Could not find end of logo, make sure to include the closing EOF and to use tabs for indentation") + .0; + + let mut logo_parts = vec![]; + for logo_part in logo.split("${") { + if let Some((new_color, rest)) = logo_part.split_once('}') { + let new_color: u8 = new_color + .get(1..) + .and_then(|num| num.parse().ok()) + .unwrap_or_else(|| panic!("Invalid color: {new_color}")); + let rest = rest.replace("\\\\", "\\"); + let rest = rest.replace("\\`", "`"); + let lines = rest.split('\n').collect::>(); + let last_index = lines.len() - 1; + for (index, line) in lines.into_iter().enumerate() { + let mut line = line.to_owned(); + if index != last_index { + line += "\n"; + } + logo_parts.push(LogoPart { + color: Color(Some(new_color)), + content: line.into(), + }); + } + } else if !logo_part.is_empty() { + let logo_part = logo_part.replace("\\\\", "\\"); + logo_parts.push(LogoPart { + color: Color(None), + content: logo_part.into(), + }); + } + } + + Some(( + pattern == "[Ll]inux*", + Logo { + primary_color: Color(Some(primary_color)), + secondary_color: Color(Some(secondary_color)), + pattern: pattern.to_owned().into(), + logo_parts: logo_parts.into(), + }, + )) +} diff --git a/src/lib.rs b/src/lib.rs index ecdbeb4..8abcc3e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ -use std::{env, fmt::Display, fs, io::Result, process::Command, str::FromStr}; +use std::{env, fs, io::Result, process::Command}; use glob::glob; use globset::Glob; @@ -6,48 +6,7 @@ use libmacchina::{ traits::GeneralReadout as _, traits::KernelReadout as _, traits::MemoryReadout as _, traits::PackageReadout as _, GeneralReadout, KernelReadout, MemoryReadout, PackageReadout, }; -use pfetch_extractor::parse_logos; - -#[derive(Clone, Copy, Debug)] -pub struct Color(pub Option); - -impl Display for Color { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self.0 { - Some(color @ 0..=7) => write!(f, "\x1b[3{color}m"), - Some(color) => write!(f, "\x1b[38;5;{color}m"), - None => write!(f, "\x1b[39m"), - } - } -} - -impl FromStr for Color { - type Err = String; - - fn from_str(s: &str) -> std::result::Result { - Ok(Color(s.parse::().ok())) - } -} - -pub struct Logo { - pub primary_color: Color, - pub secondary_color: Color, - pub pattern: &'static str, - pub logo_parts: &'static [(Color, &'static str)], -} - -impl Display for Logo { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "{}", - self.logo_parts - .iter() - .map(|(color, part)| format!("{color}{part}")) - .collect::() - ) - } -} +use pfetch_logo_parser::Logo; #[derive(Debug)] pub enum PackageManager { @@ -353,7 +312,7 @@ pub fn host(general_readout: &GeneralReadout) -> Option { } pub fn logo(logo_name: &str) -> Logo { - let (tux, logos) = parse_logos!(); + let (tux, logos) = pfetch_extractor::parse_logos!(); logos .into_iter() .find(|logo| { diff --git a/src/main.rs b/src/main.rs index 35211f4..437b484 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,6 +2,7 @@ use libmacchina::{ traits::GeneralReadout as _, traits::KernelReadout as _, traits::MemoryReadout as _, traits::PackageReadout as _, GeneralReadout, KernelReadout, MemoryReadout, PackageReadout, }; +use pfetch_logo_parser::{Color, Logo, LogoPart}; use std::{env, fmt::Display, str::FromStr}; #[derive(Debug, PartialEq)] @@ -53,11 +54,11 @@ impl FromStr for PfetchInfo { } } -fn pfetch(info: Vec<(pfetch::Color, String, String)>, logo: pfetch::Logo, logo_enabled: bool) { +fn pfetch(info: Vec<(Color, String, String)>, logo: Logo, logo_enabled: bool) { let raw_logo = if logo_enabled { logo.logo_parts .iter() - .map(|(_, part)| *part) + .map(|LogoPart { content, .. }| content.as_ref()) .collect::() } else { "".to_string() @@ -126,7 +127,7 @@ fn pfetch(info: Vec<(pfetch::Color, String, String)>, logo: pfetch::Logo, logo_e ), color2 = match dotenvy::var("PF_COL2") { Ok(newcolor) => { - match pfetch::Color::from_str(&newcolor) { + match Color::from_str(&newcolor) { Ok(newcolor) => format!("{newcolor}"), Err(_) => "".to_string(), } @@ -266,7 +267,7 @@ fn main() { // color overrides if let Ok(newcolor) = dotenvy::var("PF_COL1") { - if let Ok(newcolor) = pfetch::Color::from_str(&newcolor) { + if let Ok(newcolor) = Color::from_str(&newcolor) { logo.primary_color = newcolor; } } @@ -274,12 +275,12 @@ fn main() { if let Ok(newcolor) = dotenvy::var("PF_COL3") { if newcolor == "COL1" { logo.secondary_color = logo.primary_color; - } else if let Ok(newcolor) = pfetch::Color::from_str(&newcolor) { + } else if let Ok(newcolor) = Color::from_str(&newcolor) { logo.secondary_color = newcolor; } } - let gathered_pfetch_info: Vec<(pfetch::Color, String, String)> = enabled_pf_info + let gathered_pfetch_info: Vec<(Color, String, String)> = enabled_pf_info .iter() .filter_map(|info| match info { PfetchInfo::Os => Some((logo.primary_color, info.to_string(), os.clone())),