Skip to main content

openvpn_mgmt_codec/
stream_mode.rs

1use std::fmt;
2use std::str::FromStr;
3
4/// Error returned when a string is not a recognized stream mode.
5#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
6#[error("unrecognized stream mode: {0:?}")]
7pub struct ParseStreamModeError(pub String);
8
9/// Mode selector for commands that share the on/off/all/on-all/N grammar.
10/// This is used by `log`, `state`, and `echo`, all of which support
11/// identical sub-commands.
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum StreamMode {
14    /// Enable real-time notifications.
15    On,
16
17    /// Disable real-time notifications.
18    Off,
19
20    /// Dump the entire history buffer.
21    All,
22
23    /// Atomically enable real-time notifications AND dump history.
24    /// This guarantees no messages are missed between the dump and
25    /// the start of real-time streaming.
26    OnAll,
27
28    /// Show the N most recent history entries.
29    Recent(u32),
30}
31
32impl StreamMode {
33    /// Whether this mode produces a multi-line history dump rather than a
34    /// simple `SUCCESS:` acknowledgement.
35    pub fn returns_history(self) -> bool {
36        matches!(self, Self::All | Self::OnAll | Self::Recent(_))
37    }
38}
39
40impl fmt::Display for StreamMode {
41    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42        match self {
43            Self::On => f.write_str("on"),
44            Self::Off => f.write_str("off"),
45            Self::All => f.write_str("all"),
46            Self::OnAll => f.write_str("on all"),
47            Self::Recent(n) => write!(f, "{n}"),
48        }
49    }
50}
51
52impl FromStr for StreamMode {
53    type Err = ParseStreamModeError;
54
55    /// Parse a stream mode string: `on`, `off`, `all`, `on all`, or a number.
56    fn from_str(input: &str) -> Result<Self, Self::Err> {
57        match input {
58            "on" => Ok(Self::On),
59            "off" => Ok(Self::Off),
60            "all" => Ok(Self::All),
61            "on all" => Ok(Self::OnAll),
62            other => other
63                .parse::<u32>()
64                .map(Self::Recent)
65                .map_err(|_| ParseStreamModeError(input.to_string())),
66        }
67    }
68}
69
70#[cfg(test)]
71mod tests {
72    use super::*;
73    use test_case::test_case;
74
75    #[test_case(StreamMode::On)]
76    #[test_case(StreamMode::Off)]
77    #[test_case(StreamMode::All)]
78    #[test_case(StreamMode::OnAll)]
79    #[test_case(StreamMode::Recent(42))]
80    fn parse_roundtrip(mode: StreamMode) {
81        let string = mode.to_string();
82        assert_eq!(string.parse::<StreamMode>().unwrap(), mode);
83    }
84
85    #[test]
86    fn parse_invalid() {
87        assert!("bogus".parse::<StreamMode>().is_err());
88    }
89}