monitor_input/
input_source.rs

1use anyhow::Context;
2use std::str::FromStr;
3use strum_macros::{AsRefStr, EnumString, FromRepr};
4
5/// The raw representation of an input source value.
6/// See also [`InputSource`].
7pub type InputSourceRaw = u8;
8
9#[derive(Copy, Clone, Debug, PartialEq, AsRefStr, EnumString, FromRepr)]
10#[repr(u8)]
11#[strum(ascii_case_insensitive)]
12/// An input source value.
13/// See also [`InputSourceRaw`].
14pub enum InputSource {
15    #[strum(serialize = "DP1")]
16    DisplayPort1 = 0x0F,
17    #[strum(serialize = "DP2")]
18    DisplayPort2 = 0x10,
19    Hdmi1 = 0x11,
20    Hdmi2 = 0x12,
21    UsbC1 = 0x19,
22    UsbC2 = 0x1B,
23}
24
25impl InputSource {
26    /// Get [`InputSourceRaw`].
27    /// ```
28    /// # use monitor_input::InputSource;
29    /// assert_eq!(InputSource::Hdmi1.as_raw(), 17);
30    /// ```
31    pub fn as_raw(self) -> InputSourceRaw {
32        self as InputSourceRaw
33    }
34
35    /// Get [`InputSourceRaw`] from a string.
36    /// The string is either the name of an [`InputSource`] or a number.
37    /// # Examples
38    /// ```
39    /// # use monitor_input::InputSource;
40    /// // Input strings are either an [`InputSource`] or a number.
41    /// assert_eq!(
42    ///     InputSource::raw_from_str("Hdmi1").unwrap(),
43    ///     InputSource::Hdmi1.as_raw()
44    /// );
45    /// assert_eq!(InputSource::raw_from_str("27").unwrap(), 27);
46    ///
47    /// // Undefined string will be an error.
48    /// assert!(InputSource::raw_from_str("xyz").is_err());
49    /// // The error message should contain the original string.
50    /// assert!(
51    ///     InputSource::raw_from_str("xyz")
52    ///         .unwrap_err()
53    ///         .to_string()
54    ///         .contains("xyz")
55    /// );
56    /// ```
57    pub fn raw_from_str(input: &str) -> anyhow::Result<InputSourceRaw> {
58        if let Ok(value) = input.parse::<InputSourceRaw>() {
59            return Ok(value);
60        }
61        InputSource::from_str(input)
62            .map(|value| value.as_raw())
63            .with_context(|| format!("\"{input}\" is not a valid input source"))
64    }
65
66    /// Get a string from [`InputSourceRaw`].
67    /// # Examples
68    /// ```
69    /// # use monitor_input::InputSource;
70    /// assert_eq!(InputSource::str_from_raw(InputSource::Hdmi1.as_raw()), "Hdmi1");
71    /// assert_eq!(InputSource::str_from_raw(17), "Hdmi1");
72    /// assert_eq!(InputSource::str_from_raw(255), "255");
73    /// ```
74    pub fn str_from_raw(value: InputSourceRaw) -> String {
75        match InputSource::from_repr(value) {
76            Some(input_source) => input_source.as_ref().to_string(),
77            None => value.to_string(),
78        }
79    }
80}
81
82#[cfg(test)]
83mod tests {
84    use super::*;
85
86    #[test]
87    fn input_source_from_str() {
88        assert_eq!(InputSource::from_str("Hdmi1"), Ok(InputSource::Hdmi1));
89        // Test `ascii_case_insensitive`.
90        assert_eq!(InputSource::from_str("hdmi1"), Ok(InputSource::Hdmi1));
91        assert_eq!(InputSource::from_str("HDMI1"), Ok(InputSource::Hdmi1));
92        // Test `serialize`.
93        assert_eq!(InputSource::from_str("DP1"), Ok(InputSource::DisplayPort1));
94        assert_eq!(InputSource::from_str("dp2"), Ok(InputSource::DisplayPort2));
95        // Test failures.
96        assert!(InputSource::from_str("xyz").is_err());
97    }
98}