1use crate::cli::Cli;
2use crate::config::Config;
3use crate::fetch::SystemInfo;
4use crate::logo;
5use crate::theme::Theme;
6
7impl SystemInfo {
8 pub fn display(&self, cli: &Cli, _config: &Config) -> anyhow::Result<()> {
14 println!();
15 let theme_name = _config.theme.as_deref().or(cli.theme.as_deref());
16 let mut theme = match theme_name {
17 Some(name) => Theme::from_name(name),
18 None => Theme::detect_system_theme(), };
20
21 if let Some(custom) = &_config.custom_theme {
23 theme = Theme::with_custom_overrides(theme, custom);
24 }
25
26 let show_logo = _config.show_logo.unwrap_or(true) && !cli.no_logo;
27 if show_logo {
28 #[cfg(feature = "graphics")]
29 let mut printed_logo = false;
30 #[cfg(not(feature = "graphics"))]
31 let printed_logo = false;
32
33 if !cli.ascii_only {
34 let user_logo = if let Some(config_dir) = dirs::config_dir() {
36 let p = config_dir.join("retch").join("logo.png");
37 if p.exists() {
38 Some(p)
39 } else {
40 None
41 }
42 } else {
43 None
44 };
45
46 #[cfg(feature = "graphics")]
48 if !printed_logo && logo::supports_kitty() {
49 if let Some(path) = &user_logo {
50 logo::print_graphical_logo_from_path(path);
51 printed_logo = true;
52 } else if let Some(distro) = logo::detect_distro() {
53 if let Some(bytes) = logo::get_embedded_logo(Some(&distro)) {
54 logo::print_graphical_logo(bytes);
55 printed_logo = true;
56 }
57 }
58 }
59
60 #[cfg(feature = "graphics")]
62 if !printed_logo && logo::supports_iterm2() {
63 if let Some(path) = &user_logo {
64 logo::print_iterm2_logo_from_path(path);
65 printed_logo = true;
66 } else if let Some(distro) = logo::detect_distro() {
67 if let Some(bytes) = logo::get_embedded_logo(Some(&distro)) {
68 logo::print_iterm2_logo(bytes);
69 printed_logo = true;
70 }
71 }
72 }
73
74 #[cfg(feature = "graphics")]
76 if !printed_logo && logo::supports_sixel() {
77 if let Some(path) = &user_logo {
78 logo::print_sixel_logo_from_path(path);
79 printed_logo = true;
80 } else if let Some(distro) = logo::detect_distro() {
81 if let Some(bytes) = logo::get_embedded_logo(Some(&distro)) {
82 logo::print_sixel_logo(bytes);
83 printed_logo = true;
84 }
85 }
86 }
87
88 if !printed_logo && logo::chafa_available() {
90 if let Some(path) = &user_logo {
91 if logo::print_with_chafa(path) {
92 printed_logo = true;
93 }
94 } else if logo::detect_distro().is_some() {
95 }
98 }
99 }
100
101 if !printed_logo {
103 let distro_hint = logo::detect_distro();
104 logo::print_distro_logo_with_ascii(distro_hint.as_deref(), cli.ascii_only);
106 }
107 println!(); }
109
110 let allowed_fields: Option<Vec<String>> = if cli.long {
112 None } else if cli.short {
114 Some(vec![
115 "os".to_string(),
116 "kernel".to_string(),
117 "host".to_string(),
118 "cpu".to_string(),
119 "gpu".to_string(),
120 "memory".to_string(),
121 "disk".to_string(),
122 ])
123 } else if let Some(fields) = &_config.fields {
124 Some(fields.iter().map(|s| s.to_lowercase()).collect())
125 } else {
126 Some(vec![
128 "os".to_string(),
129 "kernel".to_string(),
130 "host".to_string(),
131 "cpu".to_string(),
132 "gpu".to_string(),
133 "memory".to_string(),
134 "swap".to_string(),
135 "load".to_string(),
136 "disk".to_string(),
137 "net".to_string(),
138 "uptime".to_string(),
139 ])
140 };
141
142 let should_show = |label: &str| -> bool {
143 match &allowed_fields {
144 Some(fields) => fields.contains(&label.to_lowercase()),
145 None => true,
146 }
147 };
148
149 let label_width = 10;
151 let print_line = |label: &str, value: &str| {
152 if should_show(label) {
153 println!(
154 "{:>width$}{} {}",
155 theme.color_label(label),
156 theme.color_separator(":"),
157 theme.color_value(value),
158 width = label_width
159 );
160 }
161 };
162
163 print_line("OS", &self.os);
164 if let Some(kernel) = &self.kernel {
165 print_line("Kernel", kernel);
166 }
167 if let Some(host) = &self.hostname {
168 print_line("Host", host);
169 }
170 if let Some(user) = &self.current_user {
171 print_line("User", user);
172 }
173 print_line("Arch", &self.arch);
174 print_line("CPU", &format!("{} ({} cores)", self.cpu, self.cpu_cores));
175 if let Some(freq) = &self.cpu_freq {
176 print_line("CPU Freq", freq);
177 }
178 if should_show("GPU") {
179 for gpu in &self.gpu {
180 print_line("GPU", gpu);
181 }
182 }
183 print_line("Memory", &self.memory);
184 print_line("Swap", &self.swap);
185 print_line("Procs", &self.processes.to_string());
186 if let Some(load) = &self.load_avg {
187 print_line("Load", load);
188 }
189
190 if should_show("Disk") {
191 for disk in &self.disks {
192 print_line("Disk", disk);
193 }
194 }
195
196 if should_show("Temp") {
197 for temp in &self.temps {
198 print_line("Temp", temp);
199 }
200 }
201
202 if should_show("Net") {
203 for net in &self.networks {
204 print_line("Net", net);
205 }
206 }
207
208 let uptime_str = format_uptime(&self.uptime);
210 let boot_display = format!("{} since {}", uptime_str, self.boot_time);
211 print_line("Uptime", &boot_display);
212
213 if let Some(bat) = &self.battery {
214 print_line("Battery", bat);
215 }
216
217 if let Some(shell) = &self.shell {
218 print_line("Shell", shell);
219 }
220 if let Some(term) = &self.terminal {
221 print_line("Terminal", term);
222 }
223 if let Some(de) = &self.desktop {
224 print_line("Desktop", de);
225 }
226 print_line("Users", &self.users.to_string());
227 if let Some(pkgs) = self.packages {
228 if pkgs > 0 {
229 print_line("Packages", &pkgs.to_string());
230 }
231 }
232
233 Ok(())
234 }
235}
236
237fn format_uptime(uptime: &str) -> String {
241 let seconds: u64 = uptime.trim_end_matches('s').parse().unwrap_or(0);
243
244 let years = seconds / (365 * 24 * 3600);
245 let days = (seconds % (365 * 24 * 3600)) / (24 * 3600);
246 let hours = (seconds % (24 * 3600)) / 3600;
247 let minutes = (seconds % 3600) / 60;
248 let secs = seconds % 60;
249
250 let mut parts = Vec::new();
251 if years > 0 {
252 parts.push(format!("{}y", years));
253 }
254 if days > 0 {
255 parts.push(format!("{}d", days));
256 }
257 if hours > 0 {
258 parts.push(format!("{}h", hours));
259 }
260 if minutes > 0 {
261 parts.push(format!("{}m", minutes));
262 }
263 if secs > 0 || parts.is_empty() {
264 parts.push(format!("{}s", secs));
265 }
266
267 parts.join(" ")
268}
269
270#[cfg(test)]
271mod tests {
272 use super::*;
273
274 #[test]
275 fn test_format_uptime() {
276 assert_eq!(format_uptime("60s"), "1m");
277 assert_eq!(format_uptime("3600s"), "1h");
278 assert_eq!(format_uptime("3661s"), "1h 1m 1s");
279 assert_eq!(format_uptime("86400s"), "1d");
280 assert_eq!(format_uptime("90061s"), "1d 1h 1m 1s");
281 assert_eq!(format_uptime("31536000s"), "1y");
282 assert_eq!(format_uptime("31626061s"), "1y 1d 1h 1m 1s");
283 assert_eq!(format_uptime("0s"), "0s");
284 }
285}