Skip to main content

cu_consolemon/
sysinfo.rs

1use libmacchina::{
2    GeneralReadout, KernelReadout, MemoryReadout, PackageReadout, traits::GeneralReadout as _,
3    traits::KernelReadout as _, traits::MemoryReadout as _, traits::PackageReadout as _,
4};
5
6use pfetch_logo_parser::{Color, Logo, LogoPart};
7use std::{env, fmt::Display, str::FromStr};
8
9#[derive(Debug, PartialEq)]
10pub enum PfetchInfo {
11    Os,
12    Host,
13    Kernel,
14    Uptime,
15    Cpu,
16    Memory,
17    Shell,
18    Editor,
19    Wm,
20    De,
21}
22
23impl Display for PfetchInfo {
24    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25        write!(f, "{}", format!("{self:?}").to_lowercase())
26    }
27}
28
29impl FromStr for PfetchInfo {
30    type Err = String;
31
32    fn from_str(info: &str) -> Result<Self, Self::Err> {
33        match info {
34            "os" => Ok(PfetchInfo::Os),
35            "host" => Ok(PfetchInfo::Host),
36            "kernel" => Ok(PfetchInfo::Kernel),
37            "uptime" => Ok(PfetchInfo::Uptime),
38            "cpu" => Ok(PfetchInfo::Cpu),
39            "memory" => Ok(PfetchInfo::Memory),
40            "shell" => Ok(PfetchInfo::Shell),
41            "editor" => Ok(PfetchInfo::Editor),
42            "wm" => Ok(PfetchInfo::Wm),
43            "de" => Ok(PfetchInfo::De),
44            unknown_info => Err(format!("Unknown pfetch info: {unknown_info}")),
45        }
46    }
47}
48
49// Struct for the various readouts
50pub struct Readouts {
51    pub general_readout: GeneralReadout,
52    pub package_readout: PackageReadout,
53    pub memory_readout: MemoryReadout,
54    pub kernel_readout: KernelReadout,
55}
56
57impl Default for Readouts {
58    fn default() -> Self {
59        Readouts {
60            general_readout: GeneralReadout::new(),
61            package_readout: PackageReadout::new(),
62            memory_readout: MemoryReadout::new(),
63            kernel_readout: KernelReadout::new(),
64        }
65    }
66}
67
68pub fn get_info(info: &PfetchInfo, readouts: &Readouts) -> Option<String> {
69    match info {
70        PfetchInfo::Os => pfetch::os(&readouts.general_readout),
71        PfetchInfo::Host => pfetch::host(&readouts.general_readout),
72        PfetchInfo::Kernel => pfetch::kernel(&readouts.kernel_readout),
73        PfetchInfo::Uptime => pfetch::uptime(&readouts.general_readout),
74        PfetchInfo::Cpu => pfetch::cpu(&readouts.general_readout),
75        PfetchInfo::Memory => pfetch::memory(&readouts.memory_readout),
76        PfetchInfo::Shell => pfetch::shell(&readouts.general_readout),
77        PfetchInfo::Editor => Some(env::var("EDITOR").unwrap_or_else(|_| "nvim".into())),
78        PfetchInfo::Wm => pfetch::wm(&readouts.general_readout),
79        PfetchInfo::De => pfetch::de(&readouts.general_readout),
80    }
81}
82
83// Function to render the gathered info along with ASCII logo
84pub fn pfetch_info() -> String {
85    let readouts = Readouts::default();
86
87    let os = pfetch::os(&readouts.general_readout).unwrap_or_default();
88
89    let all_infos = [
90        PfetchInfo::Os,
91        PfetchInfo::Host,
92        PfetchInfo::Kernel,
93        PfetchInfo::Uptime,
94        PfetchInfo::Cpu,
95        PfetchInfo::Memory,
96        PfetchInfo::Shell,
97        PfetchInfo::Editor,
98        PfetchInfo::Wm,
99        PfetchInfo::De,
100    ];
101
102    let logo = pfetch::logo(&os);
103    let gathered_pfetch_info: Vec<(Color, String, String)> = all_infos
104        .iter()
105        .filter_map(|info| match info {
106            PfetchInfo::Os => Some((logo.primary_color, info.to_string(), os.clone())),
107            _ => get_info(info, &readouts)
108                .map(|info_str| (logo.primary_color, info.to_string(), info_str)),
109        })
110        .collect();
111
112    pfetch(gathered_pfetch_info, logo, true)
113}
114
115fn env_usize(name: &str, default: usize) -> usize {
116    dotenvy::var(name)
117        .ok()
118        .and_then(|value| value.parse::<usize>().ok())
119        .unwrap_or(default)
120}
121
122fn pfetch(info: Vec<(Color, String, String)>, logo: Logo, logo_enabled: bool) -> String {
123    let raw_logo = if logo_enabled {
124        logo.logo_parts
125            .iter()
126            .map(|LogoPart { content, .. }| content.as_ref())
127            .collect::<String>()
128    } else {
129        "".into()
130    };
131    let color_enabled = dotenvy::var("PF_COLOR").unwrap_or_default() != "0";
132    let logo = if color_enabled {
133        logo.to_string()
134    } else {
135        format!("{logo:#}")
136    };
137    let mut logo_lines = logo.lines();
138    let raw_logo_lines: Vec<_> = raw_logo.lines().collect();
139    let logo_width = raw_logo_lines
140        .iter()
141        .map(|line| line.chars().count())
142        .max()
143        .unwrap_or(0);
144    let line_amount = usize::max(raw_logo_lines.len(), info.len());
145
146    let info1_width = info
147        .iter()
148        .skip(1)
149        .map(|(_, line, _)| {
150            if line.starts_with("\x1b[4") {
151                // exclude palette from info1 width
152                0
153            } else {
154                line.len()
155            }
156        })
157        .max()
158        .unwrap_or(0);
159
160    let padding1 = env_usize("PF_PAD1", 0);
161    let padding2 = env_usize("PF_PAD2", 3);
162    let padding3 = env_usize("PF_PAD3", 1);
163
164    let mut pfetch_str = String::new();
165
166    for l in 0..line_amount {
167        pfetch_str += &format!(
168            "{padding1}{bold}{logo}{padding2}{color}{info1}{nobold}{separator}{padding3}{color2}{info2}\n",
169            padding1 = " ".repeat(padding1),
170            bold = if color_enabled { "\x1b[1m" } else { "" },
171            logo = if logo_enabled {
172                logo_lines.next().unwrap_or("")
173            } else {
174                ""
175            },
176            padding2 = " ".repeat(
177                logo_width - raw_logo_lines.get(l).map_or(0, |line| line.chars().count())
178                    + if logo_enabled { padding2 } else { 0 }
179            ),
180            color = if color_enabled {
181                info.get(l).map_or("".to_owned(), |line| {
182                    let (color, _, _) = line;
183                    color.to_string()
184                })
185            } else {
186                "".into()
187            },
188            info1 = info.get(l).map_or("", |line| &line.1),
189            nobold = if color_enabled { "\x1b[0m" } else { "" },
190            separator = info
191                .get(l)
192                .map_or("".into(), |line| if !&line.2.is_empty() {
193                    dotenvy::var("PF_SEP").unwrap_or_default()
194                } else {
195                    "".into()
196                }),
197            padding3 = " ".repeat(
198                info1_width.saturating_sub(info.get(l).map_or(0, |(_, line, _)| line.len()))
199                    + padding3
200            ),
201            color2 = if color_enabled {
202                match dotenvy::var("PF_COL2") {
203                    Ok(newcolor) => match Color::from_str(&newcolor) {
204                        Ok(newcolor) => format!("{newcolor}"),
205                        Err(_) => "".into(),
206                    },
207                    Err(_) => "".into(),
208                }
209            } else {
210                "".into()
211            },
212            info2 = info.get(l).map_or("", |line| &line.2)
213        )
214    }
215
216    pfetch_str
217}