1use rkyv::{Archive, Deserialize, Serialize};
7use serde::{Deserialize as SerdeDeserialize, Serialize as SerdeSerialize};
8
9use crate::IPC_SCHEMA_VERSION;
10
11#[derive(Archive, Serialize, Deserialize, Debug, Clone)]
13#[rkyv(derive(Debug))]
14pub struct IpcRequest {
15 pub ipc_schema_version: u32,
17 pub request_id: String,
19 pub repo_root: String,
21 pub actor_id: String,
23 pub data_dir: String,
25 pub command: IpcCommand,
27}
28
29impl IpcRequest {
30 pub fn new(
32 request_id: String,
33 repo_root: String,
34 actor_id: String,
35 data_dir: String,
36 command: IpcCommand,
37 ) -> Self {
38 Self {
39 ipc_schema_version: IPC_SCHEMA_VERSION,
40 request_id,
41 repo_root,
42 actor_id,
43 data_dir,
44 command,
45 }
46 }
47}
48
49#[derive(Archive, Serialize, Deserialize, Debug, Clone)]
51#[rkyv(derive(Debug))]
52pub struct IpcResponse {
53 pub ipc_schema_version: u32,
55 pub request_id: String,
57 pub ok: bool,
59 pub data: Option<String>,
61 pub error: Option<IpcErrorPayload>,
63}
64
65impl IpcResponse {
66 pub fn success(request_id: String, data: Option<String>) -> Self {
68 Self {
69 ipc_schema_version: IPC_SCHEMA_VERSION,
70 request_id,
71 ok: true,
72 data,
73 error: None,
74 }
75 }
76
77 pub fn error(request_id: String, code: String, message: String) -> Self {
79 Self {
80 ipc_schema_version: IPC_SCHEMA_VERSION,
81 request_id,
82 ok: false,
83 data: None,
84 error: Some(IpcErrorPayload {
85 code,
86 message,
87 details: None,
88 }),
89 }
90 }
91}
92
93#[derive(Archive, Serialize, Deserialize, Debug, Clone, SerdeSerialize, SerdeDeserialize)]
95#[rkyv(derive(Debug))]
96pub struct IpcErrorPayload {
97 pub code: String,
99 pub message: String,
101 pub details: Option<String>,
103}
104
105#[derive(Archive, Serialize, Deserialize, Debug, Clone)]
109#[rkyv(derive(Debug))]
110pub enum IpcCommand {
111 IssueCreate {
113 title: String,
114 body: String,
115 labels: Vec<String>,
116 },
117 IssueList {
118 state: Option<String>,
119 label: Option<String>,
120 },
121 IssueShow {
122 issue_id: String,
123 },
124 IssueUpdate {
125 issue_id: String,
126 title: Option<String>,
127 body: Option<String>,
128 },
129 IssueComment {
130 issue_id: String,
131 body: String,
132 },
133 IssueLabel {
134 issue_id: String,
135 add: Vec<String>,
136 remove: Vec<String>,
137 },
138 IssueAssign {
139 issue_id: String,
140 add: Vec<String>,
141 remove: Vec<String>,
142 },
143 IssueClose {
144 issue_id: String,
145 },
146 IssueReopen {
147 issue_id: String,
148 },
149 IssueLink {
150 issue_id: String,
151 url: String,
152 note: Option<String>,
153 },
154 IssueAttach {
155 issue_id: String,
156 file_path: String,
157 },
158
159 DbStats,
161
162 Export {
164 format: String,
165 since: Option<String>,
166 },
167
168 Rebuild,
170
171 Sync {
173 remote: String,
174 pull: bool,
175 push: bool,
176 },
177
178 SnapshotCreate,
180 SnapshotList,
181 SnapshotGc {
182 keep: u32,
183 },
184
185 DaemonStatus,
187 DaemonStop,
188}
189
190#[cfg(test)]
191mod tests {
192 use super::*;
193
194 #[test]
195 fn test_request_creation() {
196 let req = IpcRequest::new(
197 "test-123".to_string(),
198 "/path/to/repo".to_string(),
199 "abcd1234".to_string(),
200 ".git/grite/actors/abcd1234".to_string(),
201 IpcCommand::IssueList {
202 state: Some("open".to_string()),
203 label: None,
204 },
205 );
206
207 assert_eq!(req.ipc_schema_version, IPC_SCHEMA_VERSION);
208 assert_eq!(req.request_id, "test-123");
209 }
210
211 #[test]
212 fn test_response_success() {
213 let resp = IpcResponse::success(
214 "test-123".to_string(),
215 Some(r#"{"issues": []}"#.to_string()),
216 );
217
218 assert!(resp.ok);
219 assert!(resp.error.is_none());
220 assert!(resp.data.is_some());
221 }
222
223 #[test]
224 fn test_response_error() {
225 let resp = IpcResponse::error(
226 "test-123".to_string(),
227 "not_found".to_string(),
228 "Issue not found".to_string(),
229 );
230
231 assert!(!resp.ok);
232 assert!(resp.data.is_none());
233 assert!(resp.error.is_some());
234 assert_eq!(resp.error.as_ref().unwrap().code, "not_found");
235 }
236
237 #[test]
238 fn test_rkyv_roundtrip() {
239 let req = IpcRequest::new(
240 "test-456".to_string(),
241 "/repo".to_string(),
242 "actor123".to_string(),
243 ".git/grite/actors/actor123".to_string(),
244 IpcCommand::IssueCreate {
245 title: "Test Issue".to_string(),
246 body: "Description".to_string(),
247 labels: vec!["bug".to_string()],
248 },
249 );
250
251 let bytes = rkyv::to_bytes::<rkyv::rancor::Error>(&req).unwrap();
253
254 let archived = rkyv::access::<ArchivedIpcRequest, rkyv::rancor::Error>(&bytes).unwrap();
256 assert_eq!(archived.request_id, "test-456");
257 assert_eq!(archived.ipc_schema_version, IPC_SCHEMA_VERSION);
258 }
259}