disty_cli/
units.rs

1use crate::formatting::Format;
2
3#[derive(Clone, Copy, clap::ValueEnum)]
4pub enum Unit {
5    // Time units
6    #[value(name = "ns")]
7    Nanoseconds,
8    #[value(name = "us")]
9    Microseconds,
10    #[value(name = "ms")]
11    Milliseconds,
12    #[value(name = "s")]
13    Seconds,
14
15    // Byte units (decimal)
16    #[value(name = "B")]
17    Bytes,
18    #[value(name = "KB")]
19    Kilobytes,
20    #[value(name = "MB")]
21    Megabytes,
22    #[value(name = "GB")]
23    Gigabytes,
24    #[value(name = "TB")]
25    Terabytes,
26    #[value(name = "PB")]
27    Petabytes,
28
29    // Byte units (binary)
30    #[value(name = "KiB")]
31    Kibibytes,
32    #[value(name = "MiB")]
33    Mebibytes,
34    #[value(name = "GiB")]
35    Gibibytes,
36    #[value(name = "TiB")]
37    Tebibytes,
38    #[value(name = "PiB")]
39    Pebibytes,
40}
41
42impl Unit {
43    /// Get the scale factor to convert from this unit to base unit
44    pub fn scale(&self) -> f64 {
45        match self {
46            // Time: base unit is nanoseconds
47            Self::Nanoseconds => 1.0,
48            Self::Microseconds => 1e3,
49            Self::Milliseconds => 1e6,
50            Self::Seconds => 1e9,
51
52            // Bytes: base unit is bytes
53            Self::Bytes => 1.0,
54            Self::Kilobytes => 1e3,
55            Self::Megabytes => 1e6,
56            Self::Gigabytes => 1e9,
57            Self::Terabytes => 1e12,
58            Self::Petabytes => 1e15,
59            Self::Kibibytes => 1024.0,
60            Self::Mebibytes => 1024.0_f64.powi(2),
61            Self::Gibibytes => 1024.0_f64.powi(3),
62            Self::Tebibytes => 1024.0_f64.powi(4),
63            Self::Pebibytes => 1024.0_f64.powi(5),
64        }
65    }
66
67    /// Returns the appropriate output format (time units display as durations, byte units as sizes)
68    pub fn default_format(&self) -> Format {
69        match self {
70            Self::Nanoseconds | Self::Microseconds | Self::Milliseconds | Self::Seconds => {
71                Format::Time
72            }
73            _ => Format::Bytes,
74        }
75    }
76}
77
78#[cfg(test)]
79mod tests {
80    use super::*;
81
82    #[test]
83    fn test_time_unit_scales() {
84        assert_eq!(Unit::Nanoseconds.scale(), 1.0);
85        assert_eq!(Unit::Microseconds.scale(), 1e3);
86        assert_eq!(Unit::Milliseconds.scale(), 1e6);
87        assert_eq!(Unit::Seconds.scale(), 1e9);
88    }
89
90    #[test]
91    fn test_decimal_byte_unit_scales() {
92        assert_eq!(Unit::Bytes.scale(), 1.0);
93        assert_eq!(Unit::Kilobytes.scale(), 1e3);
94        assert_eq!(Unit::Megabytes.scale(), 1e6);
95        assert_eq!(Unit::Gigabytes.scale(), 1e9);
96        assert_eq!(Unit::Terabytes.scale(), 1e12);
97        assert_eq!(Unit::Petabytes.scale(), 1e15);
98    }
99
100    #[test]
101    fn test_binary_byte_unit_scales() {
102        assert_eq!(Unit::Kibibytes.scale(), 1024.0);
103        assert_eq!(Unit::Mebibytes.scale(), 1024.0_f64.powi(2));
104        assert_eq!(Unit::Gibibytes.scale(), 1024.0_f64.powi(3));
105        assert_eq!(Unit::Tebibytes.scale(), 1024.0_f64.powi(4));
106        assert_eq!(Unit::Pebibytes.scale(), 1024.0_f64.powi(5));
107    }
108
109    #[test]
110    fn test_time_unit_default_format() {
111        assert!(matches!(Unit::Nanoseconds.default_format(), Format::Time));
112        assert!(matches!(Unit::Microseconds.default_format(), Format::Time));
113        assert!(matches!(Unit::Milliseconds.default_format(), Format::Time));
114        assert!(matches!(Unit::Seconds.default_format(), Format::Time));
115    }
116
117    #[test]
118    fn test_byte_unit_default_format() {
119        assert!(matches!(Unit::Bytes.default_format(), Format::Bytes));
120        assert!(matches!(Unit::Kilobytes.default_format(), Format::Bytes));
121        assert!(matches!(Unit::Megabytes.default_format(), Format::Bytes));
122        assert!(matches!(Unit::Kibibytes.default_format(), Format::Bytes));
123        assert!(matches!(Unit::Mebibytes.default_format(), Format::Bytes));
124    }
125
126    #[test]
127    fn test_conversion_examples() {
128        // 5 microseconds = 5000 nanoseconds
129        assert_eq!(5.0 * Unit::Microseconds.scale(), 5000.0);
130
131        // 2 megabytes = 2,000,000 bytes
132        assert_eq!(2.0 * Unit::Megabytes.scale(), 2_000_000.0);
133
134        // 3 mebibytes = 3,145,728 bytes
135        assert_eq!(3.0 * Unit::Mebibytes.scale(), 3_145_728.0);
136    }
137
138    #[test]
139    fn test_decimal_vs_binary_difference() {
140        // 1 MB (decimal) = 1,000,000 bytes
141        // 1 MiB (binary) = 1,048,576 bytes
142        let mb = Unit::Megabytes.scale();
143        let mib = Unit::Mebibytes.scale();
144
145        assert_eq!(mb, 1e6);
146        assert_eq!(mib, 1024.0 * 1024.0);
147        assert!(mib > mb);
148        assert!((mib - mb).abs() > 48_000.0); // At least 48KB difference
149    }
150}