Skip to main content

memrec_common/protocol/
response.rs

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}