use libmacchina::{
GeneralReadout, KernelReadout, MemoryReadout, traits::GeneralReadout as _,
traits::KernelReadout as _, traits::MemoryReadout as _,
};
use pfetch_logo_parser::{Color, Logo, LogoPart};
use std::{env, fmt::Display, str::FromStr};
pub type SystemInfo = String;
#[derive(Debug, PartialEq)]
enum PfetchInfo {
Os,
Host,
Kernel,
Uptime,
Cpu,
Memory,
Shell,
Editor,
Wm,
De,
}
impl Display for PfetchInfo {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", format!("{self:?}").to_lowercase())
}
}
impl FromStr for PfetchInfo {
type Err = String;
fn from_str(info: &str) -> Result<Self, Self::Err> {
match info {
"os" => Ok(PfetchInfo::Os),
"host" => Ok(PfetchInfo::Host),
"kernel" => Ok(PfetchInfo::Kernel),
"uptime" => Ok(PfetchInfo::Uptime),
"cpu" => Ok(PfetchInfo::Cpu),
"memory" => Ok(PfetchInfo::Memory),
"shell" => Ok(PfetchInfo::Shell),
"editor" => Ok(PfetchInfo::Editor),
"wm" => Ok(PfetchInfo::Wm),
"de" => Ok(PfetchInfo::De),
unknown_info => Err(format!("Unknown pfetch info: {unknown_info}")),
}
}
}
struct Readouts {
general_readout: GeneralReadout,
memory_readout: MemoryReadout,
kernel_readout: KernelReadout,
}
impl Default for Readouts {
fn default() -> Self {
Readouts {
general_readout: GeneralReadout::new(),
memory_readout: MemoryReadout::new(),
kernel_readout: KernelReadout::new(),
}
}
}
fn get_info(info: &PfetchInfo, readouts: &Readouts) -> Option<String> {
match info {
PfetchInfo::Os => pfetch::os(&readouts.general_readout),
PfetchInfo::Host => pfetch::host(&readouts.general_readout),
PfetchInfo::Kernel => pfetch::kernel(&readouts.kernel_readout),
PfetchInfo::Uptime => pfetch::uptime(&readouts.general_readout),
PfetchInfo::Cpu => pfetch::cpu(&readouts.general_readout),
PfetchInfo::Memory => pfetch::memory(&readouts.memory_readout),
PfetchInfo::Shell => pfetch::shell(&readouts.general_readout),
PfetchInfo::Editor => Some(env::var("EDITOR").unwrap_or_else(|_| "nvim".into())),
PfetchInfo::Wm => pfetch::wm(&readouts.general_readout),
PfetchInfo::De => pfetch::de(&readouts.general_readout),
}
}
pub(crate) fn default_system_info() -> SystemInfo {
let readouts = Readouts::default();
let os = pfetch::os(&readouts.general_readout).unwrap_or_default();
let all_infos = [
PfetchInfo::Os,
PfetchInfo::Host,
PfetchInfo::Kernel,
PfetchInfo::Uptime,
PfetchInfo::Cpu,
PfetchInfo::Memory,
PfetchInfo::Shell,
PfetchInfo::Editor,
PfetchInfo::Wm,
PfetchInfo::De,
];
let logo = pfetch::logo(&os);
let gathered_pfetch_info: Vec<(Color, String, String)> = all_infos
.iter()
.filter_map(|info| match info {
PfetchInfo::Os => Some((logo.primary_color, info.to_string(), os.clone())),
_ => get_info(info, &readouts)
.map(|info_str| (logo.primary_color, info.to_string(), info_str)),
})
.collect();
pfetch(gathered_pfetch_info, logo, true)
}
fn env_usize(name: &str, default: usize) -> usize {
dotenvy::var(name)
.ok()
.and_then(|value| value.parse::<usize>().ok())
.unwrap_or(default)
}
fn pfetch(info: Vec<(Color, String, String)>, logo: Logo, logo_enabled: bool) -> String {
let raw_logo = if logo_enabled {
logo.logo_parts
.iter()
.map(|LogoPart { content, .. }| content.as_ref())
.collect::<String>()
} else {
"".into()
};
let color_enabled = dotenvy::var("PF_COLOR").unwrap_or_default() != "0";
let logo = if color_enabled {
logo.to_string()
} else {
format!("{logo:#}")
};
let mut logo_lines = logo.lines();
let raw_logo_lines: Vec<_> = raw_logo.lines().collect();
let logo_width = raw_logo_lines
.iter()
.map(|line| line.chars().count())
.max()
.unwrap_or(0);
let line_amount = usize::max(raw_logo_lines.len(), info.len());
let info1_width = info
.iter()
.skip(1)
.map(|(_, line, _)| {
if line.starts_with("\x1b[4") {
0
} else {
line.len()
}
})
.max()
.unwrap_or(0);
let padding1 = env_usize("PF_PAD1", 0);
let padding2 = env_usize("PF_PAD2", 3);
let padding3 = env_usize("PF_PAD3", 1);
let mut pfetch_str = String::new();
for l in 0..line_amount {
pfetch_str += &format!(
"{padding1}{bold}{logo}{padding2}{color}{info1}{nobold}{separator}{padding3}{color2}{info2}\n",
padding1 = " ".repeat(padding1),
bold = if color_enabled { "\x1b[1m" } else { "" },
logo = if logo_enabled {
logo_lines.next().unwrap_or("")
} else {
""
},
padding2 = " ".repeat(
logo_width - raw_logo_lines.get(l).map_or(0, |line| line.chars().count())
+ if logo_enabled { padding2 } else { 0 }
),
color = if color_enabled {
info.get(l).map_or("".to_owned(), |line| {
let (color, _, _) = line;
color.to_string()
})
} else {
"".into()
},
info1 = info.get(l).map_or("", |line| &line.1),
nobold = if color_enabled { "\x1b[0m" } else { "" },
separator = info.get(l).map_or("".into(), |line| {
if !&line.2.is_empty() {
dotenvy::var("PF_SEP").unwrap_or_default()
} else {
"".into()
}
}),
padding3 = " ".repeat(
info1_width.saturating_sub(info.get(l).map_or(0, |(_, line, _)| line.len()))
+ padding3
),
color2 = if color_enabled {
match dotenvy::var("PF_COL2") {
Ok(newcolor) => match Color::from_str(&newcolor) {
Ok(newcolor) => format!("{newcolor}"),
Err(_) => "".into(),
},
Err(_) => "".into(),
}
} else {
"".into()
},
info2 = info.get(l).map_or("", |line| &line.2)
)
}
pfetch_str
}