1use serde::{Deserialize, Serialize};
2use uuid::Uuid;
3use chrono::{DateTime, Utc};
4
5use crate::types::{Memory, MemoryType, Project};
6use super::error::JsonRpcError;
7
8#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct JsonRpcResponse {
10 pub jsonrpc: String,
11 #[serde(skip_serializing_if = "Option::is_none")]
12 pub result: Option<ResponseResult>,
13 #[serde(skip_serializing_if = "Option::is_none")]
14 pub error: Option<JsonRpcError>,
15 pub id: u64,
16}
17
18impl JsonRpcResponse {
19 pub fn success(result: ResponseResult, id: u64) -> Self {
20 Self {
21 jsonrpc: "2.0".to_string(),
22 result: Some(result),
23 error: None,
24 id,
25 }
26 }
27
28 pub fn error(err: JsonRpcError, id: u64) -> Self {
29 Self {
30 jsonrpc: "2.0".to_string(),
31 result: None,
32 error: Some(err),
33 id,
34 }
35 }
36}
37
38#[derive(Debug, Clone, Serialize, Deserialize)]
39#[serde(tag = "type", rename_all = "snake_case")]
40pub enum ResponseResult {
41 Memory(MemoryResult),
42 MemoryList(MemoryListResult),
43 SearchResult(SearchResult),
44 SemanticSearchResult(SemanticSearchResult),
45 Project(ProjectResult),
46 ProjectList(ProjectListResult),
47 ProjectInfo(ProjectInfoResult),
48 Version(VersionResult),
49 Config(ConfigResult),
50 Stats(StatsResult),
51 Success(SuccessResult),
52}
53
54#[derive(Debug, Clone, Serialize, Deserialize)]
55pub struct MemoryResult {
56 pub memory: Memory,
57}
58
59#[derive(Debug, Clone, Serialize, Deserialize)]
60pub struct MemoryListResult {
61 pub memories: Vec<Memory>,
62 pub total: usize,
63}
64
65#[derive(Debug, Clone, Serialize, Deserialize)]
66pub struct SearchResult {
67 pub memories: Vec<Memory>,
68 pub total: usize,
69 pub elapsed_ms: u64,
70}
71
72#[derive(Debug, Clone, Serialize, Deserialize)]
73pub struct SemanticSearchResult {
74 pub results: Vec<SearchHit>,
75 pub total: usize,
76 pub query_embedding_time_ms: u64,
77 pub search_time_ms: u64,
78}
79
80#[derive(Debug, Clone, Serialize, Deserialize)]
81pub struct SearchHit {
82 pub memory_id: Uuid,
83 pub score: f32,
84 pub memory_type: MemoryType,
85 pub content_preview: String,
86 pub project_id: Option<Uuid>,
87 pub tags: Vec<String>,
88 pub is_chunked: bool,
89 pub chunk_group_id: Option<Uuid>,
90 pub chunk_index: Option<u32>,
91 pub chunk_total: Option<u32>,
92 pub created_at: DateTime<Utc>,
93}
94
95#[derive(Debug, Clone, Serialize, Deserialize)]
96pub struct ProjectResult {
97 pub project: Project,
98}
99
100#[derive(Debug, Clone, Serialize, Deserialize)]
101pub struct ProjectListResult {
102 pub projects: Vec<Project>,
103 pub total: usize,
104}
105
106#[derive(Debug, Clone, Serialize, Deserialize)]
107pub struct ProjectInfoResult {
108 pub project_id: Uuid,
109 pub project_name: Option<String>,
110 pub project_root: String,
111 pub memory_count: usize,
112 pub mr_pid_exists: bool,
113}
114
115#[derive(Debug, Clone, Serialize, Deserialize)]
116pub struct VersionResult {
117 pub version: String,
118}
119
120#[derive(Debug, Clone, Serialize, Deserialize)]
121pub struct ConfigResult {
122 pub key: String,
123 pub value: String,
124}
125
126#[derive(Debug, Clone, Serialize, Deserialize)]
127pub struct StatsResult {
128 pub total_memories: usize,
129 pub active_memories: usize,
130 pub deleted_memories: usize,
131 pub storage_usage: f32,
132 pub avg_importance: f32,
133}
134
135#[derive(Debug, Clone, Serialize, Deserialize)]
136pub struct SuccessResult {
137 pub message: String,
138}
139
140impl From<bool> for SuccessResult {
141 fn from(success: bool) -> Self {
142 Self {
143 message: if success { "Success".to_string() } else { "Failed".to_string() },
144 }
145 }
146}
147
148#[cfg(test)]
149mod tests {
150 use super::*;
151 use crate::types::MemoryType;
152
153 #[test]
154 fn test_success_response() {
155 let memory = Memory::new("test".to_string(), MemoryType::Knowledge);
156 let resp = JsonRpcResponse::success(
157 ResponseResult::Memory(MemoryResult { memory }),
158 1,
159 );
160
161 assert_eq!(resp.jsonrpc, "2.0");
162 assert!(resp.result.is_some());
163 assert!(resp.error.is_none());
164 }
165
166 #[test]
167 fn test_error_response() {
168 let err = JsonRpcError {
169 code: -32001,
170 message: "Not found".to_string(),
171 data: None,
172 };
173 let resp = JsonRpcResponse::error(err, 1);
174
175 assert!(resp.result.is_none());
176 assert!(resp.error.is_some());
177 }
178
179 #[test]
180 fn test_response_serde() {
181 let memory = Memory::new("test".to_string(), MemoryType::Knowledge);
182 let resp = JsonRpcResponse::success(
183 ResponseResult::Memory(MemoryResult { memory }),
184 1,
185 );
186
187 let json = serde_json::to_string(&resp).unwrap();
188 let parsed: JsonRpcResponse = serde_json::from_str(&json).unwrap();
189
190 assert_eq!(resp.id, parsed.id);
191 }
192
193 #[test]
194 fn test_semantic_search_result_serde() {
195 let result = SemanticSearchResult {
196 results: vec![SearchHit {
197 memory_id: Uuid::new_v4(),
198 score: 0.95,
199 memory_type: MemoryType::Decision,
200 content_preview: "test content".to_string(),
201 project_id: Some(Uuid::new_v4()),
202 tags: vec!["critical".to_string()],
203 is_chunked: false,
204 chunk_group_id: None,
205 chunk_index: None,
206 chunk_total: None,
207 created_at: Utc::now(),
208 }],
209 total: 1,
210 query_embedding_time_ms: 10,
211 search_time_ms: 5,
212 };
213
214 let json = serde_json::to_string(&result).unwrap();
215 let parsed: SemanticSearchResult = serde_json::from_str(&json).unwrap();
216
217 assert_eq!(result.total, parsed.total);
218 }
219}