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
use std::convert::TryInto;
use std::fmt;

use crate::errors::*;

#[derive(Debug, PartialEq, Clone, Copy)]
pub enum Unit {
    Bytes,
    Bits,
    Percents,
    Degrees,
    Seconds,
    Watts,
    Hertz,
    None,
}

impl fmt::Display for Unit {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "{}",
            match self {
                Self::Bytes => "B",
                Self::Bits => "b",
                Self::Percents => "%",
                Self::Degrees => "°",
                Self::Seconds => "s",
                Self::Watts => "W",
                Self::Hertz => "Hz",
                Self::None => "",
            }
        )
    }
}

impl TryInto<Unit> for &str {
    type Error = crate::errors::Error;

    fn try_into(self) -> Result<Unit> {
        match self {
            "B" => Ok(Unit::Bytes),
            "b" => Ok(Unit::Bits),
            "%" => Ok(Unit::Percents),
            "deg" => Ok(Unit::Degrees),
            "s" => Ok(Unit::Seconds),
            "W" => Ok(Unit::Watts),
            "Hz" => Ok(Unit::Hertz),
            "" => Ok(Unit::None),
            x => Err(ConfigurationError(
                "Can not parse unit".to_string(),
                format!("unknown unit: '{}'", x.to_string()),
            )),
        }
    }
}

impl Unit {
    //TODO support more complex conversions like Celsius -> Fahrenheit
    pub fn convert(&self, into: Self) -> Result<f64> {
        match self {
            Self::Bits if into == Self::Bytes => Ok(1. / 8.),
            Self::Bytes if into == Self::Bits => Ok(8.),
            x if into == *x || into == Self::None => Ok(1.),
            x => Err(ConfigurationError(
                "Can not convert unit".to_string(),
                format!("it is not possible to convert '{:?}' to '{:?}'", x, into),
            )),
        }
    }

    pub fn is_byte(&self) -> bool {
        matches!(self, Self::Bytes | Self::Bits)
    }
}