wdl_engine/
units.rs

1//! Module for unit representations.
2
3use std::str::FromStr;
4
5/// Represents a storage unit.
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
7pub enum StorageUnit {
8    /// The unit is in bytes.
9    #[default]
10    Bytes,
11    /// The unit is in kilobytes (10^3 bytes).
12    Kilobytes,
13    /// The unit is in megabytes (10^6 bytes).
14    Megabytes,
15    /// The unit is in gigabytes (10^9 bytes).
16    Gigabytes,
17    /// The unit is in terabytes (10^12 bytes).
18    Terabytes,
19    /// The unit is in kibibytes (2^10 bytes).
20    Kibibytes,
21    /// The unit is in mebibytes (2^20 bytes).
22    Mebibytes,
23    /// The unit is in gibibytes (2^30 bytes).
24    Gibibytes,
25    /// The unit is in tebibytes (2^40 bytes).
26    Tebibytes,
27}
28
29impl StorageUnit {
30    /// Converts the given number of bytes into a float representing the number
31    /// of units.
32    pub fn units(&self, bytes: u64) -> f64 {
33        let bytes = bytes as f64;
34        match self {
35            Self::Bytes => bytes,
36            Self::Kilobytes => bytes / 1000.0,
37            Self::Megabytes => bytes / 1000000.0,
38            Self::Gigabytes => bytes / 1000000000.0,
39            Self::Terabytes => bytes / 1000000000000.0,
40            Self::Kibibytes => bytes / 1024.0,
41            Self::Mebibytes => bytes / 1048576.0,
42            Self::Gibibytes => bytes / 1073741824.0,
43            Self::Tebibytes => bytes / 1099511627776.0,
44        }
45    }
46
47    /// Converts the given number of units into the corresponding number of
48    /// bytes.
49    pub fn bytes(&self, num: u64) -> Option<u64> {
50        match self {
51            Self::Bytes => Some(num),
52            Self::Kilobytes => num.checked_mul(1000),
53            Self::Megabytes => num.checked_mul(1000000),
54            Self::Gigabytes => num.checked_mul(1000000000),
55            Self::Terabytes => num.checked_mul(1000000000000),
56            Self::Kibibytes => num.checked_mul(1024),
57            Self::Mebibytes => num.checked_mul(1048576),
58            Self::Gibibytes => num.checked_mul(1073741824),
59            Self::Tebibytes => num.checked_mul(1099511627776),
60        }
61    }
62}
63
64impl FromStr for StorageUnit {
65    type Err = ();
66
67    fn from_str(s: &str) -> Result<Self, Self::Err> {
68        match s {
69            "B" => Ok(Self::Bytes),
70            "KB" | "K" => Ok(Self::Kilobytes),
71            "MB" | "M" => Ok(Self::Megabytes),
72            "GB" | "G" => Ok(Self::Gigabytes),
73            "TB" | "T" => Ok(Self::Terabytes),
74            "KiB" | "Ki" => Ok(Self::Kibibytes),
75            "MiB" | "Mi" => Ok(Self::Mebibytes),
76            "GiB" | "Gi" => Ok(Self::Gibibytes),
77            "TiB" | "Ti" => Ok(Self::Tebibytes),
78            _ => Err(()),
79        }
80    }
81}
82
83/// Converts a unit string (e.g. `2 GiB`) to bytes.
84///
85/// The string is expected to contain a single integer followed by the unit.
86///
87/// Returns `None` if the string is not a valid unit string or if the resulting
88/// byte count exceeds an unsigned 64-bit integer.
89pub fn convert_unit_string(s: &str) -> Option<u64> {
90    // No space, so try splitting on first alpha
91    let (n, unit) = match s.chars().position(|c| c.is_ascii_alphabetic()) {
92        Some(index) => {
93            let (n, unit) = s.split_at(index);
94            (
95                n.trim().parse::<u64>().ok()?,
96                unit.trim().parse::<StorageUnit>().ok()?,
97            )
98        }
99        None => return None,
100    };
101
102    unit.bytes(n)
103}