openvpn_mgmt_frame/frame.rs
1use std::collections::BTreeMap;
2
3/// A classified line (or accumulated block) from the OpenVPN management
4/// interface.
5///
6/// The frame decoder emits one `Frame` per logical unit. Most variants
7/// map 1:1 to a wire line; [`ClientEnv`](Frame::ClientEnv) is the
8/// exception — it accumulates the full `>CLIENT:` header + ENV block
9/// before being emitted.
10///
11/// ```
12/// use bytes::BytesMut;
13/// use tokio_util::codec::Decoder;
14/// use openvpn_mgmt_frame::{Frame, FrameDecoder};
15///
16/// let mut decoder = FrameDecoder::new();
17/// let mut buf = BytesMut::from(
18/// "SUCCESS: pid=42\nERROR: unknown command\nEND\n"
19/// );
20///
21/// assert!(matches!(decoder.decode(&mut buf).unwrap(), Some(Frame::Success(_))));
22/// assert!(matches!(decoder.decode(&mut buf).unwrap(), Some(Frame::Error(_))));
23/// assert!(matches!(decoder.decode(&mut buf).unwrap(), Some(Frame::End)));
24/// ```
25#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
26#[derive(Debug, Clone, PartialEq, Eq)]
27pub enum Frame {
28 /// `SUCCESS: [text]` — a command completed successfully.
29 Success(String),
30
31 /// `ERROR: [text]` — a command failed.
32 Error(String),
33
34 /// A `>` notification line (single-line).
35 ///
36 /// `kind` is the tag before the first `:` (e.g. `"STATE"`, `"LOG"`).
37 /// `payload` is everything after the `:`.
38 ///
39 /// `>CLIENT:` lines with ENV blocks are **not** emitted as
40 /// `Notification` — they become [`ClientEnv`](Frame::ClientEnv)
41 /// instead.
42 Notification {
43 /// Notification type tag (e.g. `"STATE"`, `"LOG"`, `"BYTECOUNT"`).
44 kind: String,
45 /// Everything after `>KIND:`.
46 payload: String,
47 },
48
49 /// A fully accumulated `>CLIENT:` notification with its ENV block.
50 ///
51 /// All `>CLIENT:ENV,key=value` lines have been collected; the
52 /// terminating `>CLIENT:ENV,END` has been consumed.
53 ClientEnv {
54 /// Raw event string (e.g. `"CONNECT"`, `"REAUTH"`, `"ADDRESS"`).
55 event: String,
56 /// Everything after the event on the header line (CID, KID, etc.),
57 /// as a raw comma-separated string.
58 args: String,
59 /// Accumulated ENV key-value pairs.
60 env: BTreeMap<String, String>,
61 },
62
63 /// `ENTER PASSWORD:` prompt.
64 PasswordPrompt,
65
66 /// A bare `END` line — terminates a multi-line response block.
67 End,
68
69 /// The first `>INFO:` line (the connection banner).
70 ///
71 /// Subsequent `>INFO:` lines are emitted as
72 /// [`Notification`](Frame::Notification) with `kind = "INFO"`.
73 Info(String),
74
75 /// Any line that is not self-describing (no `SUCCESS:`/`ERROR:`/`>`
76 /// prefix, not `END`, not `ENTER PASSWORD:`).
77 ///
78 /// Higher layers use command-tracking state to decide whether this
79 /// belongs to a multi-line response or is an unrecognized line.
80 Line(String),
81}