ghidra_cli/ipc/
protocol.rs1#![allow(dead_code)]
7
8use serde::{Deserialize, Serialize};
9
10#[derive(Debug, Serialize, Deserialize)]
12pub struct Request {
13 pub id: u64,
15 pub command: Command,
17}
18
19impl Request {
20 pub fn new(id: u64, command: Command) -> Self {
22 Self { id, command }
23 }
24}
25
26#[derive(Debug, Serialize, Deserialize)]
28pub struct Response {
29 pub id: u64,
31 pub success: bool,
33 #[serde(skip_serializing_if = "Option::is_none")]
35 pub result: Option<serde_json::Value>,
36 #[serde(skip_serializing_if = "Option::is_none")]
38 pub error: Option<String>,
39}
40
41impl Response {
42 pub fn success(id: u64, result: serde_json::Value) -> Self {
44 Self {
45 id,
46 success: true,
47 result: Some(result),
48 error: None,
49 }
50 }
51
52 pub fn error(id: u64, message: impl Into<String>) -> Self {
54 Self {
55 id,
56 success: false,
57 result: None,
58 error: Some(message.into()),
59 }
60 }
61
62 pub fn ok(id: u64) -> Self {
64 Self {
65 id,
66 success: true,
67 result: Some(serde_json::json!({})),
68 error: None,
69 }
70 }
71}
72
73#[derive(Debug, Clone, Serialize, Deserialize)]
77#[serde(tag = "type", rename_all = "snake_case")]
78pub enum Command {
79 ListFunctions {
82 #[serde(skip_serializing_if = "Option::is_none")]
83 limit: Option<usize>,
84 #[serde(skip_serializing_if = "Option::is_none")]
85 filter: Option<String>,
86 },
87
88 Decompile { address: String },
90
91 ListStrings {
93 #[serde(skip_serializing_if = "Option::is_none")]
94 limit: Option<usize>,
95 },
96
97 ListImports,
99
100 ListExports,
102
103 MemoryMap,
105
106 ProgramInfo,
108
109 XRefsTo { address: String },
111
112 XRefsFrom { address: String },
114
115 Import {
118 binary_path: String,
119 project: String,
120 #[serde(skip_serializing_if = "Option::is_none")]
121 program: Option<String>,
122 },
123
124 Analyze { project: String, program: String },
126
127 Ping,
130
131 Status,
133
134 ClearCache,
136
137 Shutdown,
139
140 ExecuteCli {
143 command_json: String,
145 },
146}
147
148#[cfg(test)]
149mod tests {
150 use super::*;
151
152 #[test]
153 fn test_request_serialization() {
154 let request = Request::new(1, Command::Ping);
155 let json = serde_json::to_string(&request).unwrap();
156 let deserialized: Request = serde_json::from_str(&json).unwrap();
157 assert_eq!(deserialized.id, 1);
158 assert!(matches!(deserialized.command, Command::Ping));
159 }
160
161 #[test]
162 fn test_response_success() {
163 let response = Response::success(1, serde_json::json!({"count": 42}));
164 assert!(response.success);
165 assert_eq!(response.id, 1);
166 assert!(response.result.is_some());
167 }
168
169 #[test]
170 fn test_response_error() {
171 let response = Response::error(1, "Something went wrong");
172 assert!(!response.success);
173 assert_eq!(response.error.as_ref().unwrap(), "Something went wrong");
174 }
175
176 #[test]
177 fn test_command_serialization() {
178 let cmd = Command::ListFunctions {
179 limit: Some(100),
180 filter: Some("main".to_string()),
181 };
182 let json = serde_json::to_string(&cmd).unwrap();
183 assert!(json.contains("list_functions"));
184 assert!(json.contains("100"));
185 assert!(json.contains("main"));
186 }
187}