From d11f6f0a9f4308c6da8112df65cf1434e5814fdf Mon Sep 17 00:00:00 2001 From: Teoh Han Hui Date: Sat, 13 Jul 2024 04:04:14 +0800 Subject: [PATCH] Use selected backend to get distro name --- crates/hyfetch/src/bin/hyfetch.rs | 85 +++++++++++++-------- crates/hyfetch/src/color_util.rs | 10 +-- crates/hyfetch/src/models.rs | 12 +-- crates/hyfetch/src/neofetch_util.rs | 114 ++++++++++++++++++++-------- crates/hyfetch/src/presets.rs | 14 ++-- crates/hyfetch/src/types.rs | 2 +- 6 files changed, 154 insertions(+), 83 deletions(-) diff --git a/crates/hyfetch/src/bin/hyfetch.rs b/crates/hyfetch/src/bin/hyfetch.rs index adc6ba7b..758741bd 100644 --- a/crates/hyfetch/src/bin/hyfetch.rs +++ b/crates/hyfetch/src/bin/hyfetch.rs @@ -12,7 +12,7 @@ use hyfetch::models::Config; use hyfetch::neofetch_util::ensure_git_bash; use hyfetch::neofetch_util::{self, ascii_size, get_distro_ascii, literal_input, ColorAlignment}; use hyfetch::presets::{AssignLightness, ColorProfile, Preset}; -use hyfetch::types::{AnsiMode, LightDark}; +use hyfetch::types::{AnsiMode, Backend, TerminalTheme}; use hyfetch::utils::get_cache_path; use palette::Srgb; use strum::{EnumCount, VariantArray, VariantNames}; @@ -28,34 +28,54 @@ fn main() -> Result<()> { let options = options().run(); - init_tracing_subsriber(options.debug).context("failed to init tracing subscriber")?; + let debug_mode = options.debug; + + init_tracing_subsriber(debug_mode).context("failed to init tracing subscriber")?; debug!(?options, "CLI options"); // Use a custom distro let distro = options.distro.as_ref(); + let backend = options.backend.unwrap_or(Backend::Neofetch); + let use_overlay = options.overlay; + #[cfg(windows)] ensure_git_bash().context("failed to find git bash")?; if options.test_print { - let (asc, _) = get_distro_ascii(distro).context("failed to get distro ascii")?; + let (asc, _) = get_distro_ascii(distro, backend).context("failed to get distro ascii")?; println!("{asc}"); return Ok(()); } let config = if options.config { - create_config(distro, &options.config_file, options.overlay, options.debug) - .context("failed to create config")? + create_config( + &options.config_file, + distro, + backend, + use_overlay, + debug_mode, + ) + .context("failed to create config")? } else if let Some(config) = read_config(&options.config_file).context("failed to read config")? { config } else { - create_config(distro, &options.config_file, options.overlay, options.debug) - .context("failed to create config")? + create_config( + &options.config_file, + distro, + backend, + use_overlay, + debug_mode, + ) + .context("failed to create config")? }; + let color_mode = options.mode.unwrap_or(config.mode); + let theme = config.light_dark; + // Check if it's June (pride month) let now = OffsetDateTime::now_local().context("failed to get current datetime in local timezone")?; @@ -83,7 +103,6 @@ fn main() -> Result<()> { // Use a custom distro let distro = options.distro.as_ref().or(config.distro.as_ref()); - let color_mode = options.mode.unwrap_or(config.mode); let backend = options.backend.unwrap_or(config.backend); let args = options.args.as_ref().or(config.args.as_ref()); @@ -98,7 +117,7 @@ fn main() -> Result<()> { } else if let Some(lightness) = options.lightness { color_profile.with_lightness(AssignLightness::Replace(lightness)) } else { - color_profile.with_lightness_dl(config.lightness(), config.light_dark, options.overlay) + color_profile.with_lightness_adaptive(config.lightness(), theme, use_overlay) }; debug!(?color_profile, "lightened color profile"); @@ -109,7 +128,7 @@ fn main() -> Result<()> { None, ) } else { - get_distro_ascii(distro).context("failed to get distro ascii")? + get_distro_ascii(distro, backend).context("failed to get distro ascii")? }; let color_align = if fore_back.is_some() { match config.color_align { @@ -124,7 +143,7 @@ fn main() -> Result<()> { config.color_align }; let asc = color_align - .recolor_ascii(asc, color_profile, color_mode, config.light_dark) + .recolor_ascii(asc, color_profile, color_mode, theme) .context("failed to recolor ascii")?; neofetch_util::run(asc, backend, args)?; @@ -174,8 +193,9 @@ fn read_config(path: &Path) -> Result> { /// The config is automatically stored to file. #[tracing::instrument(level = "debug")] fn create_config( - distro: Option<&String>, path: &Path, + distro: Option<&String>, + backend: Backend, use_overlay: bool, debug_mode: bool, ) -> Result { @@ -202,14 +222,15 @@ fn create_config( }); debug!(?det_ansi, "detected color mode"); - let (asc, fore_back) = get_distro_ascii(distro).context("failed to get distro ascii")?; + let (asc, fore_back) = + get_distro_ascii(distro, backend).context("failed to get distro ascii")?; let (asc_width, asc_lines) = ascii_size(asc); - let theme = det_bg.map(|bg| bg.theme()).unwrap_or(LightDark::Light); + let theme = det_bg.map(|bg| bg.theme()).unwrap_or(TerminalTheme::Light); let color_mode = det_ansi.unwrap_or(AnsiMode::Ansi256); let logo = color( match theme { - LightDark::Light => "&l&bhyfetch&~&L", - LightDark::Dark => "&l&bhy&ffetch&~&L", + TerminalTheme::Light => "&l&bhyfetch&~&L", + TerminalTheme::Dark => "&l&bhy&ffetch&~&L", }, color_mode, ) @@ -280,9 +301,9 @@ fn create_config( }; ////////////////////////////// - // 2. Select light/dark mode + // 2. Select theme (light/dark mode) - let select_light_dark = || -> Result<(LightDark, &str)> { + let select_theme = || -> Result<(TerminalTheme, &str)> { if let Some(det_bg) = det_bg { return Ok((det_bg.theme(), "Detected background color")); } @@ -294,11 +315,10 @@ fn create_config( }; let theme = { - let (light_dark, ttl) = - select_light_dark().context("failed to select light / dark mode")?; - debug!(?light_dark, "selected theme"); - update_title(&mut title, &mut option_counter, ttl, light_dark.into()); - light_dark + let (selected_theme, ttl) = select_theme().context("failed to select theme")?; + debug!(?selected_theme, "selected theme"); + update_title(&mut title, &mut option_counter, ttl, selected_theme.into()); + selected_theme }; ////////////////////////////// @@ -400,7 +420,7 @@ fn create_config( let color_profile: ColorProfile; let preset_rainbow = Preset::Rainbow .color_profile() - .with_lightness_dl(Config::default_lightness(theme), theme, use_overlay) + .with_lightness_adaptive(Config::default_lightness(theme), theme, use_overlay) .color_text( "preset", color_mode, @@ -421,7 +441,7 @@ fn create_config( opts.push("prev"); } println!("Enter 'next' to go to the next page and 'prev' to go to the previous page."); - let preset = literal_input( + let selection = literal_input( format!("Which {preset_rainbow} do you want to use? "), &opts[..], Preset::Rainbow.into(), @@ -429,18 +449,19 @@ fn create_config( color_mode, ) .context("failed to select preset")?; - if preset == "next" { + if selection == "next" { page += 1; - } else if preset == "prev" { + } else if selection == "prev" { page -= 1; } else { - let preset: Preset = preset.parse().expect("selected preset should be valid"); - debug!(?preset, "selected preset"); - color_profile = preset.color_profile(); + let selected_preset: Preset = + selection.parse().expect("selected preset should be valid"); + debug!(?selected_preset, "selected preset"); + color_profile = selected_preset.color_profile(); { - let preset_name: &'static str = preset.into(); + let preset_name: &'static str = selected_preset.into(); let preset_colored_name = color_profile - .with_lightness_dl(Config::default_lightness(theme), theme, use_overlay) + .with_lightness_adaptive(Config::default_lightness(theme), theme, use_overlay) .color_text( preset_name, color_mode, diff --git a/crates/hyfetch/src/color_util.rs b/crates/hyfetch/src/color_util.rs index 17c2dee2..311e74e6 100644 --- a/crates/hyfetch/src/color_util.rs +++ b/crates/hyfetch/src/color_util.rs @@ -11,7 +11,7 @@ use palette::{IntoColorMut, LinSrgb, Okhsl, Srgb}; use serde::{Deserialize, Serialize}; use thiserror::Error; -use crate::types::{AnsiMode, LightDark}; +use crate::types::{AnsiMode, TerminalTheme}; const MINECRAFT_COLORS: [(&str, &str); 30] = [ // Minecraft formatting codes @@ -114,7 +114,7 @@ pub trait ToAnsiString { } pub trait Theme { - fn theme(&self) -> LightDark; + fn theme(&self) -> TerminalTheme; } impl Lightness { @@ -227,16 +227,16 @@ impl ToAnsiString for Srgb { } impl Theme for Srgb { - fn theme(&self) -> LightDark { + fn theme(&self) -> TerminalTheme { let mut rgb_f32_color: LinSrgb = self.into_linear(); { let okhsl_f32_color: &mut Okhsl = &mut rgb_f32_color.into_color_mut(); if okhsl_f32_color.lightness > 0.5 { - LightDark::Light + TerminalTheme::Light } else { - LightDark::Dark + TerminalTheme::Dark } } } diff --git a/crates/hyfetch/src/models.rs b/crates/hyfetch/src/models.rs index 63c67c6f..d43929d0 100644 --- a/crates/hyfetch/src/models.rs +++ b/crates/hyfetch/src/models.rs @@ -3,13 +3,13 @@ use serde::{Deserialize, Serialize}; use crate::color_util::Lightness; use crate::neofetch_util::ColorAlignment; use crate::presets::Preset; -use crate::types::{AnsiMode, Backend, LightDark}; +use crate::types::{AnsiMode, Backend, TerminalTheme}; #[derive(Clone, Debug, Deserialize, Serialize)] pub struct Config { pub preset: Preset, pub mode: AnsiMode, - pub light_dark: LightDark, + pub light_dark: TerminalTheme, lightness: Option, pub color_align: ColorAlignment, pub backend: Backend, @@ -21,12 +21,12 @@ pub struct Config { } impl Config { - pub fn default_lightness(term: LightDark) -> Lightness { - match term { - LightDark::Dark => { + pub fn default_lightness(theme: TerminalTheme) -> Lightness { + match theme { + TerminalTheme::Dark => { Lightness::new(0.65).expect("default lightness should not be invalid") }, - LightDark::Light => { + TerminalTheme::Light => { Lightness::new(0.4).expect("default lightness should not be invalid") }, } diff --git a/crates/hyfetch/src/neofetch_util.rs b/crates/hyfetch/src/neofetch_util.rs index fba8f9b3..3fe11ec0 100644 --- a/crates/hyfetch/src/neofetch_util.rs +++ b/crates/hyfetch/src/neofetch_util.rs @@ -22,7 +22,7 @@ use crate::color_util::{ }; use crate::distros::Distro; use crate::presets::ColorProfile; -use crate::types::{AnsiMode, Backend, LightDark}; +use crate::types::{AnsiMode, Backend, TerminalTheme}; use crate::utils::{find_file, find_in_path, process_command_status}; const NEOFETCH_COLOR_PATTERNS: [&str; 6] = ["${c1}", "${c2}", "${c3}", "${c4}", "${c5}", "${c6}"]; @@ -84,7 +84,7 @@ impl ColorAlignment { asc: String, color_profile: ColorProfile, color_mode: AnsiMode, - term: LightDark, + theme: TerminalTheme, ) -> Result { let reset = color("&~&*", color_mode).expect("color reset should not be invalid"); @@ -105,9 +105,9 @@ impl ColorAlignment { let asc = asc.replace( &format!("${{c{fore}}}"), &color( - match term { - LightDark::Light => "&0", - LightDark::Dark => "&f", + match theme { + TerminalTheme::Light => "&0", + TerminalTheme::Dark => "&f", }, color_mode, ) @@ -466,14 +466,17 @@ 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<(String, Option)> +pub fn get_distro_ascii( + distro: Option, + backend: Backend, +) -> Result<(String, Option)> where S: AsRef + fmt::Debug, { let distro: Cow<_> = if let Some(distro) = distro.as_ref() { distro.as_ref().into() } else { - get_distro_name() + get_distro_name(backend) .context("failed to get distro name")? .into() }; @@ -500,6 +503,7 @@ where Ok((normalize_ascii(asc), None)) } +#[tracing::instrument(level = "debug", skip(asc))] pub fn run(asc: String, backend: Backend, args: Option<&Vec>) -> Result<()> { match backend { Backend::Neofetch => { @@ -653,10 +657,60 @@ where } } +/// Runs fastfetch command, returning the piped stdout output. +fn run_fastfetch_command_piped(args: &[S]) -> Result +where + S: AsRef + fmt::Debug, +{ + let mut command = make_fastfetch_command(args)?; + + let output = command + .output() + .context("failed to execute fastfetch as child process")?; + debug!(?output, "fastfetch output"); + process_command_status(&output.status).context("fastfetch command exited with error")?; + + let out = String::from_utf8(output.stdout) + .context("failed to process fastfetch output as it contains invalid UTF-8")? + .trim() + .to_owned(); + Ok(out) +} + +fn make_fastfetch_command(args: &[S]) -> Result +where + S: AsRef, +{ + // Find fastfetch binary + let fastfetch_path = fastfetch_path().context("failed to get fastfetch path")?; + let fastfetch_path = fastfetch_path.context("fastfetch command not found")?; + + debug!(?fastfetch_path, "fastfetch path"); + + let mut command = Command::new(fastfetch_path); + command.args(args); + Ok(command) +} + #[tracing::instrument(level = "debug")] -fn get_distro_name() -> Result { - run_neofetch_command_piped(&["ascii_distro_name"]) - .context("failed to get distro name from neofetch") +fn get_distro_name(backend: Backend) -> Result { + match backend { + Backend::Neofetch => run_neofetch_command_piped(&["ascii_distro_name"]) + .context("failed to get distro name from neofetch"), + Backend::Fastfetch | Backend::FastfetchOld => run_fastfetch_command_piped(&[ + "--logo", + "none", + "-s", + "OS", + "--disable-linewrap", + "--os-key", + " ", + ]) + .context("failed to get distro name from fastfetch"), + Backend::Qwqfetch => { + todo!() + }, + } } /// Runs neofetch with colors. @@ -676,21 +730,18 @@ fn run_neofetch(asc: String, args: Option<&Vec>) -> Result<()> { // Call neofetch with the temp file let temp_file_path = temp_file.into_temp_path(); let args = { - let mut v = vec![ - "--ascii", - "--source", - temp_file_path - .to_str() - .expect("temp file path should not contain invalid UTF-8"), - "--ascii-colors", + let mut v: Vec> = vec![ + OsStr::new("--ascii").into(), + OsStr::new("--source").into(), + OsStr::new(&temp_file_path).into(), + OsStr::new("--ascii-colors").into(), ]; if let Some(args) = args { - let args: Vec<_> = args.iter().map(|s| &**s).collect(); - v.extend(args); + v.extend(args.iter().map(|arg| OsStr::new(arg).into())); } v }; - let mut command = make_neofetch_command(&args)?; + let mut command = make_neofetch_command(&args[..])?; debug!(?command, "neofetch command"); @@ -761,12 +812,6 @@ fn fastfetch_path() -> Result> { /// Runs fastfetch with colors. #[tracing::instrument(level = "debug", skip(asc))] fn run_fastfetch(asc: String, args: Option<&Vec>, legacy: bool) -> Result<()> { - // Find fastfetch binary - let fastfetch_path = fastfetch_path().context("failed to get fastfetch path")?; - let fastfetch_path = fastfetch_path.context("fastfetch command not found")?; - - debug!(?fastfetch_path, "fastfetch path"); - // Write temp file let mut temp_file = NamedTempFile::with_prefix("ascii.txt").context("failed to create temp file for ascii")?; @@ -776,12 +821,17 @@ fn run_fastfetch(asc: String, args: Option<&Vec>, legacy: bool) -> Resul // Call fastfetch with the temp file let temp_file_path = temp_file.into_temp_path(); - let mut command = Command::new(fastfetch_path); - command.arg(if legacy { "--raw" } else { "--file-raw" }); - command.arg(&temp_file_path); - if let Some(args) = args { - command.args(args); - } + let args = { + let mut v: Vec> = vec![ + OsStr::new(if legacy { "--raw" } else { "--file-raw" }).into(), + OsStr::new(&temp_file_path).into(), + ]; + if let Some(args) = args { + v.extend(args.iter().map(|arg| OsStr::new(arg).into())); + } + v + }; + let mut command = make_fastfetch_command(&args[..])?; debug!(?command, "fastfetch command"); diff --git a/crates/hyfetch/src/presets.rs b/crates/hyfetch/src/presets.rs index ecabc60d..a20d06f9 100644 --- a/crates/hyfetch/src/presets.rs +++ b/crates/hyfetch/src/presets.rs @@ -10,7 +10,7 @@ use tracing::debug; use unicode_segmentation::UnicodeSegmentation; use crate::color_util::{ForegroundBackground, Lightness, ToAnsiString}; -use crate::types::{AnsiMode, LightDark}; +use crate::types::{AnsiMode, TerminalTheme}; #[derive( Copy, @@ -639,20 +639,20 @@ impl ColorProfile { } /// Creates a new color profile, with the colors set to the specified - /// [`Okhsl`] lightness value, with respect to dark/light terminals. - pub fn with_lightness_dl( + /// [`Okhsl`] lightness value, adapted to the terminal theme. + pub fn with_lightness_adaptive( &self, lightness: Lightness, - term: LightDark, + theme: TerminalTheme, use_overlay: bool, ) -> Self { if use_overlay { todo!() } - match term { - LightDark::Dark => self.with_lightness(AssignLightness::ClampMin(lightness)), - LightDark::Light => self.with_lightness(AssignLightness::ClampMax(lightness)), + match theme { + TerminalTheme::Dark => self.with_lightness(AssignLightness::ClampMin(lightness)), + TerminalTheme::Light => self.with_lightness(AssignLightness::ClampMax(lightness)), } } diff --git a/crates/hyfetch/src/types.rs b/crates/hyfetch/src/types.rs index b2ead9b9..d934763a 100644 --- a/crates/hyfetch/src/types.rs +++ b/crates/hyfetch/src/types.rs @@ -23,7 +23,7 @@ pub enum AnsiMode { )] #[serde(rename_all = "lowercase")] #[strum(serialize_all = "lowercase")] -pub enum LightDark { +pub enum TerminalTheme { Light, Dark, }