1use serde::{Deserialize, Serialize};
9use serde_json::Value;
10use std::collections::HashMap;
11use std::path::PathBuf;
12
13pub type Result<T> = std::result::Result<T, WorkspaceError>;
15
16#[derive(Debug, thiserror::Error)]
18pub enum WorkspaceError {
19 #[error("IO error: {0}")]
20 Io(#[from] std::io::Error),
21
22 #[error("Serialization error: {0}")]
23 Serialization(String),
24
25 #[error("Workspace not found: {0}")]
26 NotFound(String),
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize)]
31pub struct Workspace {
32 pub id: String,
34
35 pub name: String,
37
38 pub root: PathBuf,
40
41 #[serde(default)]
43 pub metadata: HashMap<String, String>,
44
45 #[serde(default)]
47 pub context: HashMap<String, Value>,
48}
49
50impl Workspace {
51 pub fn new(id: impl Into<String>, name: impl Into<String>, root: PathBuf) -> Self {
53 Self {
54 id: id.into(),
55 name: name.into(),
56 root,
57 metadata: HashMap::new(),
58 context: HashMap::new(),
59 }
60 }
61
62 pub fn with_metadata(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
64 self.metadata.insert(key.into(), value.into());
65 self
66 }
67
68 pub fn with_context(mut self, key: impl Into<String>, value: Value) -> Self {
70 self.context.insert(key.into(), value);
71 self
72 }
73
74 pub fn get_context(&self, key: &str) -> Option<&Value> {
76 self.context.get(key)
77 }
78
79 pub fn get_metadata(&self, key: &str) -> Option<&String> {
81 self.metadata.get(key)
82 }
83}
84
85#[derive(Debug, Default)]
87pub struct WorkspaceManager {
88 workspaces: HashMap<String, Workspace>,
89 active_workspace: Option<String>,
90}
91
92impl WorkspaceManager {
93 pub fn new() -> Self {
95 Self::default()
96 }
97
98 pub fn create(&mut self, workspace: Workspace) {
100 self.workspaces.insert(workspace.id.clone(), workspace);
101 }
102
103 pub fn get(&self, id: &str) -> Option<&Workspace> {
105 self.workspaces.get(id)
106 }
107
108 pub fn get_mut(&mut self, id: &str) -> Option<&mut Workspace> {
110 self.workspaces.get_mut(id)
111 }
112
113 pub fn set_active(&mut self, id: &str) -> Result<()> {
115 if !self.workspaces.contains_key(id) {
116 return Err(WorkspaceError::NotFound(id.to_string()));
117 }
118 self.active_workspace = Some(id.to_string());
119 Ok(())
120 }
121
122 pub fn get_active(&self) -> Option<&Workspace> {
124 self.active_workspace
125 .as_ref()
126 .and_then(|id| self.workspaces.get(id))
127 }
128
129 pub fn get_active_mut(&mut self) -> Option<&mut Workspace> {
131 self.active_workspace
132 .as_ref()
133 .and_then(|id| self.workspaces.get_mut(id))
134 }
135
136 pub fn list(&self) -> Vec<String> {
138 self.workspaces.keys().cloned().collect()
139 }
140
141 pub fn remove(&mut self, id: &str) -> Option<Workspace> {
143 if self.active_workspace.as_deref() == Some(id) {
144 self.active_workspace = None;
145 }
146 self.workspaces.remove(id)
147 }
148}
149
150#[cfg(test)]
151mod tests {
152 use super::*;
153
154 #[test]
155 fn test_workspace_creation() {
156 let workspace = Workspace::new("test", "Test Workspace", PathBuf::from("/tmp/test"));
157 assert_eq!(workspace.id, "test");
158 assert_eq!(workspace.name, "Test Workspace");
159 }
160
161 #[test]
162 fn test_workspace_builder() {
163 let workspace = Workspace::new("test", "Test", PathBuf::from("/tmp"))
164 .with_metadata("version", "1.0")
165 .with_context("key", serde_json::json!({"value": 42}));
166
167 assert_eq!(workspace.get_metadata("version"), Some(&"1.0".to_string()));
168 assert!(workspace.get_context("key").is_some());
169 }
170
171 #[test]
172 fn test_workspace_manager() {
173 let mut manager = WorkspaceManager::new();
174
175 let workspace = Workspace::new("test", "Test", PathBuf::from("/tmp"));
176 manager.create(workspace);
177
178 assert!(manager.get("test").is_some());
179 assert_eq!(manager.list().len(), 1);
180 }
181
182 #[test]
183 fn test_active_workspace() {
184 let mut manager = WorkspaceManager::new();
185
186 let workspace = Workspace::new("test", "Test", PathBuf::from("/tmp"));
187 manager.create(workspace);
188
189 manager.set_active("test").unwrap();
190 assert!(manager.get_active().is_some());
191 assert_eq!(manager.get_active().unwrap().id, "test");
192 }
193
194 #[test]
195 fn test_remove_workspace() {
196 let mut manager = WorkspaceManager::new();
197
198 let workspace = Workspace::new("test", "Test", PathBuf::from("/tmp"));
199 manager.create(workspace);
200
201 manager.set_active("test").unwrap();
202 assert!(manager.remove("test").is_some());
203 assert!(manager.get_active().is_none());
204 }
205}