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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
use errors::ValidationError;

use std::default;
use std::fmt;

bitflags! {
    /// Display dimming.
    ///
    /// The whole display is dimmed via PWM @ N/16 duty cycle; individual LEDs cannot be dimmed independently.
    ///
    /// The value should be in the inclusive range [`BRIGHTNESS_MIN`] to [`BRIGHTNESS_MAX`]. Use the [`from_u8`]
    /// helper to create a validated `Dimming` value.
    ///
    /// [`BRIGHTNESS_MIN`]: struct.Dimming.html#associatedconstant.BRIGHTNESS_MIN
    /// [`BRIGHTNESS_MAX`]: struct.Dimming.html#associatedconstant.BRIGHTNESS_MAX
    /// [`from_u8`]: struct.Dimming.html#method.from_u8
    pub struct Dimming: u8 {
        /// Command to set the digital dimming.
        const COMMAND = 0b1110_0000;
        /// Minimum brightness @ 1/16 PWM duty cycle. (Same as `BRIGHTNESS_1_16`)
        const BRIGHTNESS_MIN = Self::BRIGHTNESS_1_16.bits;
        /// Brightness @ 1/16 PWM duty cycle.
        const BRIGHTNESS_1_16 = 0;
        /// Brightness @ 2/16 PWM duty cycle.
        const BRIGHTNESS_2_16 = 1;
        /// Brightness @ 3/16 PWM duty cycle.
        const BRIGHTNESS_3_16 = 2;
        /// Brightness @ 4/16 PWM duty cycle.
        const BRIGHTNESS_4_16 = 3;
        /// Brightness @ 5/16 PWM duty cycle.
        const BRIGHTNESS_5_16 = 4;
        /// Brightness @ 6/16 PWM duty cycle.
        const BRIGHTNESS_6_16 = 5;
        /// Brightness @ 7/16 PWM duty cycle.
        const BRIGHTNESS_7_16 = 6;
        /// Brightness @ 8/16 PWM duty cycle.
        const BRIGHTNESS_8_16 = 7;
        /// Brightness @ 9/16 PWM duty cycle.
        const BRIGHTNESS_9_16 = 8;
        /// Brightness @ 10/16 PWM duty cycle.
        const BRIGHTNESS_10_16 = 9;
        /// Brightness @ 11/16 PWM duty cycle.
        const BRIGHTNESS_11_16 = 10;
        /// Brightness @ 12/16 PWM duty cycle.
        const BRIGHTNESS_12_16 = 11;
        /// Brightness @ 13/16 PWM duty cycle.
        const BRIGHTNESS_13_16 = 12;
        /// Brightness @ 14/16 PWM duty cycle.
        const BRIGHTNESS_14_16 = 13;
        /// Brightness @ 15/16 PWM duty cycle.
        const BRIGHTNESS_15_16 = 14;
        /// Brightness @ 16/16 PWM duty cycle.
        const BRIGHTNESS_16_16 = 15;
        /// Maximum brightness @ 16/16 PWM duty cycle. (Same as `BRIGHTNESS_16_16`)
        ///
        /// *This is the Power-on Reset default.*
        const BRIGHTNESS_MAX = Self::BRIGHTNESS_16_16.bits;
    }
}

impl default::Default for Dimming {
    fn default() -> Dimming {
        Dimming::BRIGHTNESS_MAX
    }
}

impl fmt::Display for Dimming {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Dimming::COMMAND => write!(f, "Dimming::COMMAND"),
            Dimming::BRIGHTNESS_MIN => write!(f, "Dimming::BRIGHTNESS_MIN"),
            Dimming::BRIGHTNESS_MAX => write!(f, "Dimming::BRIGHTNESS_MAX"),
            _ => write!(f, "Dimming::{:#10b}", self.bits()),
        }
    }
}

impl Dimming {
    /// Return a validated `Dimming` value from the given `u8`.
    ///
    /// *NOTE: The brightness values are 0-indexed, e.g. `0u8` is equivalent to `1/16`, and `15u8` is `16/16`.*
    ///
    /// # Errors
    ///
    /// The value is validated to be in the inclusive range [`BRIGHTNESS_MIN`] to [`BRIGHTNESS_MAX`]. If
    /// the given `u8` value is too large then [`ht16k33::ValidationError::ValueTooLarge`] is returned.
    ///
    /// # Example
    ///
    /// ```
    /// # extern crate failure;
    /// # extern crate ht16k33;
    /// # use failure::Error;
    /// use ht16k33::Dimming;
    /// # fn main() -> Result<(), Error> {
    ///
    /// let brightness = Dimming::from_u8(1u8)?;
    ///
    /// assert_eq!(1u8, brightness.bits());
    ///
    /// # Ok(())
    /// # }
    /// ```
    ///
    /// # Error Example
    ///
    /// ```should_panic
    /// # extern crate ht16k33;
    /// use ht16k33::Dimming;
    /// use ht16k33::ValidationError;
    /// # fn main() {
    ///
    /// // Greater than the `BRIGHTNESS_MAX` value of `15u8`.
    /// let value = 16u8;
    ///
    /// let brightness = match Dimming::from_u8(value) {
    ///     Ok(brightness) => brightness,
    ///     Err(ValidationError) => panic!(),
    /// };
    ///
    /// # }
    /// ```
    ///
    /// [`BRIGHTNESS_MIN`]: struct.Dimming.html#associatedconstant.BRIGHTNESS_MIN
    /// [`BRIGHTNESS_MAX`]: struct.Dimming.html#associatedconstant.BRIGHTNESS_MAX
    /// [`ht16k33::ValidationError::ValueTooLarge`]: enum.ValidationError.html#variant.ValueTooLarge
    // TODO Implement as TryFrom<u8> once it's available in `stable`.
    pub fn from_u8(value: u8) -> Result<Self, ValidationError> {
        if value > Dimming::BRIGHTNESS_MAX.bits() {
            return Err(ValidationError::ValueTooLarge {
                name: "Dimming".to_string(),
                value,
                limit: Dimming::BRIGHTNESS_MAX.bits(),
                inclusive: true,
            });
        }

        Ok(Dimming::from_bits_truncate(value))
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn brightness_min() {
        assert_eq!(
            Dimming::BRIGHTNESS_1_16,
            Dimming::BRIGHTNESS_MIN,
            "Dimming MIN brightness matches 1/16 value"
        );
    }

    #[test]
    fn brightness_max() {
        assert_eq!(
            Dimming::BRIGHTNESS_16_16,
            Dimming::BRIGHTNESS_MAX,
            "Dimming MAX brightness matches 16/16 value"
        );
    }

    #[test]
    fn default() {
        assert_eq!(
            Dimming::BRIGHTNESS_MAX,
            Dimming::default(),
            "Dimming default is MAX brightness"
        );
    }

    #[test]
    fn from_u8() {
        for value in 0u8..16 {
            let dimming = Dimming::from_u8(value).unwrap();
            assert_eq!(value, dimming.bits());
        }
    }

    #[test]
    #[should_panic]
    fn from_u8_too_large() {
        let _ = Dimming::from_u8(16u8).unwrap();
    }
}