1use serde::{Deserialize, Serialize};
2
3pub const DEFAULT_TCP_PORT: u16 = 7422;
5
6pub const DEFAULT_HTTP_PORT: u16 = 7423;
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
12#[serde(tag = "type", content = "data")]
13pub enum Command {
14 Status,
15 Whoami,
16 PairCreate {
17 label: Option<String>,
18 expire_secs: Option<u64>,
20 },
21 PairList,
22 PairRevoke {
23 id: String,
24 },
25 PairRevokeAll,
26 RepoRegister {
27 name: String,
28 path: String,
29 },
30 RepoUnregister {
31 name: String,
32 },
33 RepoList,
34 WorkstreamCreate {
35 repo_name: String,
36 name: String,
37 },
38 WorkstreamList {
39 repo_name: String,
40 },
41 WorkstreamDelete {
42 repo_name: String,
43 name: String,
44 },
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize)]
48pub struct WorkstreamInfo {
49 pub name: String,
50 pub repo_name: String,
51}
52
53#[derive(Debug, Clone, Serialize, Deserialize)]
54#[serde(tag = "type", content = "data")]
55pub enum Response {
56 Pong,
57 Ok,
58 DaemonStatus(DaemonStatus),
59 ClientInfo(ClientInfo),
60 Pair(PairPayload),
62 PairedClient(PairedClient),
63 PairedClients(Vec<PairedClient>),
64 Revoked(u32),
66 Repo(RepoInfo),
67 Repos(Vec<RepoInfo>),
68 Workstream(WorkstreamInfo),
69 Workstreams(Vec<WorkstreamInfo>),
70 Error(VexProtoError),
71}
72
73#[derive(Debug, Clone, Serialize, Deserialize)]
74pub struct DaemonStatus {
75 pub uptime_secs: u64,
76 pub connected_clients: u32,
77 pub version: String,
78}
79
80#[derive(Debug, Clone, Serialize, Deserialize)]
82pub struct PairPayload {
83 pub token_id: String,
84 pub token_secret: String,
85 pub host: Option<String>,
87}
88
89impl PairPayload {
90 pub fn pairing_string(&self) -> String {
92 format!("{}:{}", self.token_id, self.token_secret)
93 }
94}
95
96#[derive(Debug, Clone, Serialize, Deserialize)]
97pub struct PairedClient {
98 pub token_id: String,
99 pub label: Option<String>,
100 pub created_at: String,
101 pub expires_at: Option<String>,
102 pub last_seen: Option<String>,
103}
104
105#[derive(Debug, Clone, Serialize, Deserialize)]
106pub struct RepoInfo {
107 pub name: String,
108 pub path: String,
109}
110
111#[derive(Debug, Clone, Serialize, Deserialize)]
112pub struct ClientInfo {
113 pub token_id: Option<String>,
114 pub is_local: bool,
115}
116
117#[derive(Debug, Clone, Serialize, Deserialize)]
118#[serde(rename_all = "snake_case")]
119pub enum Transport {
120 Unix,
121 Tcp,
122}
123
124#[derive(Debug, Clone, Serialize, Deserialize)]
125#[serde(tag = "code", content = "message")]
126pub enum VexProtoError {
127 Unauthorized,
128 LocalOnly,
129 NotFound,
130 Internal(String),
131}
132
133#[derive(Debug, Clone, Serialize, Deserialize)]
135pub struct AuthToken {
136 pub token_id: String,
137 pub token_secret: String,
139}
140
141pub mod framing {
144 use serde::{Deserialize, Serialize};
145 use std::io;
146 use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
147
148 #[derive(Debug)]
149 pub enum VexFrameError {
150 Io(io::Error),
151 Json(serde_json::Error),
152 }
153
154 impl std::fmt::Display for VexFrameError {
155 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
156 match self {
157 VexFrameError::Io(e) => write!(f, "IO error: {e}"),
158 VexFrameError::Json(e) => write!(f, "JSON error: {e}"),
159 }
160 }
161 }
162
163 impl std::error::Error for VexFrameError {}
164
165 impl From<io::Error> for VexFrameError {
166 fn from(e: io::Error) -> Self {
167 VexFrameError::Io(e)
168 }
169 }
170
171 impl From<serde_json::Error> for VexFrameError {
172 fn from(e: serde_json::Error) -> Self {
173 VexFrameError::Json(e)
174 }
175 }
176
177 pub async fn send<W, T>(w: &mut W, msg: &T) -> Result<(), VexFrameError>
179 where
180 W: AsyncWrite + Unpin,
181 T: Serialize,
182 {
183 let body = serde_json::to_vec(msg)?;
184 w.write_u32(body.len() as u32).await?;
185 w.write_all(&body).await?;
186 Ok(())
187 }
188
189 pub async fn recv<R, T>(r: &mut R) -> Result<T, VexFrameError>
191 where
192 R: AsyncRead + Unpin,
193 T: for<'de> Deserialize<'de>,
194 {
195 let len = r.read_u32().await?;
196 let mut buf = vec![0u8; len as usize];
197 r.read_exact(&mut buf).await?;
198 Ok(serde_json::from_slice(&buf)?)
199 }
200}