Skip to main content

Mountain/ApplicationState/State/WorkspaceState/
WorkspaceState.rs

1//! # WorkspaceState Module (ApplicationState)
2//!
3//! ## RESPONSIBILITIES
4//! Manages workspace-related state including workspace folders, workspace
5//! trust status, workspace configuration path, window state, and the currently
6//! active document URI.
7//!
8//! ## ARCHITECTURAL ROLE
9//! WorkspaceState is part of the **state organization layer**, representing
10//! all workspace-specific state in the application. This includes:
11//! - Workspace folders currently open
12//! - Workspace configuration file path
13//! - Workspace trust/security status
14//! - Main window presentation state
15//! - Currently active document
16//!
17//! ## KEY COMPONENTS
18//! - State: Main struct containing workspace-related fields
19//! - Default: Initialization implementation
20//! - Helper methods: Workspace manipulation utilities
21//!
22//! ## ERROR HANDLING
23//! - Thread-safe access via `Arc<Mutex<...>>`
24//! - Proper lock error handling with `MapLockError` helpers
25//! - Atomic operations for simple types (IsTrusted)
26//!
27//! ## LOGGING
28//! State changes are logged at appropriate levels (debug, info, warn, error).
29//!
30//! ## PERFORMANCE CONSIDERATIONS
31//! - Lock mutexes briefly and release immediately
32//! - Avoid nested locks to prevent deadlocks
33//! - Use Arc for shared ownership across threads
34//! - Use AtomicBool for simple lock-free reads
35//!
36//! ## TODO
37//! - [ ] Add workspace validation invariants
38//! - [ ] Implement workspace change events
39//! - [ ] Add workspace metrics collection
40
41use std::sync::{
42	Arc,
43	Mutex as StandardMutex,
44	atomic::{AtomicBool, Ordering as AtomicOrdering},
45};
46
47use crate::{
48	ApplicationState::DTO::{WindowStateDTO::WindowStateDTO, WorkspaceFolderStateDTO::WorkspaceFolderStateDTO},
49	dev_log,
50};
51
52/// Workspace state containing all workspace-related fields.
53#[derive(Clone)]
54pub struct State {
55	/// Currently open workspace folders.
56	pub WorkspaceFolders:Arc<StandardMutex<Vec<WorkspaceFolderStateDTO>>>,
57
58	/// Path to the workspace configuration file (if any).
59	pub WorkspaceConfigurationPath:Arc<StandardMutex<Option<std::path::PathBuf>>>,
60
61	/// Workspace trust status (security).
62	pub IsTrusted:Arc<AtomicBool>,
63
64	/// Main window presentation state.
65	pub WindowState:Arc<StandardMutex<WindowStateDTO>>,
66
67	/// Currently active document URI.
68	pub ActiveDocumentURI:Arc<StandardMutex<Option<String>>>,
69}
70
71impl Default for State {
72	fn default() -> Self {
73		dev_log!("workspaces", "[WorkspaceState] Initializing default workspace state...");
74
75		Self {
76			WorkspaceFolders:Arc::new(StandardMutex::new(Vec::new())),
77			WorkspaceConfigurationPath:Arc::new(StandardMutex::new(None)),
78			IsTrusted:Arc::new(AtomicBool::new(false)),
79			WindowState:Arc::new(StandardMutex::new(WindowStateDTO::default())),
80			ActiveDocumentURI:Arc::new(StandardMutex::new(None)),
81		}
82	}
83}
84
85impl State {
86	/// Gets the current workspace trust status.
87	pub fn GetTrustStatus(&self) -> bool { self.IsTrusted.load(AtomicOrdering::Relaxed) }
88
89	/// Sets the workspace trust status.
90	pub fn SetTrustStatus(&self, trusted:bool) {
91		self.IsTrusted.store(trusted, AtomicOrdering::Relaxed);
92		dev_log!("workspaces", "[WorkspaceState] Trust status set to: {}", trusted);
93	}
94
95	/// Gets the workspace configuration path.
96	pub fn GetConfigurationPath(&self) -> Option<std::path::PathBuf> {
97		self.WorkspaceConfigurationPath.lock().ok().and_then(|guard| guard.clone())
98	}
99
100	/// Sets the workspace configuration path.
101	pub fn SetConfigurationPath(&self, path:Option<std::path::PathBuf>) {
102		if let Ok(mut guard) = self.WorkspaceConfigurationPath.lock() {
103			*guard = path.clone();
104			dev_log!("workspaces", "[WorkspaceState] Configuration path updated to: {:?}", path);
105		}
106	}
107
108	/// Gets the currently active document URI.
109	pub fn GetActiveDocumentURI(&self) -> Option<String> {
110		self.ActiveDocumentURI.lock().ok().and_then(|guard| guard.clone())
111	}
112
113	/// Sets the currently active document URI.
114	pub fn SetActiveDocumentURI(&self, uri:Option<String>) {
115		if let Ok(mut guard) = self.ActiveDocumentURI.lock() {
116			*guard = uri.clone();
117			dev_log!("workspaces", "[WorkspaceState] Active document URI updated to: {:?}", uri);
118		}
119	}
120
121	/// Gets all workspace folders.
122	pub fn GetWorkspaceFolders(&self) -> Vec<WorkspaceFolderStateDTO> {
123		self.WorkspaceFolders.lock().ok().map(|guard| guard.clone()).unwrap_or_default()
124	}
125
126	/// Sets the workspace folders.
127	pub fn SetWorkspaceFolders(&self, folders:Vec<WorkspaceFolderStateDTO>) {
128		if let Ok(mut guard) = self.WorkspaceFolders.lock() {
129			*guard = folders;
130			dev_log!(
131				"workspaces",
132				"[WorkspaceState] Workspace folders updated ({} folders)",
133				guard.len()
134			);
135		}
136	}
137
138	/// Atomically replace the workspace folders and return the (added, removed)
139	/// delta. `added` contains every folder present in the new list but not the
140	/// old one; `removed` contains every folder present in the old list but not
141	/// the new. Comparison is by URI, so re-indexing does not produce spurious
142	/// add/remove pairs.
143	///
144	/// Callers use the delta to drive downstream events such as
145	/// `$deltaWorkspaceFolders` (Cocoon) and `onDidChangeWorkspaceFolders`
146	/// listeners inside extensions.
147	pub fn SetWorkspaceFoldersReturnDelta(
148		&self,
149		folders:Vec<WorkspaceFolderStateDTO>,
150	) -> (Vec<WorkspaceFolderStateDTO>, Vec<WorkspaceFolderStateDTO>) {
151		match self.WorkspaceFolders.lock() {
152			Ok(mut guard) => {
153				let Old = guard.clone();
154				let OldUris:std::collections::HashSet<String> = Old.iter().map(|F| F.URI.to_string()).collect();
155				let NewUris:std::collections::HashSet<String> = folders.iter().map(|F| F.URI.to_string()).collect();
156				let Added:Vec<WorkspaceFolderStateDTO> = folders
157					.iter()
158					.filter(|F| !OldUris.contains(&F.URI.to_string()))
159					.cloned()
160					.collect();
161				let Removed:Vec<WorkspaceFolderStateDTO> =
162					Old.iter().filter(|F| !NewUris.contains(&F.URI.to_string())).cloned().collect();
163				*guard = folders;
164				dev_log!(
165					"workspaces",
166					"[WorkspaceState] Workspace folders updated ({} folders, +{} -{})",
167					guard.len(),
168					Added.len(),
169					Removed.len()
170				);
171				(Added, Removed)
172			},
173			Err(_) => (Vec::new(), Vec::new()),
174		}
175	}
176
177	/// Gets the window state.
178	pub fn GetWindowState(&self) -> WindowStateDTO {
179		self.WorkspaceFolders
180			.lock()
181			.ok()
182			.and_then(|_| self.WindowState.lock().ok().map(|guard| guard.clone()))
183			.unwrap_or_default()
184	}
185
186	/// Sets the window state.
187	pub fn SetWindowState(&self, state:WindowStateDTO) {
188		if let Ok(mut guard) = self.WindowState.lock() {
189			*guard = state;
190			dev_log!("workspaces", "[WorkspaceState] Window state updated");
191		}
192	}
193}