1use std::{ffi::CStr, fmt::Write as _, io, mem::MaybeUninit};
2
3use crate::{UtsName, colors::COLORS, syscall::read_file_fast};
4
5#[must_use]
6#[cfg_attr(feature = "hotpath", hotpath::measure)]
7pub fn get_username_and_hostname(utsname: &UtsName) -> String {
8 let username = unsafe {
9 let ptr = libc::getenv(c"USER".as_ptr());
10 if ptr.is_null() {
11 "unknown_user"
12 } else {
13 CStr::from_ptr(ptr).to_str().unwrap_or("unknown_user")
14 }
15 };
16 let hostname = utsname.nodename().to_str().unwrap_or("unknown_host");
17
18 let capacity = COLORS.yellow.len()
19 + username.len()
20 + COLORS.red.len()
21 + 1
22 + COLORS.green.len()
23 + hostname.len()
24 + COLORS.reset.len();
25 let mut result = String::with_capacity(capacity);
26
27 result.push_str(COLORS.yellow);
28 result.push_str(username);
29 result.push_str(COLORS.red);
30 result.push('@');
31 result.push_str(COLORS.green);
32 result.push_str(hostname);
33 result.push_str(COLORS.reset);
34
35 result
36}
37
38#[must_use]
39#[cfg_attr(feature = "hotpath", hotpath::measure)]
40pub fn get_shell() -> String {
41 unsafe {
42 let ptr = libc::getenv(c"SHELL".as_ptr());
43 if ptr.is_null() {
44 return "unknown_shell".into();
45 }
46
47 let bytes = CStr::from_ptr(ptr).to_bytes();
48 let start = bytes.iter().rposition(|&b| b == b'/').map_or(0, |i| i + 1);
49 let name = std::str::from_utf8_unchecked(&bytes[start..]);
50 name.into()
51 }
52}
53
54#[cfg_attr(feature = "hotpath", hotpath::measure)]
60#[allow(clippy::cast_precision_loss)]
61pub fn get_root_disk_usage() -> Result<String, io::Error> {
62 let mut vfs = MaybeUninit::uninit();
63 let path = b"/\0";
64
65 if unsafe { libc::statvfs(path.as_ptr().cast(), vfs.as_mut_ptr()) } != 0 {
66 return Err(io::Error::last_os_error());
67 }
68
69 let vfs = unsafe { vfs.assume_init() };
70 let block_size = vfs.f_bsize;
71 let total_blocks = vfs.f_blocks;
72 let available_blocks = vfs.f_bavail;
73
74 let total_size = block_size * total_blocks;
75 let used_size = total_size - (block_size * available_blocks);
76
77 let total_size = total_size as f64 / (1024.0 * 1024.0 * 1024.0);
78 let used_size = used_size as f64 / (1024.0 * 1024.0 * 1024.0);
79 let usage = (used_size / total_size) * 100.0;
80
81 let mut result = String::with_capacity(64);
82 write!(
83 result,
84 "{used_size:.2} GiB / {total_size:.2} GiB ({cyan}{usage:.0}%{reset})",
85 cyan = COLORS.cyan,
86 reset = COLORS.reset,
87 )
88 .unwrap();
89
90 Ok(result)
91}
92
93#[inline]
95fn parse_u64_fast(s: &[u8]) -> u64 {
96 let mut result = 0u64;
97 for &byte in s {
98 if byte.is_ascii_digit() {
99 result = result * 10 + u64::from(byte - b'0');
100 } else {
101 break;
102 }
103 }
104 result
105}
106
107#[cfg_attr(feature = "hotpath", hotpath::measure)]
113pub fn get_memory_usage() -> Result<String, io::Error> {
114 #[cfg_attr(feature = "hotpath", hotpath::measure)]
115 fn parse_memory_info() -> Result<(f64, f64), io::Error> {
116 let mut total_memory_kb = 0u64;
117 let mut available_memory_kb = 0u64;
118 let mut buffer = [0u8; 1024];
119
120 let bytes_read = read_file_fast("/proc/meminfo", &mut buffer)?;
122 let meminfo = &buffer[..bytes_read];
123
124 let mut offset = 0;
126 let mut found_total = false;
127 let mut found_available = false;
128
129 while offset < meminfo.len() && (!found_total || !found_available) {
130 let remaining = &meminfo[offset..];
131
132 let line_end = remaining
134 .iter()
135 .position(|&b| b == b'\n')
136 .unwrap_or(remaining.len());
137 let line = &remaining[..line_end];
138
139 if line.starts_with(b"MemTotal:") {
140 let mut pos = 9;
142 while pos < line.len() && line[pos].is_ascii_whitespace() {
143 pos += 1;
144 }
145 total_memory_kb = parse_u64_fast(&line[pos..]);
146 found_total = true;
147 } else if line.starts_with(b"MemAvailable:") {
148 let mut pos = 13;
150 while pos < line.len() && line[pos].is_ascii_whitespace() {
151 pos += 1;
152 }
153 available_memory_kb = parse_u64_fast(&line[pos..]);
154 found_available = true;
155 }
156
157 offset += line_end + 1;
158 }
159
160 #[allow(clippy::cast_precision_loss)]
161 let total_memory_gb = total_memory_kb as f64 / 1024.0 / 1024.0;
162 #[allow(clippy::cast_precision_loss)]
163 let available_memory_gb = available_memory_kb as f64 / 1024.0 / 1024.0;
164 let used_memory_gb = total_memory_gb - available_memory_gb;
165
166 Ok((used_memory_gb, total_memory_gb))
167 }
168
169 let (used_memory, total_memory) = parse_memory_info()?;
170 #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
171 let percentage_used = (used_memory / total_memory * 100.0).round() as u64;
172
173 let mut result = String::with_capacity(64);
174 write!(
175 result,
176 "{used_memory:.2} GiB / {total_memory:.2} GiB \
177 ({cyan}{percentage_used}%{reset})",
178 cyan = COLORS.cyan,
179 reset = COLORS.reset,
180 )
181 .unwrap();
182
183 Ok(result)
184}