1use libmacchina::{
2 GeneralReadout, KernelReadout, MemoryReadout, PackageReadout, traits::GeneralReadout as _,
3 traits::KernelReadout as _, traits::MemoryReadout as _, traits::PackageReadout as _,
4};
5
6use pfetch_logo_parser::{Color, Logo, LogoPart};
7use std::{env, fmt::Display, str::FromStr};
8
9#[derive(Debug, PartialEq)]
10pub enum PfetchInfo {
11 Os,
12 Host,
13 Kernel,
14 Uptime,
15 Cpu,
16 Memory,
17 Shell,
18 Editor,
19 Wm,
20 De,
21}
22
23impl Display for PfetchInfo {
24 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25 write!(f, "{}", format!("{self:?}").to_lowercase())
26 }
27}
28
29impl FromStr for PfetchInfo {
30 type Err = String;
31
32 fn from_str(info: &str) -> Result<Self, Self::Err> {
33 match info {
34 "os" => Ok(PfetchInfo::Os),
35 "host" => Ok(PfetchInfo::Host),
36 "kernel" => Ok(PfetchInfo::Kernel),
37 "uptime" => Ok(PfetchInfo::Uptime),
38 "cpu" => Ok(PfetchInfo::Cpu),
39 "memory" => Ok(PfetchInfo::Memory),
40 "shell" => Ok(PfetchInfo::Shell),
41 "editor" => Ok(PfetchInfo::Editor),
42 "wm" => Ok(PfetchInfo::Wm),
43 "de" => Ok(PfetchInfo::De),
44 unknown_info => Err(format!("Unknown pfetch info: {unknown_info}")),
45 }
46 }
47}
48
49pub struct Readouts {
51 pub general_readout: GeneralReadout,
52 pub package_readout: PackageReadout,
53 pub memory_readout: MemoryReadout,
54 pub kernel_readout: KernelReadout,
55}
56
57impl Default for Readouts {
58 fn default() -> Self {
59 Readouts {
60 general_readout: GeneralReadout::new(),
61 package_readout: PackageReadout::new(),
62 memory_readout: MemoryReadout::new(),
63 kernel_readout: KernelReadout::new(),
64 }
65 }
66}
67
68pub fn get_info(info: &PfetchInfo, readouts: &Readouts) -> Option<String> {
69 match info {
70 PfetchInfo::Os => pfetch::os(&readouts.general_readout),
71 PfetchInfo::Host => pfetch::host(&readouts.general_readout),
72 PfetchInfo::Kernel => pfetch::kernel(&readouts.kernel_readout),
73 PfetchInfo::Uptime => pfetch::uptime(&readouts.general_readout),
74 PfetchInfo::Cpu => pfetch::cpu(&readouts.general_readout),
75 PfetchInfo::Memory => pfetch::memory(&readouts.memory_readout),
76 PfetchInfo::Shell => pfetch::shell(&readouts.general_readout),
77 PfetchInfo::Editor => Some(env::var("EDITOR").unwrap_or_else(|_| "nvim".into())),
78 PfetchInfo::Wm => pfetch::wm(&readouts.general_readout),
79 PfetchInfo::De => pfetch::de(&readouts.general_readout),
80 }
81}
82
83pub fn pfetch_info() -> String {
85 let readouts = Readouts::default();
86
87 let os = pfetch::os(&readouts.general_readout).unwrap_or_default();
88
89 let all_infos = [
90 PfetchInfo::Os,
91 PfetchInfo::Host,
92 PfetchInfo::Kernel,
93 PfetchInfo::Uptime,
94 PfetchInfo::Cpu,
95 PfetchInfo::Memory,
96 PfetchInfo::Shell,
97 PfetchInfo::Editor,
98 PfetchInfo::Wm,
99 PfetchInfo::De,
100 ];
101
102 let logo = pfetch::logo(&os);
103 let gathered_pfetch_info: Vec<(Color, String, String)> = all_infos
104 .iter()
105 .filter_map(|info| match info {
106 PfetchInfo::Os => Some((logo.primary_color, info.to_string(), os.clone())),
107 _ => get_info(info, &readouts)
108 .map(|info_str| (logo.primary_color, info.to_string(), info_str)),
109 })
110 .collect();
111
112 pfetch(gathered_pfetch_info, logo, true)
113}
114
115fn env_usize(name: &str, default: usize) -> usize {
116 dotenvy::var(name)
117 .ok()
118 .and_then(|value| value.parse::<usize>().ok())
119 .unwrap_or(default)
120}
121
122fn pfetch(info: Vec<(Color, String, String)>, logo: Logo, logo_enabled: bool) -> String {
123 let raw_logo = if logo_enabled {
124 logo.logo_parts
125 .iter()
126 .map(|LogoPart { content, .. }| content.as_ref())
127 .collect::<String>()
128 } else {
129 "".into()
130 };
131 let color_enabled = dotenvy::var("PF_COLOR").unwrap_or_default() != "0";
132 let logo = if color_enabled {
133 logo.to_string()
134 } else {
135 format!("{logo:#}")
136 };
137 let mut logo_lines = logo.lines();
138 let raw_logo_lines: Vec<_> = raw_logo.lines().collect();
139 let logo_width = raw_logo_lines
140 .iter()
141 .map(|line| line.chars().count())
142 .max()
143 .unwrap_or(0);
144 let line_amount = usize::max(raw_logo_lines.len(), info.len());
145
146 let info1_width = info
147 .iter()
148 .skip(1)
149 .map(|(_, line, _)| {
150 if line.starts_with("\x1b[4") {
151 0
153 } else {
154 line.len()
155 }
156 })
157 .max()
158 .unwrap_or(0);
159
160 let padding1 = env_usize("PF_PAD1", 0);
161 let padding2 = env_usize("PF_PAD2", 3);
162 let padding3 = env_usize("PF_PAD3", 1);
163
164 let mut pfetch_str = String::new();
165
166 for l in 0..line_amount {
167 pfetch_str += &format!(
168 "{padding1}{bold}{logo}{padding2}{color}{info1}{nobold}{separator}{padding3}{color2}{info2}\n",
169 padding1 = " ".repeat(padding1),
170 bold = if color_enabled { "\x1b[1m" } else { "" },
171 logo = if logo_enabled {
172 logo_lines.next().unwrap_or("")
173 } else {
174 ""
175 },
176 padding2 = " ".repeat(
177 logo_width - raw_logo_lines.get(l).map_or(0, |line| line.chars().count())
178 + if logo_enabled { padding2 } else { 0 }
179 ),
180 color = if color_enabled {
181 info.get(l).map_or("".to_owned(), |line| {
182 let (color, _, _) = line;
183 color.to_string()
184 })
185 } else {
186 "".into()
187 },
188 info1 = info.get(l).map_or("", |line| &line.1),
189 nobold = if color_enabled { "\x1b[0m" } else { "" },
190 separator = info
191 .get(l)
192 .map_or("".into(), |line| if !&line.2.is_empty() {
193 dotenvy::var("PF_SEP").unwrap_or_default()
194 } else {
195 "".into()
196 }),
197 padding3 = " ".repeat(
198 info1_width.saturating_sub(info.get(l).map_or(0, |(_, line, _)| line.len()))
199 + padding3
200 ),
201 color2 = if color_enabled {
202 match dotenvy::var("PF_COL2") {
203 Ok(newcolor) => match Color::from_str(&newcolor) {
204 Ok(newcolor) => format!("{newcolor}"),
205 Err(_) => "".into(),
206 },
207 Err(_) => "".into(),
208 }
209 } else {
210 "".into()
211 },
212 info2 = info.get(l).map_or("", |line| &line.2)
213 )
214 }
215
216 pfetch_str
217}