ev3robot/
percentage.rs

1use std::fmt::{self, Debug, Formatter};
2use std::str::FromStr;
3
4use anyhow::anyhow;
5use derive_more::*;
6
7macro_rules! percentage {
8    ($name:ident, $range:pat, $repr:ty) => {
9        #[derive(
10            Clone,
11            Copy,
12            PartialEq,
13            Eq,
14            PartialOrd,
15            Ord,
16            Display,
17            Binary,
18            Octal,
19            LowerHex,
20            UpperHex,
21            LowerExp,
22            UpperExp,
23        )]
24        pub struct $name($repr);
25
26        impl $name {
27            pub fn new(percent: $repr) -> anyhow::Result<Self> {
28                percent.try_into()
29            }
30
31            pub fn value(self) -> $repr {
32                self.into()
33            }
34
35            pub fn to_fraction(self) -> f32 {
36                self.value() as f32 / 100_f32
37            }
38        }
39
40        impl TryFrom<$repr> for $name {
41            type Error = anyhow::Error;
42
43            fn try_from(value: $repr) -> anyhow::Result<Self> {
44                if let $range = value {
45                    Ok($name(value))
46                } else {
47                    Err(anyhow!("invalid percentage"))
48                }
49            }
50        }
51
52        impl FromStr for $name {
53            type Err = anyhow::Error;
54
55            fn from_str(s: &str) -> anyhow::Result<Self> {
56                s.parse::<$repr>()?.try_into()
57            }
58        }
59
60        impl Debug for $name {
61            fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
62                write!(f, "{}%", self.value())
63            }
64        }
65
66        impl From<$name> for $repr {
67            fn from($name(percent): $name) -> Self {
68                percent
69            }
70        }
71    };
72}
73
74percentage!(Percentage, 0..=100, u8);
75percentage!(SignedPercentage, -100..=100, i8);