1use clap::{ArgAction, Parser, ValueEnum};
2use std::collections::{HashMap, HashSet};
3
4#[derive(Default, Debug)]
6pub struct CliOverrides {
7 pub flags: HashMap<String, String>,
8 pub only_modules: Option<Vec<String>>,
9 pub hide_modules: HashSet<String>,
10 pub config_path: Option<String>,
11 pub use_defaults: bool,
12 pub output_format: OutputFormat,
13 pub ssh_hosts: Vec<String>,
14}
15
16impl CliOverrides {
17 fn set_bool(&mut self, key: &str, value: bool) {
18 self.flags.insert(
19 key.to_string(),
20 if value { "true" } else { "false" }.to_string(),
21 );
22 }
23
24 fn set_string(&mut self, key: &str, value: String) {
25 self.flags.insert(key.to_string(), value);
26 }
27}
28
29#[derive(Clone, Copy, Debug, PartialEq, Eq, ValueEnum)]
30pub enum OutputFormat {
31 Pretty,
32 Json,
33}
34
35impl Default for OutputFormat {
36 fn default() -> Self {
37 OutputFormat::Pretty
38 }
39}
40
41#[derive(Parser, Debug)]
42#[command(
43 name = "leenfetch",
44 about = "Minimal, stylish system info for your terminal",
45 version,
46 disable_help_flag = true
47)]
48pub struct Args {
49 #[arg(short = 'h', long = "help", action = ArgAction::SetTrue)]
51 pub help: bool,
52
53 #[arg(long, value_enum, default_value_t = OutputFormat::Pretty)]
55 pub format: OutputFormat,
56
57 #[arg(short = 'i', long, action = ArgAction::SetTrue)]
59 pub init: bool,
60
61 #[arg(short = 'r', long, action = ArgAction::SetTrue)]
63 pub reinit: bool,
64
65 #[arg(short = 'l', long = "list-options", action = ArgAction::SetTrue)]
67 pub list_options: bool,
68
69 #[arg(long = "config")]
71 pub config_path: Option<String>,
72
73 #[arg(long = "no-config", action = ArgAction::SetTrue)]
75 pub no_config: bool,
76
77 #[arg(long = "ascii_distro")]
78 pub ascii_distro: Option<String>,
79 #[arg(long = "ascii_colors")]
80 pub ascii_colors: Option<String>,
81 #[arg(long = "custom_ascii_path")]
82 pub custom_ascii_path: Option<String>,
83 #[arg(long = "battery_display")]
84 pub battery_display: Option<String>,
85 #[arg(long = "disk_display")]
86 pub disk_display: Option<String>,
87 #[arg(long = "disk_subtitle")]
88 pub disk_subtitle: Option<String>,
89 #[arg(long = "memory_unit")]
90 pub memory_unit: Option<String>,
91 #[arg(long = "package_managers", alias = "packages")]
92 pub package_managers: Option<String>,
93 #[arg(long = "uptime_shorthand", alias = "uptime")]
94 pub uptime_shorthand: Option<String>,
95 #[arg(long = "os_age_shorthand")]
96 pub os_age_shorthand: Option<String>,
97 #[arg(long = "distro_shorthand", alias = "distro-display")]
98 pub distro_shorthand: Option<String>,
99 #[arg(long = "color_blocks")]
100 pub color_blocks: Option<String>,
101 #[arg(long = "cpu_temp", alias = "cpu-temp-unit")]
102 pub cpu_temp: Option<String>,
103 #[arg(long = "only")]
104 pub only_modules: Option<String>,
105 #[arg(long = "hide")]
106 pub hide_modules: Option<String>,
107 #[arg(long = "disable")]
108 pub disable_modules: Option<String>,
109
110 #[arg(long = "memory_percent")]
111 pub memory_percent: Option<bool>,
112 #[arg(long = "cpu_speed")]
113 pub cpu_speed: Option<bool>,
114 #[arg(long = "cpu_frequency")]
115 pub cpu_frequency: Option<bool>,
116 #[arg(long = "cpu_cores")]
117 pub cpu_cores: Option<bool>,
118 #[arg(long = "cpu_brand")]
119 pub cpu_brand: Option<bool>,
120 #[arg(long = "shell_path")]
121 pub shell_path: Option<bool>,
122 #[arg(long = "shell_version")]
123 pub shell_version: Option<bool>,
124 #[arg(long = "de_version")]
125 pub de_version: Option<bool>,
126 #[arg(long = "gpu_brand")]
127 pub gpu_brand: Option<bool>,
128 #[arg(long = "kernel_shorthand")]
129 pub kernel_shorthand: Option<bool>,
130 #[arg(long = "speed_shorthand")]
131 pub speed_shorthand: Option<bool>,
132 #[arg(long = "disk_percent")]
133 pub disk_percent: Option<bool>,
134 #[arg(long = "gpu_type")]
135 pub gpu_type: Option<String>,
136 #[arg(long = "disk_show")]
137 pub disk_show: Option<String>,
138
139 #[arg(long = "ssh", value_name = "HOST")]
141 pub ssh_hosts: Vec<String>,
142
143 #[arg(long = "print_config", action = ArgAction::SetTrue)]
145 pub print_config: bool,
146}
147
148impl Args {
149 pub fn into_overrides(self) -> CliOverrides {
150 let mut overrides = CliOverrides::default();
151 overrides.use_defaults = self.no_config;
152 overrides.config_path = self.config_path.clone();
153 overrides.output_format = self.format;
154
155 if let Some(val) = self.ascii_distro {
156 overrides.set_string("ascii_distro", val);
157 }
158 if let Some(val) = self.ascii_colors {
159 overrides.set_string("ascii_colors", val);
160 }
161 if let Some(val) = self.custom_ascii_path {
162 overrides.set_string("custom_ascii_path", val);
163 }
164 if let Some(val) = self.battery_display {
165 overrides.set_string("battery_display", val);
166 }
167 if let Some(val) = self.disk_display {
168 overrides.set_string("disk_display", val);
169 }
170 if let Some(val) = self.disk_subtitle {
171 overrides.set_string("disk_subtitle", val);
172 }
173 if let Some(val) = self.memory_unit {
174 overrides.set_string("memory_unit", val);
175 }
176 if let Some(val) = self.package_managers {
177 overrides.set_string("package_managers", val);
178 }
179 if let Some(val) = self.uptime_shorthand {
180 overrides.set_string("uptime_shorthand", val);
181 }
182 if let Some(val) = self.os_age_shorthand {
183 overrides.set_string("os_age_shorthand", val);
184 }
185 if let Some(val) = self.distro_shorthand {
186 overrides.set_string("distro_shorthand", val);
187 }
188 if let Some(val) = self.color_blocks {
189 overrides.set_string("color_blocks", val);
190 }
191 if let Some(val) = self.cpu_temp {
192 overrides.set_string("cpu_temp", val);
193 }
194 if let Some(val) = self.gpu_type {
195 overrides.set_string("gpu_type", val);
196 }
197 if let Some(val) = self.disk_show {
198 overrides.set_string("disk_show", val);
199 }
200
201 if let Some(only) = self.only_modules {
202 let modules = only
203 .split(',')
204 .map(|item| item.trim().to_string())
205 .filter(|item| !item.is_empty())
206 .collect::<Vec<_>>();
207 overrides.only_modules = if modules.is_empty() {
208 None
209 } else {
210 Some(modules)
211 };
212 }
213
214 if let Some(hide) = self.hide_modules {
215 for entry in hide.split(',') {
216 let trimmed = entry.trim();
217 if !trimmed.is_empty() {
218 overrides.hide_modules.insert(trimmed.to_string());
219 }
220 }
221 }
222
223 if let Some(disable) = self.disable_modules {
224 for entry in disable.split(',') {
225 let trimmed = entry.trim();
226 if !trimmed.is_empty() {
227 overrides.hide_modules.insert(trimmed.to_string());
228 }
229 }
230 }
231
232 apply_bool_override(&mut overrides, "memory_percent", self.memory_percent);
233 apply_bool_override(&mut overrides, "cpu_speed", self.cpu_speed);
234 apply_bool_override(&mut overrides, "cpu_frequency", self.cpu_frequency);
235 apply_bool_override(&mut overrides, "cpu_cores", self.cpu_cores);
236 apply_bool_override(&mut overrides, "cpu_brand", self.cpu_brand);
237 apply_bool_override(&mut overrides, "shell_path", self.shell_path);
238 apply_bool_override(&mut overrides, "shell_version", self.shell_version);
239 apply_bool_override(&mut overrides, "de_version", self.de_version);
240 apply_bool_override(&mut overrides, "gpu_brand", self.gpu_brand);
241 apply_bool_override(&mut overrides, "kernel_shorthand", self.kernel_shorthand);
242 apply_bool_override(&mut overrides, "speed_shorthand", self.speed_shorthand);
243 apply_bool_override(&mut overrides, "disk_percent", self.disk_percent);
244
245 overrides.ssh_hosts = self.ssh_hosts.clone();
246
247 overrides
248 }
249}
250
251fn apply_bool_override(overrides: &mut CliOverrides, key: &str, value: Option<bool>) {
252 if let Some(value) = value {
253 overrides.set_bool(key, value);
254 }
255}
256
257pub fn print_custom_help() {
258 println!(
259 "{}",
260 r#"🧠 leenfetch — Minimal, Stylish System Info for Your Terminal
261
262USAGE:
263 leenfetch [OPTIONS]
264
265OPTIONS:
266 -V, --version Print version information and exit
267 -h, --help Show this help message and exit
268 -i, --init Create the default config file in ~/.config/leenfetch/
269 -r, --reinit Reinitialize the config file to defaults
270 -l, --list-options Show all available config options and values
271 --config <path> Load configuration from a custom file
272 --no-config Ignore config files and use built-in defaults
273 --ssh <host> Fetch info from remote hosts via SSH (repeatable)
274 --format <kind> Output format: pretty (default) or json
275
276 --ascii_distro <s> Override detected distro (e.g., ubuntu, arch, arch_small)
277 --ascii_colors <s> Override color palette (e.g., 2,7,3 or "distro")
278 --custom_ascii_path <p> Use ASCII art from the given file path
279
280 --battery_display <mode> Battery output style (off, bar, infobar, barinfo)
281 --disk_display <mode> Disk output style (info, percentage, infobar, barinfo, bar)
282 --disk_subtitle <mode> Disk subtitle (name, dir, none, mount)
283 --disk_percent <bool> Show disk percentage
284 --disk_show <path> Which disks to display (comma-separated mount points)
285 --memory_unit <unit> Force memory unit (kib, mib, gib)
286 --package_managers <mode> Package summary verbosity (off, on, tiny)
287 --uptime_shorthand <mode> Uptime shorthand (full, tiny, seconds)
288 --os_age_shorthand <mode> OS age shorthand (full, tiny, seconds)
289 --distro_shorthand <mode> Distro detail level (name, name_version, ...)
290 --color_blocks <glyph> Glyph used for color swatches
291 --cpu_temp <unit> CPU temperature unit (C, F, off)
292 --gpu_type <type> Which GPU to display (all, dedicated, integrated)
293 --only <list> Render only listed modules (comma-separated)
294 --hide <list> Hide listed modules (comma-separated)
295 --disable <list> Alias for --hide
296
297 --memory_percent <true|false>
298 --cpu_speed <true|false>
299 --cpu_frequency <true|false>
300 --cpu_cores <true|false>
301 --cpu_brand <true|false>
302 --shell_path <true|false>
303 --shell_version <true|false>
304 --de_version <true|false>
305 --gpu_brand <true|false>
306 --kernel_shorthand <true|false>
307 --speed_shorthand <true|false>
308 --disk_percent <true|false>
309
310DESCRIPTION:
311 leenfetch is a modern, minimal, and the fastest system info tool,
312 written in Rust, designed for terminal enthusiasts.
313
314 It fetches and prints system information like:
315 • OS, Kernel, Uptime
316 • CPU, GPU, Memory, Disks
317 • Shell, WM, DE, Theme
318 • Resolution, Battery, Current Song
319
320 🛠️ Configuration:
321 • Linux: ~/.config/leenfetch/config.jsonc
322 • Windows: %APPDATA%/leenfetch/config.jsonc
323 One JSONC file with inline comments covering flags and a Fastfetch-style modules array.
324 Edit it to control appearance, spacing (via "break" entries), and output order.
325
326EXAMPLES:
327 leenfetch 🚀 Run normally with your config
328 leenfetch --init 🔧 Create the default config file
329 leenfetch --ssh user@server 🌐 Fetch from a remote host over SSH
330 leenfetch --ssh host1 --ssh host2 🛰️ Fetch multiple hosts sequentially
331 leenfetch --ascii_distro arch 🎨 Use Arch logo manually
332 leenfetch --ascii_colors 2,7,3 🌈 Use custom colors
333 leenfetch --package_managers tiny 📦 Compact package summary for screenshots
334 leenfetch --only cpu,memory 🧩 Focus on specific modules temporarily
335 leenfetch --list-options 📜 View all available configuration keys
336
337TIPS:
338 • Adjust styles in the `flags` section (e.g., ascii_distro, disk_display, battery_display)
339 • Reorder entries in the `modules` array (use "break" for spacing)
340 • Tweak `logo.padding` to add margins around the ASCII art
341
342For more, see the README or run `leenfetch --list-options`.
343 "#
344 );
345}
346
347pub fn list_options() {
348 println!(
349 "{}",
350 r#"
351
352📄 leenfetch Configuration Options Reference
353──────────────────────────────────────────────
354
355📁 leenfetch stores everything in a single JSONC file:
356 • Linux: ~/.config/leenfetch/config.jsonc
357 • Windows: %APPDATA%/leenfetch/config.jsonc
358
359🗂️ Sections inside config.jsonc:
360 • 🖼️ flags — Display and formatting options
361 • 🧱 modules — Output order and custom rows
362──────────────────────────────────────────────
363🖼️ flags — Display and Formatting Options
364──────────────────────────────────────────────
365 ascii_distro = "auto" | <name>
366 Which ASCII art to use. "auto" detects your distro or specify a distro name (e.g., "arch").
367
368 ascii_colors = "distro" | <list>
369 Color palette for ASCII art. "distro" uses default, or provide a comma-separated list (e.g., "1,2,3,4").
370
371 custom_ascii_path = "" | <path>
372 Path to a custom ASCII art file. Empty for default.
373
374 battery_display = "off" | "bar" | "infobar" | "barinfo"
375 How to show battery info: none, bar only, info+bar, or bar+info.
376
377 color_blocks = <string>
378 String used for color blocks (e.g., "███", "\#\#\#").
379
380 cpu_brand = true | false
381 Show CPU brand name.
382
383 cpu_cores = true | false
384 Show CPU core count.
385
386 cpu_frequency = true | false
387 Show CPU frequency.
388
389 cpu_speed = true | false
390 Show CPU speed.
391
392 cpu_temp = "C" | "F" | "off"
393 Temperature unit for CPU: Celsius, Fahrenheit, or off.
394
395 de_version = true | false
396 Show desktop environment version.
397
398 distro_shorthand = "name" | "name_version" | "name_arch" | "name_model" | "name_model_version" | "name_model_arch" | "name_model_version_arch"
399 How much detail to show for OS info.
400
401 disk_display = "info" | "percentage" | "infobar" | "barinfo" | "bar"
402 Disk usage display style.
403
404 disk_percent = true | false
405 Show disk usage percentage.
406
407 disk_show = <path>
408 Which disks to display (default "/").
409
410 disk_subtitle = "name" | "dir" | "none" | "mount"
411 Disk label: device, last dir, none, or full mount point.
412
413 gpu_brand = true | false
414 Show GPU vendor name.
415
416 gpu_type = "all" | "dedicated" | "integrated"
417 Which GPU to display.
418
419 kernel_shorthand = true | false
420 Shorten kernel output.
421
422 memory_percent = true | false
423 Show memory as percent.
424
425 memory_unit = "mib" | "gib" | "kib"
426 Memory unit.
427
428 package_managers = "off" | "on" | "tiny"
429 Package info: none, full, or compact.
430
431 shell_path = true | false
432 Show full shell path.
433
434 shell_version = true | false
435 Show shell version.
436
437 speed_shorthand = true | false
438 Show CPU speed without decimals.
439
440 uptime_shorthand = "full" | "tiny" | "seconds"
441 Uptime format: verbose, compact, or seconds only.
442
443 os_age_shorthand = "full" | "tiny" | "seconds"
444 Format for the OS install age module.
445
446──────────────────────────────────────────────
447🖼 logo — ASCII Art Overrides
448──────────────────────────────────────────────
449 type = "auto" | "file"
450 Select built-in art or load from disk.
451
452 source = <path>
453 Path to a custom ASCII art file (used when type is "file").
454
455 padding.top = <number>
456 Add blank lines above the ASCII logo.
457
458 padding.right = <number>
459 Add spacing between the ASCII logo and information column.
460
461 padding.left = <number>
462 Indent the ASCII logo horizontally.
463
464──────────────────────────────────────────────
465🧱 modules — Output Order and Custom Rows
466──────────────────────────────────────────────
467 Each entry is either:
468 • "break" — insert a blank spacer line
469 • { "type": <field>, "key": <label>, ... } — render a module
470 • { "type": "custom", "text": "hello" } — literal text
471
472 Common module fields:
473 - "titles", "os", "distro", "model", "kernel", "os_age"
474 - "uptime", "packages", "shell", "wm", "de", "cpu", "gpu"
475 - "memory", "disk", "resolution", "theme", "battery", "song", "colors"
476"#
477 );
478}