1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
use super::*;

pub trait QuantityExt {
    fn to_memory(&self) -> Result<i64, QuantityParseError>;
    fn to_f64(&self) -> Result<f64, QuantityParseError>;
}

impl QuantityExt for resource::Quantity {
    fn to_memory(&self) -> Result<i64, QuantityParseError> {
        if let Some((number, _unit)) = self.0.split_once("Ki") {
            number.parse::<i64>().map(|n| n * 1024)
        } else if let Some((number, _unit)) = self.0.split_once("Mi") {
            number.parse::<i64>().map(|n| n * 1024 * 1024)
        } else if let Some((number, _unit)) = self.0.split_once("Gi") {
            number.parse::<i64>().map(|n| n * 1024 * 1024 * 1024)
        } else {
            self.0.parse::<i64>()
        }
        .map_err(|_e| QuantityParseError::new(&self.0))
    }

    fn to_f64(&self) -> Result<f64, QuantityParseError> {
        let out = if let Some((number, _unit)) = self.0.split_once('n') {
            number.parse::<f64>().map(|f| f / 1_000_000_000_f64)
        } else if let Some((number, _unit)) = self.0.split_once('m') {
            number.parse::<f64>().map(|f| f / 1_000_f64)
        } else {
            self.0.parse::<f64>()
        };

        out.map_err(|_e| QuantityParseError::new(&self.0))
    }
}

#[derive(Debug, thiserror::Error)]
#[error("Unexpected format: {0}")]
pub struct QuantityParseError(String);

impl QuantityParseError {
    fn new(text: &str) -> Self {
        Self(text.to_string())
    }
}

#[cfg(test)]
mod tests {
    #![allow(non_snake_case)]

    use super::*;

    #[test]
    fn memory_Ki() {
        let q = quantity("2Ki").to_memory().unwrap();
        assert_eq!(q, 2048);
    }

    #[test]
    fn memory_Mi() {
        let q = quantity("5Mi").to_memory().unwrap();
        assert_eq!(q, 5242880);
    }

    #[test]
    fn memory_Gi() {
        let q = quantity("3Gi").to_memory().unwrap();
        assert_eq!(q, 3221225472);
    }

    #[test]
    fn leading_zeros() {
        let q = quantity("0004Ki").to_memory().unwrap();
        assert_eq!(q, 4096);
    }

    #[test]
    fn cpu_nano() {
        let q = quantity("257n").to_f64().unwrap();
        assert_eq!(q, 0.000000257);
    }

    #[test]
    fn cpu_milli() {
        let q = quantity("3491m").to_f64().unwrap();
        assert_eq!(q, 3.491);
    }

    fn quantity(v: &str) -> resource::Quantity {
        resource::Quantity(v.to_string())
    }
}