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
78			WorkspaceConfigurationPath:Arc::new(StandardMutex::new(None)),
79
80			IsTrusted:Arc::new(AtomicBool::new(false)),
81
82			WindowState:Arc::new(StandardMutex::new(WindowStateDTO::default())),
83
84			ActiveDocumentURI:Arc::new(StandardMutex::new(None)),
85		}
86	}
87}
88
89impl State {
90	/// Gets the current workspace trust status.
91	pub fn GetTrustStatus(&self) -> bool { self.IsTrusted.load(AtomicOrdering::Relaxed) }
92
93	/// Sets the workspace trust status.
94	pub fn SetTrustStatus(&self, trusted:bool) {
95		self.IsTrusted.store(trusted, AtomicOrdering::Relaxed);
96
97		dev_log!("workspaces", "[WorkspaceState] Trust status set to: {}", trusted);
98	}
99
100	/// Gets the workspace configuration path.
101	pub fn GetConfigurationPath(&self) -> Option<std::path::PathBuf> {
102		self.WorkspaceConfigurationPath.lock().ok().and_then(|guard| guard.clone())
103	}
104
105	/// Sets the workspace configuration path.
106	pub fn SetConfigurationPath(&self, path:Option<std::path::PathBuf>) {
107		if let Ok(mut guard) = self.WorkspaceConfigurationPath.lock() {
108			*guard = path.clone();
109			dev_log!("workspaces", "[WorkspaceState] Configuration path updated to: {:?}", path);
110		}
111	}
112
113	/// Gets the currently active document URI.
114	pub fn GetActiveDocumentURI(&self) -> Option<String> {
115		self.ActiveDocumentURI.lock().ok().and_then(|guard| guard.clone())
116	}
117
118	/// Sets the currently active document URI.
119	pub fn SetActiveDocumentURI(&self, uri:Option<String>) {
120		if let Ok(mut guard) = self.ActiveDocumentURI.lock() {
121			*guard = uri.clone();
122			dev_log!("workspaces", "[WorkspaceState] Active document URI updated to: {:?}", uri);
123		}
124	}
125
126	/// Gets all workspace folders.
127	pub fn GetWorkspaceFolders(&self) -> Vec<WorkspaceFolderStateDTO> {
128		self.WorkspaceFolders.lock().ok().map(|guard| guard.clone()).unwrap_or_default()
129	}
130
131	/// Sets the workspace folders.
132	pub fn SetWorkspaceFolders(&self, folders:Vec<WorkspaceFolderStateDTO>) {
133		if let Ok(mut guard) = self.WorkspaceFolders.lock() {
134			*guard = folders;
135			dev_log!(
136				"workspaces",
137				"[WorkspaceState] Workspace folders updated ({} folders)",
138				guard.len()
139			);
140		}
141	}
142
143	/// Atomically replace the workspace folders and return the (added, removed)
144	/// delta. `added` contains every folder present in the new list but not the
145	/// old one; `removed` contains every folder present in the old list but not
146	/// the new. Comparison is by URI, so re-indexing does not produce spurious
147	/// add/remove pairs.
148	///
149	/// Callers use the delta to drive downstream events such as
150	/// `$deltaWorkspaceFolders` (Cocoon) and `onDidChangeWorkspaceFolders`
151	/// listeners inside extensions.
152	pub fn SetWorkspaceFoldersReturnDelta(
153		&self,
154
155		folders:Vec<WorkspaceFolderStateDTO>,
156	) -> (Vec<WorkspaceFolderStateDTO>, Vec<WorkspaceFolderStateDTO>) {
157		match self.WorkspaceFolders.lock() {
158			Ok(mut guard) => {
159				let Old = guard.clone();
160
161				let OldUris:std::collections::HashSet<String> = Old.iter().map(|F| F.URI.to_string()).collect();
162
163				let NewUris:std::collections::HashSet<String> = folders.iter().map(|F| F.URI.to_string()).collect();
164
165				let Added:Vec<WorkspaceFolderStateDTO> = folders
166					.iter()
167					.filter(|F| !OldUris.contains(&F.URI.to_string()))
168					.cloned()
169					.collect();
170
171				let Removed:Vec<WorkspaceFolderStateDTO> =
172					Old.iter().filter(|F| !NewUris.contains(&F.URI.to_string())).cloned().collect();
173
174				*guard = folders;
175				dev_log!(
176					"workspaces",
177					"[WorkspaceState] Workspace folders updated ({} folders, +{} -{})",
178					guard.len(),
179					Added.len(),
180					Removed.len()
181				);
182
183				(Added, Removed)
184			},
185
186			Err(_) => (Vec::new(), Vec::new()),
187		}
188	}
189
190	/// Gets the window state.
191	pub fn GetWindowState(&self) -> WindowStateDTO {
192		self.WorkspaceFolders
193			.lock()
194			.ok()
195			.and_then(|_| self.WindowState.lock().ok().map(|guard| guard.clone()))
196			.unwrap_or_default()
197	}
198
199	/// Sets the window state.
200	pub fn SetWindowState(&self, state:WindowStateDTO) {
201		if let Ok(mut guard) = self.WindowState.lock() {
202			*guard = state;
203			dev_log!("workspaces", "[WorkspaceState] Window state updated");
204		}
205	}
206}