Merge pull request #217 from Crypto-Spartan/zip-mem-warnings
[ouch.git] / src / utils / formatting.rs
blob5a189db20cbd11e89f52bba4f58d486884149c09
1 use std::{
2     borrow::Cow,
3     cmp,
4     ffi::OsStr,
5     path::{Component, Path},
6 };
8 /// Converts an OsStr to utf8 with custom formatting.
9 ///
10 /// This is different from [`Path::display`].
11 ///
12 /// See <https://gist.github.com/marcospb19/ebce5572be26397cf08bbd0fd3b65ac1> for a comparison.
13 pub fn to_utf(os_str: impl AsRef<OsStr>) -> String {
14     let text = format!("{:?}", os_str.as_ref());
15     text.trim_matches('"').to_string()
18 /// Removes the current dir from the beginning of a path
19 /// normally used for presentation sake.
20 /// If this function fails, it will return source path as a PathBuf.
21 pub fn strip_cur_dir(source_path: &Path) -> &Path {
22     source_path.strip_prefix(Component::CurDir).unwrap_or(source_path)
25 /// Converts a slice of AsRef<OsStr> to comma separated String
26 ///
27 /// Panics if the slice is empty.
28 pub fn concatenate_os_str_list(os_strs: &[impl AsRef<OsStr>]) -> String {
29     let mut iter = os_strs.iter().map(AsRef::as_ref);
31     let mut string = to_utf(iter.next().unwrap()); // May panic
33     for os_str in iter {
34         string += ", ";
35         string += &to_utf(os_str);
36     }
37     string
40 /// Display the directory name, but change to "current directory" when necessary.
41 pub fn nice_directory_display(os_str: impl AsRef<OsStr>) -> Cow<'static, str> {
42     if os_str.as_ref() == "." {
43         Cow::Borrowed("current directory")
44     } else {
45         let text = to_utf(os_str);
46         Cow::Owned(format!("'{}'", text))
47     }
50 /// Struct useful to printing bytes as kB, MB, GB, etc.
51 pub struct Bytes {
52     bytes: f64,
55 impl Bytes {
56     const UNIT_PREFIXES: [&'static str; 6] = ["", "k", "M", "G", "T", "P"];
58     /// Create a new Bytes.
59     pub fn new(bytes: u64) -> Self {
60         Self { bytes: bytes as f64 }
61     }
64 impl std::fmt::Display for Bytes {
65     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
66         let num = self.bytes;
67         debug_assert!(num >= 0.0);
68         if num < 1_f64 {
69             return write!(f, "{} B", num);
70         }
71         let delimiter = 1000_f64;
72         let exponent = cmp::min((num.ln() / 6.90775).floor() as i32, 4);
74         write!(f, "{:.2} ", num / delimiter.powi(exponent))?;
75         write!(f, "{}B", Bytes::UNIT_PREFIXES[exponent as usize])
76     }
79 #[cfg(test)]
80 mod tests {
81     use super::*;
83     #[test]
84     fn test_pretty_bytes_formatting() {
85         fn format_bytes(bytes: u64) -> String {
86             format!("{}", Bytes::new(bytes))
87         }
88         let b = 1;
89         let kb = b * 1000;
90         let mb = kb * 1000;
91         let gb = mb * 1000;
93         assert_eq!("0 B", format_bytes(0)); // This is weird
94         assert_eq!("1.00 B", format_bytes(b));
95         assert_eq!("999.00 B", format_bytes(b * 999));
96         assert_eq!("12.00 MB", format_bytes(mb * 12));
97         assert_eq!("123.00 MB", format_bytes(mb * 123));
98         assert_eq!("5.50 MB", format_bytes(mb * 5 + kb * 500));
99         assert_eq!("7.54 GB", format_bytes(gb * 7 + 540 * mb));
100         assert_eq!("1.20 TB", format_bytes(gb * 1200));
102         // bytes
103         assert_eq!("234.00 B", format_bytes(234));
104         assert_eq!("999.00 B", format_bytes(999));
105         // kilobytes
106         assert_eq!("2.23 kB", format_bytes(2234));
107         assert_eq!("62.50 kB", format_bytes(62500));
108         assert_eq!("329.99 kB", format_bytes(329990));
109         // megabytes
110         assert_eq!("2.75 MB", format_bytes(2750000));
111         assert_eq!("55.00 MB", format_bytes(55000000));
112         assert_eq!("987.65 MB", format_bytes(987654321));
113         // gigabytes
114         assert_eq!("5.28 GB", format_bytes(5280000000));
115         assert_eq!("95.20 GB", format_bytes(95200000000));
116         assert_eq!("302.00 GB", format_bytes(302000000000));
117     }