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