Skip to main content

leenfetch_core/modules/
helper.rs

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