Skip to main content

vex_cli/proto/
mod.rs

1use serde::{Deserialize, Serialize};
2
3/// Default port vexd listens on for TLS TCP connections.
4pub const DEFAULT_TCP_PORT: u16 = 7422;
5
6// ── Wire types ────────────────────────────────────────────────────────────────
7
8#[derive(Debug, Clone, Serialize, Deserialize)]
9#[serde(tag = "type", content = "data")]
10pub enum Command {
11    Status,
12    Whoami,
13    PairCreate {
14        label: Option<String>,
15        /// Expiry in seconds from now
16        expire_secs: Option<u64>,
17    },
18    PairList,
19    PairRevoke {
20        id: String,
21    },
22    PairRevokeAll,
23}
24
25#[derive(Debug, Clone, Serialize, Deserialize)]
26#[serde(tag = "type", content = "data")]
27pub enum Response {
28    Pong,
29    Ok,
30    DaemonStatus(DaemonStatus),
31    ClientInfo(ClientInfo),
32    /// Returned after PairCreate; contains the plaintext secret (one-time)
33    Pair(PairPayload),
34    PairedClient(PairedClient),
35    PairedClients(Vec<PairedClient>),
36    /// Returned by PairRevoke / PairRevokeAll, carrying the revoked count.
37    Revoked(u32),
38    Error(VexProtoError),
39}
40
41#[derive(Debug, Clone, Serialize, Deserialize)]
42pub struct DaemonStatus {
43    pub uptime_secs: u64,
44    pub connected_clients: u32,
45    pub version: String,
46}
47
48/// Returned by PairCreate — contains the plaintext secret for the new token.
49#[derive(Debug, Clone, Serialize, Deserialize)]
50pub struct PairPayload {
51    pub token_id: String,
52    pub token_secret: String,
53    /// Optional TCP host for encoding into a QR pairing string
54    pub host: Option<String>,
55}
56
57impl PairPayload {
58    /// Returns the pairing string in `<token_id>:<token_secret>` format.
59    pub fn pairing_string(&self) -> String {
60        format!("{}:{}", self.token_id, self.token_secret)
61    }
62}
63
64#[derive(Debug, Clone, Serialize, Deserialize)]
65pub struct PairedClient {
66    pub token_id: String,
67    pub label: Option<String>,
68    pub created_at: String,
69    pub expires_at: Option<String>,
70    pub last_seen: Option<String>,
71}
72
73#[derive(Debug, Clone, Serialize, Deserialize)]
74pub struct ClientInfo {
75    pub token_id: Option<String>,
76    pub is_local: bool,
77}
78
79#[derive(Debug, Clone, Serialize, Deserialize)]
80#[serde(rename_all = "snake_case")]
81pub enum Transport {
82    Unix,
83    Tcp,
84}
85
86#[derive(Debug, Clone, Serialize, Deserialize)]
87#[serde(tag = "code", content = "message")]
88pub enum VexProtoError {
89    Unauthorized,
90    LocalOnly,
91    NotFound,
92    Internal(String),
93}
94
95/// Sent by the client at the start of every TCP connection before any Command.
96#[derive(Debug, Clone, Serialize, Deserialize)]
97pub struct AuthToken {
98    pub token_id: String,
99    /// Plaintext hex-encoded 32-byte secret
100    pub token_secret: String,
101}
102
103// ── Framing ───────────────────────────────────────────────────────────────────
104
105pub mod framing {
106    use serde::{Deserialize, Serialize};
107    use std::io;
108    use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
109
110    #[derive(Debug)]
111    pub enum VexFrameError {
112        Io(io::Error),
113        Json(serde_json::Error),
114    }
115
116    impl std::fmt::Display for VexFrameError {
117        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
118            match self {
119                VexFrameError::Io(e) => write!(f, "IO error: {e}"),
120                VexFrameError::Json(e) => write!(f, "JSON error: {e}"),
121            }
122        }
123    }
124
125    impl std::error::Error for VexFrameError {}
126
127    impl From<io::Error> for VexFrameError {
128        fn from(e: io::Error) -> Self {
129            VexFrameError::Io(e)
130        }
131    }
132
133    impl From<serde_json::Error> for VexFrameError {
134        fn from(e: serde_json::Error) -> Self {
135            VexFrameError::Json(e)
136        }
137    }
138
139    /// Write a length-prefixed JSON frame to `w`.
140    pub async fn send<W, T>(w: &mut W, msg: &T) -> Result<(), VexFrameError>
141    where
142        W: AsyncWrite + Unpin,
143        T: Serialize,
144    {
145        let body = serde_json::to_vec(msg)?;
146        w.write_u32(body.len() as u32).await?;
147        w.write_all(&body).await?;
148        Ok(())
149    }
150
151    /// Read a length-prefixed JSON frame from `r`.
152    pub async fn recv<R, T>(r: &mut R) -> Result<T, VexFrameError>
153    where
154        R: AsyncRead + Unpin,
155        T: for<'de> Deserialize<'de>,
156    {
157        let len = r.read_u32().await?;
158        let mut buf = vec![0u8; len as usize];
159        r.read_exact(&mut buf).await?;
160        Ok(serde_json::from_slice(&buf)?)
161    }
162}