Skip to main content

Mountain/Environment/
MountainEnvironment.rs

1//! # MountainEnvironment (Environment)
2//!
3//! The primary dependency injection (DI) container for the Mountain
4//! application. It implements all provider traits defined in the Common crate,
5//! acting as the central orchestrator that provides access to all platform
6//! services.
7//!
8//! ## RESPONSIBILITIES
9//!
10//! ### 1. Dependency Injection Container
11//! - Implements `Requires<T>` for all 25+ provider traits via macro
12//! - Enable other components to request dependencies through `Require()` method
13//! - Provide lazy initialization with proper lifetime management
14//! - Support circular dependencies via `Arc`-wrapped self-references
15//!
16//! ### 2. Application Lifecycle Management
17//! - Hold references to Tauri `AppHandle` and `ApplicationState`
18//! - Manage the core application context and state
19//! - Coordinate initialization sequence for all providers
20//! - Support graceful shutdown and cleanup
21//!
22//! ### 3. Air Integration (Optional)
23//! - Manage the Air gRPC client for cloud-based services when `AirIntegration`
24//!   feature enabled
25//! - Enable dynamic switching between local and cloud services
26//! - Provide health checking for Air daemon availability
27//! - Support fallback to local services when Air unavailable
28//!
29//! ### 4. Extension Management
30//! - Implement `ExtensionManagementService` for discovering extensions
31//! - Scan registered paths for valid extensions
32//! - Manage extension metadata in `ApplicationState`
33//! - Provide extension list to extension host (Cocoon)
34//!
35//! ### 5. Service Orchestration
36//! - Act as central coordinator between all providers (FileSystem, Document,
37//!   Command, etc.)
38//! - Ensure proper initialization order and dependency resolution
39//! - Facilitate inter-provider communication via IPC or direct calls
40//! - Provide capability resolution through `Requires<T>` trait
41//!
42//! ## ARCHITECTURAL ROLE
43//!
44//! MountainEnvironment is the **central DI container and service locator** for
45//! the entire application:
46//!
47//! ```text
48//! Component ──► Requires<T> ──► MountainEnvironment ──► Arc<dyn T>
49//! ```
50//!
51//! ### Position in Mountain
52//! - `Environment` module: Root of the dependency injection system
53//! - Implements Common crate's `Environment` and `Requires` traits
54//! - All providers accessed through capability-based lookups
55//! - Created early in startup and shared via `Arc<MountainEnvironment>`
56//!
57//! ### Dependencies (Incoming)
58//! - `CommonLibrary::*` provider traits (25+ interfaces)
59//! - `tauri::AppHandle`: UI and platform integration
60//! - `ApplicationState`: Shared application state
61//! - `AirServiceClient` (optional): Cloud service integration
62//!
63//! ### Dependents (Outgoing)
64//! - All command handlers: Require capabilities from environment
65//! - `ApplicationRunTime`: Uses environment for effect execution
66//! - Provider implementations: self-referencing via `Arc<dyn Trait>`
67//! - `InitializationData`: Uses environment to gather extensions and workspace
68//!   data
69//!
70//! ## DEPENDENCY INJECTION PATTERN
71//!
72//! Mountain uses a **service locator pattern** with trait-based lookup:
73//!
74//! ```rust,ignore
75//! impl Requires<dyn FileSystemReader> for MountainEnvironment {
76//!     fn Require(&self) -> Arc<dyn FileSystemReader> {
77//!         Arc::new(self.clone()) // Self implements FileSystemReader
78//!     }
79//! }
80//! ```
81//!
82//! This enables:
83//! - **Loose coupling**: Components depend on traits, not concrete types
84//! - **Circular dependencies**: All providers can require each other through
85//!   Environment
86//! - **Testability**: Environment can be mocked or stubbed for tests
87//! - **Flexibility**: New providers added without changing dependents
88//!
89//! ## INITIALIZATION SEQUENCE
90//!
91//! 1. **Create Environment**: `MountainEnvironment::Create(app_handle,
92//!    app_state)`
93//! 2. **Provider Instantiation**: Lazy through `Requires<T>` trait calls
94//! 3. **State Access**: Each provider can access `ApplicationState` and
95//!    `AppHandle` via self
96//! 4. **Inter-Provider Communication**: Via trait methods (direct Rust calls)
97//!    or IPC
98//!
99//! Note: Provider implementations are separate modules in `Environment/`
100//! directory. Each module implements a specific trait and can be
101//! included/excluded via feature flags.
102//!
103//! ## AIR INTEGRATION
104//!
105//! When `AirIntegration` feature is enabled:
106//! - `AirClient` field is
107//!   `Option<Arc<AirServiceClient<tonic::transport::Channel>>>`
108//! - `CreateWithAir()` allows passing pre-configured Air client
109//! - `SetAirClient()` allows dynamic switching at runtime
110//! - `IsAirAvailable()` performs health check on Air daemon
111//! - Providers like `SecretProvider` and `UpdateService` can delegate to Air
112//!
113//! When feature is disabled:
114//! - `AirClient` field is absent
115//! - `IsAirAvailable()` always returns `false`
116//! - Local-only service implementations are used
117//!
118//! ## VS CODE REFERENCE
119//!
120//! Patterns borrowed from VS Code's service architecture:
121//! - `vs/platform/instantiation/common/instantiation.ts` - Service collection
122//!   and instantiation
123//! - `vs/platform/workspace/common/workspace.ts` - Service locator pattern
124//! - `vs/workbench/services/extensions/common/extensions.ts` - Extension point
125//!   management
126//!
127//! Key concepts:
128//! - Service lifetime management through `Arc` reference counting
129//! - Trait-based service definitions for loose coupling
130//! - Lazy initialization on first use
131//! - Thread-safe service access
132//!
133//! ## PROVIDER TRAITS IMPLEMENTED
134//!
135//! The following 25+ provider traits are implemented using the
136//! `impl_provider!` macro:
137//! - `CommandExecutor` - Execute commands and actions
138//! - `ConfigurationProvider` - Access application configuration
139//! - `ConfigurationInspector` - Inspect configuration values
140//! - `CustomEditorProvider` - Custom document editor support
141//! - `DebugService` - Debug adapter protocol integration
142//! - `DiagnosticManager` - Diagnostic/lint/error management
143//! - `DocumentProvider` - Document lifecycle and content access
144//! - `FileSystemReader` / `FileSystemWriter` - File system operations
145//! - `IPCProvider` - Inter-process communication
146//! - `KeybindingProvider` - Keybinding resolution and execution
147//! - `LanguageFeatureProviderRegistry` - LSP and language features
148//! - `OutputChannelManager` - Output panel channel management
149//! - `SecretProvider` - Credential and secret storage
150//! - `SourceControlManagementProvider` - Git/SCM integration
151//! - `StatusBarProvider` - Status bar item management
152//! - `StorageProvider` - Key-value storage
153//! - `SynchronizationProvider` - Remote sync and collaboration
154//! - `TerminalProvider` - Integrated terminal management
155//! - `TestController` - Test discovery and execution
156//! - `TreeViewProvider` - Tree view UI components
157//! - `UserInterfaceProvider` - UI dialog and interaction services
158//! - `WebviewProvider` - Webview panel management
159//! - `WorkspaceProvider` - Workspace and folder management
160//! - `WorkspaceEditApplier` - Workspace edit application (text changes)
161//! - `ExtensionManagementService` - Extension scanning and metadata
162//! - `SearchProvider` - Search and replace functionality
163//!
164//! ## TODO
165//! - [ ] Add telemetry integration for performance monitoring
166//! - [ ] Implement proper provider health checking
167//! - [ ] Add provider dependency validation on initialization
168//! - [ ] Consider async initialization for providers
169//! - [ ] Add circuit breaker pattern for external service calls (Air)
170//! - [ ] Implement graceful degradation when providers fail
171//! - [ ] Add metrics collection for provider usage
172//! - [ ] Consider provider initialization order dependencies
173
174use std::sync::Arc;
175
176// Import Air service client when Air integration is enabled
177#[cfg(feature = "AirIntegration")]
178use AirLibrary::Vine::Generated::air::air_service_client::AirServiceClient;
179use CommonLibrary::{
180	Command::CommandExecutor::CommandExecutor,
181	Configuration::{ConfigurationInspector::ConfigurationInspector, ConfigurationProvider::ConfigurationProvider},
182	CustomEditor::CustomEditorProvider::CustomEditorProvider,
183	Debug::DebugService::DebugService,
184	Diagnostic::DiagnosticManager::DiagnosticManager,
185	Document::DocumentProvider::DocumentProvider,
186	Environment::{Environment::Environment, Requires::Requires},
187	Error::CommonError::CommonError,
188	ExtensionManagement::ExtensionManagementService::ExtensionManagementService,
189	FileSystem::{FileSystemReader::FileSystemReader, FileSystemWriter::FileSystemWriter},
190	IPC::IPCProvider::IPCProvider,
191	Keybinding::KeybindingProvider::KeybindingProvider,
192	LanguageFeature::LanguageFeatureProviderRegistry::LanguageFeatureProviderRegistry,
193	Output::OutputChannelManager::OutputChannelManager,
194	Search::SearchProvider::SearchProvider,
195	Secret::SecretProvider::SecretProvider,
196	SourceControlManagement::SourceControlManagementProvider::SourceControlManagementProvider,
197	StatusBar::StatusBarProvider::StatusBarProvider,
198	Storage::StorageProvider::StorageProvider,
199	Synchronization::SynchronizationProvider::SynchronizationProvider,
200	Terminal::TerminalProvider::TerminalProvider,
201	Testing::TestController::TestController,
202	TreeView::TreeViewProvider::TreeViewProvider,
203	UserInterface::UserInterfaceProvider::UserInterfaceProvider,
204	Webview::WebviewProvider::WebviewProvider,
205	Workspace::{WorkspaceEditApplier::WorkspaceEditApplier, WorkspaceProvider::WorkspaceProvider},
206};
207use async_trait::async_trait;
208use serde_json::Value;
209use tauri::{AppHandle, Wry};
210
211use crate::{
212	ApplicationState::{ApplicationState, DTO::ExtensionDescriptionStateDTO::ExtensionDescriptionStateDTO},
213	dev_log,
214};
215// Import the macro for generating trait implementations
216// Note: Macros annotated with #[macro_export] are available at crate root
217use crate::impl_provider;
218
219/// The concrete `Environment` for the Mountain application.
220#[derive(Clone)]
221pub struct MountainEnvironment {
222	pub ApplicationHandle:AppHandle<Wry>,
223
224	pub ApplicationState:Arc<ApplicationState>,
225
226	/// Optional Air client for cloud-based services.
227	/// When provided, providers like SecretProvider and UpdateService can
228	/// delegate to Air.
229	#[cfg(feature = "AirIntegration")]
230	pub AirClient:Option<AirServiceClient<tonic::transport::Channel>>,
231}
232
233impl MountainEnvironment {
234	/// Creates a new `MountainEnvironment` instance.
235	#[allow(unused_mut)]
236	pub fn Create(ApplicationHandle:AppHandle<Wry>, ApplicationState:Arc<ApplicationState>) -> Self {
237		dev_log!("lifecycle", "[MountainEnvironment] New instance created.");
238
239		#[cfg(feature = "AirIntegration")]
240		{
241			Self { ApplicationHandle, ApplicationState, AirClient:None }
242		}
243
244		#[cfg(not(feature = "AirIntegration"))]
245		{
246			Self { ApplicationHandle, ApplicationState }
247		}
248	}
249
250	/// Creates a new `MountainEnvironment` instance with an optional Air
251	/// client. When AirClient is provided, providers can delegate to Air for
252	/// cloud-based services.
253	#[cfg(feature = "AirIntegration")]
254	pub fn CreateWithAir(
255		ApplicationHandle:AppHandle<Wry>,
256		ApplicationState:Arc<ApplicationState>,
257		AirClient:Option<AirServiceClient<tonic::transport::Channel>>,
258	) -> Self {
259		dev_log!(
260			"lifecycle",
261			"[MountainEnvironment] New instance created with Air client: {}",
262			AirClient.is_some()
263		);
264
265		Self { ApplicationHandle, ApplicationState, AirClient }
266	}
267
268	/// Updates the Air client for this environment.
269	/// This allows dynamically switching between Air and local services.
270	#[cfg(feature = "AirIntegration")]
271	pub fn SetAirClient(&mut self, AirClient:Option<AirServiceClient<tonic::transport::Channel>>) {
272		dev_log!("lifecycle", "[MountainEnvironment] Air client updated: {}", AirClient.is_some());
273
274		self.AirClient = AirClient;
275	}
276
277	/// Returns whether Air is available and ready.
278	#[cfg(feature = "AirIntegration")]
279	pub async fn IsAirAvailable(&self) -> bool {
280		// TODO: Implement proper health check when AirClient wrapper is available
281		// The raw gRPC client requires &mut self for health_check, but
282		// MountainEnvironment stores an immutable reference. This will be fixed when
283		// the AirClient wrapper is properly integrated.
284		if let Some(_AirClient) = &self.AirClient {
285			// For now, assume Air is available if the client exists
286			dev_log!(
287				"lifecycle",
288				"[MountainEnvironment] Air client configured (health check disabled pending integration)"
289			);
290			true
291		} else {
292			dev_log!("lifecycle", "[MountainEnvironment] No Air client configured");
293			false
294		}
295	}
296
297	/// Returns whether Air is available and ready.
298	#[cfg(not(feature = "AirIntegration"))]
299	pub async fn IsAirAvailable(&self) -> bool { false }
300
301	/// Scans a directory for extensions and returns their package.json data
302	async fn ScanExtensionDirectory(&self, path:&std::path::PathBuf) -> Result<Vec<serde_json::Value>, CommonError> {
303		use std::fs;
304
305		let mut extensions = Vec::new();
306
307		// Check if directory exists
308		if !path.exists() || !path.is_dir() {
309			dev_log!(
310				"lifecycle",
311				"warn: [ExtensionManagementService] Extension directory does not exist: {:?}",
312				path
313			);
314			return Ok(extensions);
315		}
316
317		// Read directory contents
318		let entries = fs::read_dir(path).map_err(|error| {
319			CommonError::FileSystemIO {
320				Path:path.clone(),
321				Description:format!("Failed to read extension directory: {}", error),
322			}
323		})?;
324
325		for entry in entries {
326			let entry = entry.map_err(|error| {
327				CommonError::FileSystemIO {
328					Path:path.clone(),
329					Description:format!("Failed to read directory entry: {}", error),
330				}
331			})?;
332
333			let entry_path = entry.path();
334			if entry_path.is_dir() {
335				// Look for package.json in the extension directory
336				let package_json_path = entry_path.join("package.json");
337				if package_json_path.exists() {
338					match fs::read_to_string(&package_json_path) {
339						Ok(content) => {
340							match serde_json::from_str::<Value>(&content) {
341								Ok(mut package_json) => {
342									// Add extension location information
343									if let Some(obj) = package_json.as_object_mut() {
344										obj.insert(
345											"ExtensionLocation".to_string(),
346											Value::String(entry_path.to_string_lossy().to_string()),
347										);
348									}
349									extensions.push(package_json);
350									dev_log!(
351										"lifecycle",
352										"[ExtensionManagementService] Found extension at: {:?}",
353										entry_path
354									);
355								},
356								Err(error) => {
357									dev_log!(
358										"lifecycle",
359										"warn: [ExtensionManagementService] Failed to parse package.json at {:?}: {}",
360										package_json_path,
361										error
362									);
363								},
364							}
365						},
366						Err(error) => {
367							dev_log!(
368								"lifecycle",
369								"warn: [ExtensionManagementService] Failed to read package.json at {:?}: {}",
370								package_json_path,
371								error
372							);
373						},
374					}
375				}
376			}
377		}
378
379		Ok(extensions)
380	}
381}
382
383impl Environment for MountainEnvironment {}
384
385#[async_trait]
386impl ExtensionManagementService for MountainEnvironment {
387	async fn ScanForExtensions(&self) -> Result<(), CommonError> {
388		dev_log!("lifecycle", "[ExtensionManagementService] Scanning for extensions...");
389
390		// Get the extension scan paths from ApplicationState
391		let ScanPaths:Vec<std::path::PathBuf> = {
392			let ScanPathsGuard = self
393				.ApplicationState
394				.Extension
395				.Registry
396				.ExtensionScanPaths
397				.lock()
398				.map_err(|Error| CommonError::StateLockPoisoned { Context:Error.to_string() })?;
399			ScanPathsGuard.clone()
400		};
401
402		let mut extensions = Vec::new();
403
404		// Scan each extension directory
405		for path in ScanPaths {
406			if let Ok(mut scan_result) = self.ScanExtensionDirectory(&path).await {
407				extensions.append(&mut scan_result);
408			}
409		}
410
411		// Update ApplicationState with scanned extensions
412		let mut ScannedExtensionsGuard = self
413			.ApplicationState
414			.Extension
415			.ScannedExtensions
416			.ScannedExtensions
417			.lock()
418			.map_err(|Error| CommonError::StateLockPoisoned { Context:Error.to_string() })?;
419
420		ScannedExtensionsGuard.clear();
421
422		for extension in extensions {
423			// The scanner returns camelCase JSON (serde rename_all = "camelCase").
424			// Deserialize directly into ExtensionDescriptionStateDTO.
425			match serde_json::from_value::<ExtensionDescriptionStateDTO>(extension.clone()) {
426				Ok(Dto) => {
427					// Use identifier.value or fall back to name
428					let Key = Dto
429						.Identifier
430						.as_object()
431						.and_then(|O| O.get("value"))
432						.and_then(|V| V.as_str())
433						.unwrap_or(&Dto.Name)
434						.to_string();
435					if !Key.is_empty() {
436						ScannedExtensionsGuard.insert(Key, Dto);
437					}
438				},
439				Err(Error) => {
440					let Name = extension.get("name").and_then(|V| V.as_str()).unwrap_or("?");
441					dev_log!(
442						"lifecycle",
443						"warn: [ExtensionManagementService] Failed to parse extension '{}': {}",
444						Name,
445						Error
446					);
447				},
448			}
449		}
450
451		dev_log!(
452			"lifecycle",
453			"[ExtensionManagementService] Found {} extensions",
454			ScannedExtensionsGuard.len()
455		);
456		Ok(())
457	}
458
459	async fn GetExtensions(&self) -> Result<Vec<Value>, CommonError> {
460		let ScannedExtensionsGuard = self
461			.ApplicationState
462			.Extension
463			.ScannedExtensions
464			.ScannedExtensions
465			.lock()
466			.map_err(|Error| CommonError::StateLockPoisoned { Context:Error.to_string() })?;
467
468		let GuardLen = ScannedExtensionsGuard.len();
469
470		let Extensions:Vec<Value> = ScannedExtensionsGuard
471			.values()
472			.map(|ext| serde_json::to_value(ext).unwrap_or(Value::Null))
473			.collect();
474
475		let SerializedCount = Extensions.iter().filter(|v| !v.is_null()).count();
476
477		dev_log!(
478			"lifecycle",
479			"[MountainEnvironment] GetExtensions: ScannedExtensions map={} entries, serialized={} non-null",
480			GuardLen,
481			SerializedCount
482		);
483
484		Ok(Extensions)
485	}
486
487	async fn GetExtension(&self, id:String) -> Result<Option<Value>, CommonError> {
488		let ScannedExtensionsGuard = self
489			.ApplicationState
490			.Extension
491			.ScannedExtensions
492			.ScannedExtensions
493			.lock()
494			.map_err(|Error| CommonError::StateLockPoisoned { Context:Error.to_string() })?;
495
496		if let Some(extension_dto) = ScannedExtensionsGuard.get(&id) {
497			// Convert ExtensionDescriptionStateDTO back to JSON Value
498			let mut extension_value = serde_json::Map::new();
499			extension_value.insert("Identifier".to_string(), extension_dto.Identifier.clone());
500			extension_value.insert("Name".to_string(), Value::String(extension_dto.Name.clone()));
501			extension_value.insert("Version".to_string(), Value::String(extension_dto.Version.clone()));
502			extension_value.insert("Publisher".to_string(), Value::String(extension_dto.Publisher.clone()));
503			extension_value.insert("Engines".to_string(), extension_dto.Engines.clone());
504
505			if let Some(main) = &extension_dto.Main {
506				extension_value.insert("Main".to_string(), Value::String(main.clone()));
507			}
508
509			if let Some(browser) = &extension_dto.Browser {
510				extension_value.insert("Browser".to_string(), Value::String(browser.clone()));
511			}
512
513			if let Some(module_type) = &extension_dto.ModuleType {
514				extension_value.insert("ModuleType".to_string(), Value::String(module_type.clone()));
515			}
516
517			extension_value.insert("IsBuiltin".to_string(), Value::Bool(extension_dto.IsBuiltin));
518			extension_value.insert("IsUnderDevelopment".to_string(), Value::Bool(extension_dto.IsUnderDevelopment));
519			extension_value.insert("ExtensionLocation".to_string(), extension_dto.ExtensionLocation.clone());
520
521			if let Some(activation_events) = &extension_dto.ActivationEvents {
522				let events:Vec<Value> = activation_events.iter().map(|e| Value::String(e.clone())).collect();
523				extension_value.insert("ActivationEvents".to_string(), Value::Array(events));
524			}
525
526			if let Some(contributes) = &extension_dto.Contributes {
527				extension_value.insert("Contributes".to_string(), contributes.clone());
528			}
529
530			Ok(Some(Value::Object(extension_value)))
531		} else {
532			Ok(None)
533		}
534	}
535}
536
537// --- Capability Requirement Implementations (DI) ---
538// All trait implementations are generated using the impl_provider! macro
539
540// Command and Configuration
541impl_provider!(CommandExecutor);
542impl_provider!(ConfigurationProvider);
543impl_provider!(ConfigurationInspector);
544
545// Custom Editor and Debug
546impl_provider!(CustomEditorProvider);
547impl_provider!(DebugService);
548
549// Document and Diagnostic
550impl_provider!(DocumentProvider);
551impl_provider!(DiagnosticManager);
552
553// File System
554impl_provider!(FileSystemReader);
555impl_provider!(FileSystemWriter);
556
557// IPC and Keybinding
558impl_provider!(IPCProvider);
559impl_provider!(KeybindingProvider);
560
561// Language Features and Output
562impl_provider!(LanguageFeatureProviderRegistry);
563impl_provider!(OutputChannelManager);
564
565// Secret and SCM
566impl_provider!(SecretProvider);
567impl_provider!(SourceControlManagementProvider);
568
569// Status Bar and Storage
570impl_provider!(StatusBarProvider);
571impl_provider!(StorageProvider);
572
573// Synchronization and Terminal
574impl_provider!(SynchronizationProvider);
575impl_provider!(TerminalProvider);
576
577// Test and Tree View
578impl_provider!(TestController);
579impl_provider!(TreeViewProvider);
580
581// UI and Webview
582impl_provider!(UserInterfaceProvider);
583impl_provider!(WebviewProvider);
584
585// Workspace
586impl_provider!(WorkspaceProvider);
587impl_provider!(WorkspaceEditApplier);
588
589// Extension Management and Search
590impl_provider!(ExtensionManagementService);
591impl_provider!(SearchProvider);