mockforge_collab/
core_bridge.rs1use crate::error::{CollabError, Result};
8use crate::models::TeamWorkspace;
9use mockforge_core::workspace::Workspace as CoreWorkspace;
10use mockforge_core::workspace_persistence::WorkspacePersistence;
11use serde_json::Value;
12use std::path::Path;
13use uuid::Uuid;
14
15pub struct CoreBridge {
17 persistence: WorkspacePersistence,
18}
19
20impl CoreBridge {
21 pub fn new<P: AsRef<Path>>(workspace_dir: P) -> Self {
23 Self {
24 persistence: WorkspacePersistence::new(workspace_dir),
25 }
26 }
27
28 pub fn team_to_core(&self, team_workspace: &TeamWorkspace) -> Result<CoreWorkspace> {
33 let workspace_json = &team_workspace.config;
35
36 let mut workspace: CoreWorkspace =
38 serde_json::from_value(workspace_json.clone()).map_err(|e| {
39 CollabError::Internal(format!("Failed to deserialize workspace from config: {e}"))
40 })?;
41
42 workspace.id = team_workspace.id.to_string();
45
46 workspace.name = team_workspace.name.clone();
48 workspace.description = team_workspace.description.clone();
49 workspace.updated_at = team_workspace.updated_at;
50
51 workspace.initialize_default_mock_environments();
53
54 Ok(workspace)
55 }
56
57 pub fn core_to_team(
62 &self,
63 core_workspace: &CoreWorkspace,
64 owner_id: Uuid,
65 ) -> Result<TeamWorkspace> {
66 let workspace_json = serde_json::to_value(core_workspace).map_err(|e| {
68 CollabError::Internal(format!("Failed to serialize workspace to JSON: {e}"))
69 })?;
70
71 let mut team_workspace = TeamWorkspace::new(core_workspace.name.clone(), owner_id);
73
74 team_workspace.id = Uuid::parse_str(&core_workspace.id).map_err(|e| {
76 CollabError::Internal(format!(
77 "Invalid workspace ID '{}': {}. Cannot convert to TeamWorkspace with corrupted ID.",
78 core_workspace.id, e
79 ))
80 })?;
81
82 team_workspace.description = core_workspace.description.clone();
83 team_workspace.config = workspace_json;
84 team_workspace.created_at = core_workspace.created_at;
85 team_workspace.updated_at = core_workspace.updated_at;
86
87 Ok(team_workspace)
88 }
89
90 pub fn get_workspace_state(&self, team_workspace: &TeamWorkspace) -> Result<CoreWorkspace> {
94 self.team_to_core(team_workspace)
95 }
96
97 pub fn update_workspace_state(
101 &self,
102 team_workspace: &mut TeamWorkspace,
103 core_workspace: &CoreWorkspace,
104 ) -> Result<()> {
105 let workspace_json = serde_json::to_value(core_workspace)
107 .map_err(|e| CollabError::Internal(format!("Failed to serialize workspace: {e}")))?;
108
109 team_workspace.config = workspace_json;
111 team_workspace.updated_at = chrono::Utc::now();
112
113 Ok(())
114 }
115
116 pub async fn load_workspace_from_disk(
120 &self,
121 workspace_id: &str,
122 owner_id: Uuid,
123 ) -> Result<TeamWorkspace> {
124 let core_workspace = self
126 .persistence
127 .load_workspace(workspace_id)
128 .await
129 .map_err(|e| CollabError::Internal(format!("Failed to load workspace: {e}")))?;
130
131 self.core_to_team(&core_workspace, owner_id)
133 }
134
135 pub async fn save_workspace_to_disk(&self, team_workspace: &TeamWorkspace) -> Result<()> {
139 let core_workspace = self.team_to_core(team_workspace)?;
141
142 self.persistence
144 .save_workspace(&core_workspace)
145 .await
146 .map_err(|e| CollabError::Internal(format!("Failed to save workspace: {e}")))?;
147
148 Ok(())
149 }
150
151 pub async fn export_workspace_for_backup(
155 &self,
156 team_workspace: &TeamWorkspace,
157 ) -> Result<Value> {
158 let core_workspace = self.team_to_core(team_workspace)?;
160
161 serde_json::to_value(&core_workspace)
163 .map_err(|e| CollabError::Internal(format!("Failed to serialize for backup: {e}")))
164 }
165
166 pub async fn import_workspace_from_backup(
170 &self,
171 backup_data: &Value,
172 owner_id: Uuid,
173 new_name: Option<String>,
174 ) -> Result<TeamWorkspace> {
175 let mut core_workspace: CoreWorkspace = serde_json::from_value(backup_data.clone())
177 .map_err(|e| CollabError::Internal(format!("Failed to deserialize backup: {e}")))?;
178
179 if let Some(name) = new_name {
181 core_workspace.name = name;
182 }
183
184 core_workspace.id = Uuid::new_v4().to_string();
186 core_workspace.created_at = chrono::Utc::now();
187 core_workspace.updated_at = chrono::Utc::now();
188
189 self.core_to_team(&core_workspace, owner_id)
191 }
192
193 pub fn get_workspace_state_json(&self, team_workspace: &TeamWorkspace) -> Result<Value> {
197 let core_workspace = self.team_to_core(team_workspace)?;
198 serde_json::to_value(&core_workspace)
199 .map_err(|e| CollabError::Internal(format!("Failed to serialize state: {e}")))
200 }
201
202 pub fn update_workspace_state_from_json(
206 &self,
207 team_workspace: &mut TeamWorkspace,
208 state_json: &Value,
209 ) -> Result<()> {
210 let mut core_workspace: CoreWorkspace = serde_json::from_value(state_json.clone())
212 .map_err(|e| CollabError::Internal(format!("Failed to deserialize state JSON: {e}")))?;
213
214 core_workspace.id = team_workspace.id.to_string();
216 core_workspace.name = team_workspace.name.clone();
217 core_workspace.description = team_workspace.description.clone();
218
219 self.update_workspace_state(team_workspace, &core_workspace)
221 }
222
223 pub fn create_empty_workspace(&self, name: String, owner_id: Uuid) -> Result<TeamWorkspace> {
227 let core_workspace = CoreWorkspace::new(name);
228 self.core_to_team(&core_workspace, owner_id)
229 }
230}
231
232#[cfg(test)]
233mod tests {
234 use super::*;
235
236 #[test]
237 fn test_team_to_core_conversion() {
238 let bridge = CoreBridge::new("/tmp/test");
239 let owner_id = Uuid::new_v4();
240
241 let core_workspace = CoreWorkspace::new("Test Workspace".to_string());
243 let team_workspace = bridge.core_to_team(&core_workspace, owner_id).unwrap();
244
245 let restored = bridge.team_to_core(&team_workspace).unwrap();
247
248 assert_eq!(restored.name, core_workspace.name);
249 assert_eq!(restored.folders.len(), core_workspace.folders.len());
250 assert_eq!(restored.requests.len(), core_workspace.requests.len());
251 }
252
253 #[test]
254 fn test_state_json_roundtrip() {
255 let bridge = CoreBridge::new("/tmp/test");
256 let owner_id = Uuid::new_v4();
257
258 let core_workspace = CoreWorkspace::new("Test".to_string());
260 let mut team_workspace = bridge.core_to_team(&core_workspace, owner_id).unwrap();
261
262 let state_json = bridge.get_workspace_state_json(&team_workspace).unwrap();
264
265 bridge
267 .update_workspace_state_from_json(&mut team_workspace, &state_json)
268 .unwrap();
269
270 let restored = bridge.team_to_core(&team_workspace).unwrap();
272 assert_eq!(restored.name, "Test");
273 }
274
275 #[test]
276 fn test_invalid_uuid_returns_error() {
277 let bridge = CoreBridge::new("/tmp/test");
278 let owner_id = Uuid::new_v4();
279
280 let mut core_workspace = CoreWorkspace::new("Test Invalid UUID".to_string());
282 core_workspace.id = "not-a-valid-uuid".to_string();
283
284 let result = bridge.core_to_team(&core_workspace, owner_id);
286 assert!(result.is_err(), "Expected error for invalid UUID, but conversion succeeded");
287
288 if let Err(e) = result {
290 let error_msg = format!("{}", e);
291 assert!(
292 error_msg.contains("not-a-valid-uuid"),
293 "Error message should contain the invalid UUID: {}",
294 error_msg
295 );
296 }
297 }
298
299 #[test]
300 fn test_valid_uuid_conversion() {
301 let bridge = CoreBridge::new("/tmp/test");
302 let owner_id = Uuid::new_v4();
303 let workspace_uuid = Uuid::new_v4();
304
305 let mut core_workspace = CoreWorkspace::new("Test Valid UUID".to_string());
307 core_workspace.id = workspace_uuid.to_string();
308
309 let team_workspace = bridge.core_to_team(&core_workspace, owner_id).unwrap();
311
312 assert_eq!(team_workspace.id, workspace_uuid);
314 }
315}