Skip to main content

openvpn_mgmt_codec/
client_event.rs

1use std::str::FromStr;
2
3/// Error returned when a string is not a recognized client event.
4#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
5#[error("unrecognized client event: {0:?}")]
6pub struct ParseClientEventError(pub String);
7
8/// The sub-type of a `>CLIENT:` notification.
9#[derive(Debug, Clone, PartialEq, Eq, strum::Display)]
10#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
11pub enum ClientEvent {
12    /// A new client is connecting (`>CLIENT:CONNECT`).
13    Connect,
14
15    /// An existing client is re-authenticating (`>CLIENT:REAUTH`).
16    Reauth,
17
18    /// A client connection has been fully established (`>CLIENT:ESTABLISHED`).
19    Established,
20
21    /// A client has disconnected (`>CLIENT:DISCONNECT`).
22    Disconnect,
23
24    /// A client challenge-response (`>CLIENT:CR_RESPONSE,{CID},{KID},{base64}`).
25    ///
26    /// The base64-encoded response is carried inline because it appears as
27    /// the third comma-separated field on the header line (after CID and KID),
28    /// not in the ENV block. Both cedws/openvpn-mgmt-go and
29    /// jkroepke/openvpn-auth-oauth2 handle this as a distinct event type.
30    CrResponse(String),
31
32    /// An unrecognized event type (forward compatibility).
33    #[strum(default)]
34    Unknown(String),
35}
36
37impl FromStr for ClientEvent {
38    type Err = ParseClientEventError;
39
40    /// Parse a recognized client event string.
41    ///
42    /// Recognized values: `CONNECT`, `REAUTH`, `ESTABLISHED`, `DISCONNECT`.
43    /// Returns `Err` for anything else — use [`ClientEvent::Unknown`]
44    /// explicitly if forward-compatible fallback is desired.
45    ///
46    /// Note: `CR_RESPONSE` is handled separately in the codec because it
47    /// carries an inline base64 field; it is not recognized by `FromStr`.
48    fn from_str(s: &str) -> Result<Self, Self::Err> {
49        match s {
50            "CONNECT" => Ok(Self::Connect),
51            "REAUTH" => Ok(Self::Reauth),
52            "ESTABLISHED" => Ok(Self::Established),
53            "DISCONNECT" => Ok(Self::Disconnect),
54            other => Err(ParseClientEventError(other.to_string())),
55        }
56    }
57}