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