1use std::cell::RefCell;
16use std::cell::RefMut;
17use std::io;
18use std::io::Read;
19use std::time::Duration;
20use std::time::SystemTime;
21use std::time::UNIX_EPOCH;
22
23use chrono::prelude::*;
25
26const BELOW_RC: &str = "/.config/below/belowrc";
27
28#[macro_export]
32macro_rules! every_n {
33 ($n:expr, $ex:expr) => {{
34 static COUNT: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(0);
35 let p = COUNT.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
36 if p % ($n) == 0 {
37 $ex
38 }
39 }};
40}
41pub use every_n;
42
43pub fn get_unix_timestamp(timestamp: SystemTime) -> u64 {
45 timestamp
46 .duration_since(SystemTime::UNIX_EPOCH)
47 .expect("SystemTime before UNIX EPOCH!")
48 .as_secs()
49}
50
51pub fn get_system_time(timestamp: u64) -> SystemTime {
53 UNIX_EPOCH + Duration::from_secs(timestamp)
54}
55
56fn convert(val: f64, base: f64, units: &[&'static str]) -> String {
57 if val < 1_f64 {
58 return format!("{:.1} {}", val, units[0]);
59 }
60 let exponent = std::cmp::min(
61 (val.ln() / base.ln()).floor() as i32,
62 (units.len() - 1) as i32,
63 );
64 let pretty_val = format!("{:.1}", val / base.powi(exponent))
65 .parse::<f64>()
66 .unwrap()
67 * 1_f64;
68 let unit = units[exponent as usize];
69 format!("{} {}", pretty_val, unit)
70}
71
72pub fn convert_bytes(val: f64) -> String {
74 const UNITS: &[&str] = &["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
75 convert(val, 1024_f64, UNITS)
76}
77
78pub fn convert_freq(val: u64) -> String {
80 const UNITS: &[&str] = &["Hz", "kHz", "MHz", "GHz", "THz", "PHz", "EHz", "ZHz", "YHz"];
81 let val_f64 = val as f64;
82 convert(val_f64, 1000_f64, UNITS)
83}
84
85pub fn convert_duration(val: u64) -> String {
87 const UNITS: &[&str] = &["us", "ms", "s"];
88 convert(val as f64, 1000_f64, UNITS)
89}
90
91pub fn get_prefix(collapsed: bool) -> &'static str {
92 if collapsed { "└+ " } else { "└─ " }
93}
94
95pub fn fold_string<F>(val: &str, width: usize, start_idx: usize, stop_filter: F) -> String
112where
113 F: FnMut(char) -> bool,
114{
115 let str_len = val.len();
116 if start_idx >= str_len || val[start_idx..].len() <= width || width <= 3 {
117 return val.into();
118 }
119
120 let first_symbo_pos = val[start_idx..].find(stop_filter).unwrap_or(str_len) + 1;
121 let mid_str_len = (width - 3) >> 1;
122 let front_len = std::cmp::min(first_symbo_pos, mid_str_len);
123 let front_string = val[..front_len].to_string();
124 let back_string = val[str_len - width + front_len + 3..].to_string();
125 format!("{}...{}", front_string, back_string)
126}
127
128pub fn timestamp_to_datetime(timestamp: &i64) -> String {
130 let naive = NaiveDateTime::from_timestamp_opt(*timestamp, 0).unwrap();
131 let datetime = naive.and_utc();
132 datetime
133 .with_timezone(&Local)
134 .format("%Y-%m-%d %H:%M:%S")
135 .to_string()
136}
137
138pub fn systemtime_to_datetime(system_time: SystemTime) -> String {
140 timestamp_to_datetime(&(get_unix_timestamp(system_time) as i64))
141}
142
143pub fn is_cpu_significant(v: f64) -> Option<cursive::theme::BaseColor> {
144 if v > 100.0 {
145 Some(cursive::theme::BaseColor::Red)
146 } else {
147 None
148 }
149}
150
151pub fn get_belowrc_filename() -> String {
153 format!(
154 "{}{}",
155 std::env::var("HOME").expect("Fail to obtain HOME env var"),
156 BELOW_RC
157 )
158}
159
160pub fn get_belowrc_dump_section_key() -> &'static str {
162 "dump"
163}
164
165pub fn get_belowrc_cmd_section_key() -> &'static str {
167 "cmd"
168}
169
170pub fn get_belowrc_view_section_key() -> &'static str {
172 "view"
173}
174
175pub fn read_kern_file_to_internal_buffer<R: Read>(
176 buffer: &RefCell<Vec<u8>>,
177 mut reader: R,
178) -> io::Result<RefMut<'_, str>> {
179 const BUFFER_CHUNK_SIZE: usize = 1 << 16;
180
181 let mut buffer = buffer.borrow_mut();
182 let mut total_read = 0;
183
184 loop {
185 let buf_len = buffer.len();
186 if buf_len < total_read + BUFFER_CHUNK_SIZE {
187 buffer.resize(buf_len + BUFFER_CHUNK_SIZE, 0);
188 }
189
190 match reader.read(&mut buffer[total_read..]) {
191 Ok(0) => break,
192 Ok(n) => {
193 total_read += n;
194 }
197 Err(e) if e.kind() == io::ErrorKind::Interrupted => continue,
198 Err(e) => return Err(e),
199 }
200 }
201
202 RefMut::filter_map(buffer, |vec| {
203 std::str::from_utf8_mut(&mut vec[..total_read]).ok()
204 })
205 .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "Invalid UTF-8 data"))
206}
207
208#[cfg(test)]
209mod test {
210 use super::*;
211
212 #[test]
213 fn test_every_n() {
214 let mut v1 = Vec::new();
215 let mut v2 = Vec::new();
216 for i in 0..10 {
217 every_n!(2, v1.push(i));
218 every_n!(1 + 2, v2.push(i));
219 }
220 assert_eq!(v1, vec![0, 2, 4, 6, 8]);
221 assert_eq!(v2, vec![0, 3, 6, 9]);
222 }
223
224 #[test]
225 fn test_convert_bytes() {
226 assert_eq!(convert_bytes(0.0), "0.0 B".to_owned());
228 assert_eq!(convert_bytes(1_024.0), "1 KB".to_owned());
229 assert_eq!(convert_bytes(1_023.0), "1023 B".to_owned());
230 assert_eq!(convert_bytes(1_076.0), "1.1 KB".to_owned());
231 assert_eq!(convert_bytes(10_239.0), "10 KB".to_owned());
232 assert_eq!(convert_bytes(1024_f64.powi(2)), "1 MB".to_owned());
233 assert_eq!(convert_bytes(1024_f64.powi(2) - 1.0), "1024 KB".to_owned());
235 assert_eq!(convert_bytes(1024_f64.powi(3) - 1.0), "1024 MB".to_owned());
237 assert_eq!(convert_bytes(1024_f64.powi(3)), "1 GB".to_owned());
238 assert_eq!(convert_bytes(1024_f64.powi(4)), "1 TB".to_owned());
239 }
240
241 #[test]
242 fn test_convert_freq() {
243 assert_eq!(convert_freq(0), "0.0 Hz".to_owned());
245 assert_eq!(convert_freq(1_000), "1 kHz".to_owned());
246 assert_eq!(convert_freq(999), "999 Hz".to_owned());
247 assert_eq!(convert_freq(1_050), "1.1 kHz".to_owned());
248 assert_eq!(convert_freq(9_999), "10 kHz".to_owned());
249 assert_eq!(convert_freq(1_000_000), "1 MHz".to_owned());
250 assert_eq!(convert_freq(999_950_000), "1000 MHz".to_owned());
252 assert_eq!(convert_freq(1_000_000_000), "1 GHz".to_owned());
253 assert_eq!(convert_freq(1_000_000_000_000), "1 THz".to_owned());
254 }
255}