Use selected backend to get distro name

This commit is contained in:
Teoh Han Hui 2024-07-13 04:04:14 +08:00
parent d79196a7fc
commit d11f6f0a9f
No known key found for this signature in database
GPG key ID: D43E2BABAF97DCAE
6 changed files with 154 additions and 83 deletions

View file

@ -12,7 +12,7 @@ use hyfetch::models::Config;
use hyfetch::neofetch_util::ensure_git_bash; use hyfetch::neofetch_util::ensure_git_bash;
use hyfetch::neofetch_util::{self, ascii_size, get_distro_ascii, literal_input, ColorAlignment}; use hyfetch::neofetch_util::{self, ascii_size, get_distro_ascii, literal_input, ColorAlignment};
use hyfetch::presets::{AssignLightness, ColorProfile, Preset}; use hyfetch::presets::{AssignLightness, ColorProfile, Preset};
use hyfetch::types::{AnsiMode, LightDark}; use hyfetch::types::{AnsiMode, Backend, TerminalTheme};
use hyfetch::utils::get_cache_path; use hyfetch::utils::get_cache_path;
use palette::Srgb; use palette::Srgb;
use strum::{EnumCount, VariantArray, VariantNames}; use strum::{EnumCount, VariantArray, VariantNames};
@ -28,34 +28,54 @@ fn main() -> Result<()> {
let options = options().run(); 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"); debug!(?options, "CLI options");
// Use a custom distro // Use a custom distro
let distro = options.distro.as_ref(); let distro = options.distro.as_ref();
let backend = options.backend.unwrap_or(Backend::Neofetch);
let use_overlay = options.overlay;
#[cfg(windows)] #[cfg(windows)]
ensure_git_bash().context("failed to find git bash")?; ensure_git_bash().context("failed to find git bash")?;
if options.test_print { 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}"); println!("{asc}");
return Ok(()); return Ok(());
} }
let config = if options.config { let config = if options.config {
create_config(distro, &options.config_file, options.overlay, options.debug) create_config(
.context("failed to create config")? &options.config_file,
distro,
backend,
use_overlay,
debug_mode,
)
.context("failed to create config")?
} else if let Some(config) = } else if let Some(config) =
read_config(&options.config_file).context("failed to read config")? read_config(&options.config_file).context("failed to read config")?
{ {
config config
} else { } else {
create_config(distro, &options.config_file, options.overlay, options.debug) create_config(
.context("failed to 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) // Check if it's June (pride month)
let now = let now =
OffsetDateTime::now_local().context("failed to get current datetime in local timezone")?; OffsetDateTime::now_local().context("failed to get current datetime in local timezone")?;
@ -83,7 +103,6 @@ fn main() -> Result<()> {
// Use a custom distro // Use a custom distro
let distro = options.distro.as_ref().or(config.distro.as_ref()); 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 backend = options.backend.unwrap_or(config.backend);
let args = options.args.as_ref().or(config.args.as_ref()); 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 { } else if let Some(lightness) = options.lightness {
color_profile.with_lightness(AssignLightness::Replace(lightness)) color_profile.with_lightness(AssignLightness::Replace(lightness))
} else { } 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"); debug!(?color_profile, "lightened color profile");
@ -109,7 +128,7 @@ fn main() -> Result<()> {
None, None,
) )
} else { } 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() { let color_align = if fore_back.is_some() {
match config.color_align { match config.color_align {
@ -124,7 +143,7 @@ fn main() -> Result<()> {
config.color_align config.color_align
}; };
let asc = 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")?; .context("failed to recolor ascii")?;
neofetch_util::run(asc, backend, args)?; neofetch_util::run(asc, backend, args)?;
@ -174,8 +193,9 @@ fn read_config(path: &Path) -> Result<Option<Config>> {
/// The config is automatically stored to file. /// The config is automatically stored to file.
#[tracing::instrument(level = "debug")] #[tracing::instrument(level = "debug")]
fn create_config( fn create_config(
distro: Option<&String>,
path: &Path, path: &Path,
distro: Option<&String>,
backend: Backend,
use_overlay: bool, use_overlay: bool,
debug_mode: bool, debug_mode: bool,
) -> Result<Config> { ) -> Result<Config> {
@ -202,14 +222,15 @@ fn create_config(
}); });
debug!(?det_ansi, "detected color mode"); 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 (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 color_mode = det_ansi.unwrap_or(AnsiMode::Ansi256);
let logo = color( let logo = color(
match theme { match theme {
LightDark::Light => "&l&bhyfetch&~&L", TerminalTheme::Light => "&l&bhyfetch&~&L",
LightDark::Dark => "&l&bhy&ffetch&~&L", TerminalTheme::Dark => "&l&bhy&ffetch&~&L",
}, },
color_mode, 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 { if let Some(det_bg) = det_bg {
return Ok((det_bg.theme(), "Detected background color")); return Ok((det_bg.theme(), "Detected background color"));
} }
@ -294,11 +315,10 @@ fn create_config(
}; };
let theme = { let theme = {
let (light_dark, ttl) = let (selected_theme, ttl) = select_theme().context("failed to select theme")?;
select_light_dark().context("failed to select light / dark mode")?; debug!(?selected_theme, "selected theme");
debug!(?light_dark, "selected theme"); update_title(&mut title, &mut option_counter, ttl, selected_theme.into());
update_title(&mut title, &mut option_counter, ttl, light_dark.into()); selected_theme
light_dark
}; };
////////////////////////////// //////////////////////////////
@ -400,7 +420,7 @@ fn create_config(
let color_profile: ColorProfile; let color_profile: ColorProfile;
let preset_rainbow = Preset::Rainbow let preset_rainbow = Preset::Rainbow
.color_profile() .color_profile()
.with_lightness_dl(Config::default_lightness(theme), theme, use_overlay) .with_lightness_adaptive(Config::default_lightness(theme), theme, use_overlay)
.color_text( .color_text(
"preset", "preset",
color_mode, color_mode,
@ -421,7 +441,7 @@ fn create_config(
opts.push("prev"); opts.push("prev");
} }
println!("Enter 'next' to go to the next page and 'prev' to go to the previous page."); 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? "), format!("Which {preset_rainbow} do you want to use? "),
&opts[..], &opts[..],
Preset::Rainbow.into(), Preset::Rainbow.into(),
@ -429,18 +449,19 @@ fn create_config(
color_mode, color_mode,
) )
.context("failed to select preset")?; .context("failed to select preset")?;
if preset == "next" { if selection == "next" {
page += 1; page += 1;
} else if preset == "prev" { } else if selection == "prev" {
page -= 1; page -= 1;
} else { } else {
let preset: Preset = preset.parse().expect("selected preset should be valid"); let selected_preset: Preset =
debug!(?preset, "selected preset"); selection.parse().expect("selected preset should be valid");
color_profile = preset.color_profile(); 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 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( .color_text(
preset_name, preset_name,
color_mode, color_mode,

View file

@ -11,7 +11,7 @@ use palette::{IntoColorMut, LinSrgb, Okhsl, Srgb};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use thiserror::Error; use thiserror::Error;
use crate::types::{AnsiMode, LightDark}; use crate::types::{AnsiMode, TerminalTheme};
const MINECRAFT_COLORS: [(&str, &str); 30] = [ const MINECRAFT_COLORS: [(&str, &str); 30] = [
// Minecraft formatting codes // Minecraft formatting codes
@ -114,7 +114,7 @@ pub trait ToAnsiString {
} }
pub trait Theme { pub trait Theme {
fn theme(&self) -> LightDark; fn theme(&self) -> TerminalTheme;
} }
impl Lightness { impl Lightness {
@ -227,16 +227,16 @@ impl ToAnsiString for Srgb<u8> {
} }
impl Theme for Srgb<u8> { impl Theme for Srgb<u8> {
fn theme(&self) -> LightDark { fn theme(&self) -> TerminalTheme {
let mut rgb_f32_color: LinSrgb = self.into_linear(); let mut rgb_f32_color: LinSrgb = self.into_linear();
{ {
let okhsl_f32_color: &mut Okhsl = &mut rgb_f32_color.into_color_mut(); let okhsl_f32_color: &mut Okhsl = &mut rgb_f32_color.into_color_mut();
if okhsl_f32_color.lightness > 0.5 { if okhsl_f32_color.lightness > 0.5 {
LightDark::Light TerminalTheme::Light
} else { } else {
LightDark::Dark TerminalTheme::Dark
} }
} }
} }

View file

@ -3,13 +3,13 @@ use serde::{Deserialize, Serialize};
use crate::color_util::Lightness; use crate::color_util::Lightness;
use crate::neofetch_util::ColorAlignment; use crate::neofetch_util::ColorAlignment;
use crate::presets::Preset; use crate::presets::Preset;
use crate::types::{AnsiMode, Backend, LightDark}; use crate::types::{AnsiMode, Backend, TerminalTheme};
#[derive(Clone, Debug, Deserialize, Serialize)] #[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Config { pub struct Config {
pub preset: Preset, pub preset: Preset,
pub mode: AnsiMode, pub mode: AnsiMode,
pub light_dark: LightDark, pub light_dark: TerminalTheme,
lightness: Option<Lightness>, lightness: Option<Lightness>,
pub color_align: ColorAlignment, pub color_align: ColorAlignment,
pub backend: Backend, pub backend: Backend,
@ -21,12 +21,12 @@ pub struct Config {
} }
impl Config { impl Config {
pub fn default_lightness(term: LightDark) -> Lightness { pub fn default_lightness(theme: TerminalTheme) -> Lightness {
match term { match theme {
LightDark::Dark => { TerminalTheme::Dark => {
Lightness::new(0.65).expect("default lightness should not be invalid") 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") Lightness::new(0.4).expect("default lightness should not be invalid")
}, },
} }

View file

@ -22,7 +22,7 @@ use crate::color_util::{
}; };
use crate::distros::Distro; use crate::distros::Distro;
use crate::presets::ColorProfile; 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}; use crate::utils::{find_file, find_in_path, process_command_status};
const NEOFETCH_COLOR_PATTERNS: [&str; 6] = ["${c1}", "${c2}", "${c3}", "${c4}", "${c5}", "${c6}"]; const NEOFETCH_COLOR_PATTERNS: [&str; 6] = ["${c1}", "${c2}", "${c3}", "${c4}", "${c5}", "${c6}"];
@ -84,7 +84,7 @@ impl ColorAlignment {
asc: String, asc: String,
color_profile: ColorProfile, color_profile: ColorProfile,
color_mode: AnsiMode, color_mode: AnsiMode,
term: LightDark, theme: TerminalTheme,
) -> Result<String> { ) -> Result<String> {
let reset = color("&~&*", color_mode).expect("color reset should not be invalid"); let reset = color("&~&*", color_mode).expect("color reset should not be invalid");
@ -105,9 +105,9 @@ impl ColorAlignment {
let asc = asc.replace( let asc = asc.replace(
&format!("${{c{fore}}}"), &format!("${{c{fore}}}"),
&color( &color(
match term { match theme {
LightDark::Light => "&0", TerminalTheme::Light => "&0",
LightDark::Dark => "&f", TerminalTheme::Dark => "&f",
}, },
color_mode, color_mode,
) )
@ -466,14 +466,17 @@ pub fn ensure_git_bash() -> Result<PathBuf> {
/// Gets the distro ascii of the current distro. Or if distro is specified, get /// Gets the distro ascii of the current distro. Or if distro is specified, get
/// the specific distro's ascii art instead. /// the specific distro's ascii art instead.
#[tracing::instrument(level = "debug")] #[tracing::instrument(level = "debug")]
pub fn get_distro_ascii<S>(distro: Option<S>) -> Result<(String, Option<ForeBackColorPair>)> pub fn get_distro_ascii<S>(
distro: Option<S>,
backend: Backend,
) -> Result<(String, Option<ForeBackColorPair>)>
where where
S: AsRef<str> + fmt::Debug, S: AsRef<str> + fmt::Debug,
{ {
let distro: Cow<_> = if let Some(distro) = distro.as_ref() { let distro: Cow<_> = if let Some(distro) = distro.as_ref() {
distro.as_ref().into() distro.as_ref().into()
} else { } else {
get_distro_name() get_distro_name(backend)
.context("failed to get distro name")? .context("failed to get distro name")?
.into() .into()
}; };
@ -500,6 +503,7 @@ where
Ok((normalize_ascii(asc), None)) Ok((normalize_ascii(asc), None))
} }
#[tracing::instrument(level = "debug", skip(asc))]
pub fn run(asc: String, backend: Backend, args: Option<&Vec<String>>) -> Result<()> { pub fn run(asc: String, backend: Backend, args: Option<&Vec<String>>) -> Result<()> {
match backend { match backend {
Backend::Neofetch => { Backend::Neofetch => {
@ -653,10 +657,60 @@ where
} }
} }
/// Runs fastfetch command, returning the piped stdout output.
fn run_fastfetch_command_piped<S>(args: &[S]) -> Result<String>
where
S: AsRef<OsStr> + 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<S>(args: &[S]) -> Result<Command>
where
S: AsRef<OsStr>,
{
// 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")] #[tracing::instrument(level = "debug")]
fn get_distro_name() -> Result<String> { fn get_distro_name(backend: Backend) -> Result<String> {
run_neofetch_command_piped(&["ascii_distro_name"]) match backend {
.context("failed to get distro name from neofetch") 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. /// Runs neofetch with colors.
@ -676,21 +730,18 @@ fn run_neofetch(asc: String, args: Option<&Vec<String>>) -> Result<()> {
// Call neofetch with the temp file // Call neofetch with the temp file
let temp_file_path = temp_file.into_temp_path(); let temp_file_path = temp_file.into_temp_path();
let args = { let args = {
let mut v = vec![ let mut v: Vec<Cow<OsStr>> = vec![
"--ascii", OsStr::new("--ascii").into(),
"--source", OsStr::new("--source").into(),
temp_file_path OsStr::new(&temp_file_path).into(),
.to_str() OsStr::new("--ascii-colors").into(),
.expect("temp file path should not contain invalid UTF-8"),
"--ascii-colors",
]; ];
if let Some(args) = args { if let Some(args) = args {
let args: Vec<_> = args.iter().map(|s| &**s).collect(); v.extend(args.iter().map(|arg| OsStr::new(arg).into()));
v.extend(args);
} }
v v
}; };
let mut command = make_neofetch_command(&args)?; let mut command = make_neofetch_command(&args[..])?;
debug!(?command, "neofetch command"); debug!(?command, "neofetch command");
@ -761,12 +812,6 @@ fn fastfetch_path() -> Result<Option<PathBuf>> {
/// Runs fastfetch with colors. /// Runs fastfetch with colors.
#[tracing::instrument(level = "debug", skip(asc))] #[tracing::instrument(level = "debug", skip(asc))]
fn run_fastfetch(asc: String, args: Option<&Vec<String>>, legacy: bool) -> Result<()> { fn run_fastfetch(asc: String, args: Option<&Vec<String>>, 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 // Write temp file
let mut temp_file = let mut temp_file =
NamedTempFile::with_prefix("ascii.txt").context("failed to create temp file for ascii")?; 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<String>>, legacy: bool) -> Resul
// Call fastfetch with the temp file // Call fastfetch with the temp file
let temp_file_path = temp_file.into_temp_path(); let temp_file_path = temp_file.into_temp_path();
let mut command = Command::new(fastfetch_path); let args = {
command.arg(if legacy { "--raw" } else { "--file-raw" }); let mut v: Vec<Cow<OsStr>> = vec![
command.arg(&temp_file_path); OsStr::new(if legacy { "--raw" } else { "--file-raw" }).into(),
if let Some(args) = args { OsStr::new(&temp_file_path).into(),
command.args(args); ];
} 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"); debug!(?command, "fastfetch command");

View file

@ -10,7 +10,7 @@ use tracing::debug;
use unicode_segmentation::UnicodeSegmentation; use unicode_segmentation::UnicodeSegmentation;
use crate::color_util::{ForegroundBackground, Lightness, ToAnsiString}; use crate::color_util::{ForegroundBackground, Lightness, ToAnsiString};
use crate::types::{AnsiMode, LightDark}; use crate::types::{AnsiMode, TerminalTheme};
#[derive( #[derive(
Copy, Copy,
@ -639,20 +639,20 @@ impl ColorProfile {
} }
/// Creates a new color profile, with the colors set to the specified /// Creates a new color profile, with the colors set to the specified
/// [`Okhsl`] lightness value, with respect to dark/light terminals. /// [`Okhsl`] lightness value, adapted to the terminal theme.
pub fn with_lightness_dl( pub fn with_lightness_adaptive(
&self, &self,
lightness: Lightness, lightness: Lightness,
term: LightDark, theme: TerminalTheme,
use_overlay: bool, use_overlay: bool,
) -> Self { ) -> Self {
if use_overlay { if use_overlay {
todo!() todo!()
} }
match term { match theme {
LightDark::Dark => self.with_lightness(AssignLightness::ClampMin(lightness)), TerminalTheme::Dark => self.with_lightness(AssignLightness::ClampMin(lightness)),
LightDark::Light => self.with_lightness(AssignLightness::ClampMax(lightness)), TerminalTheme::Light => self.with_lightness(AssignLightness::ClampMax(lightness)),
} }
} }

View file

@ -23,7 +23,7 @@ pub enum AnsiMode {
)] )]
#[serde(rename_all = "lowercase")] #[serde(rename_all = "lowercase")]
#[strum(serialize_all = "lowercase")] #[strum(serialize_all = "lowercase")]
pub enum LightDark { pub enum TerminalTheme {
Light, Light,
Dark, Dark,
} }