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