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, app_state)`
92//! 2. **Provider Instantiation**: Lazy through `Requires<T>` trait calls
93//! 3. **State Access**: Each provider can access `ApplicationState` and
94//!    `AppHandle` via self
95//! 4. **Inter-Provider Communication**: Via trait methods (direct Rust calls)
96//!    or IPC
97//!
98//! Note: Provider implementations are separate modules in `Environment/`
99//! directory. Each module implements a specific trait and can be
100//! included/excluded via feature flags.
101//!
102//! ## AIR INTEGRATION
103//!
104//! When `AirIntegration` feature is enabled:
105//! - `AirClient` field is
106//!   `Option<Arc<AirServiceClient<tonic::transport::Channel>>>`
107//! - `CreateWithAir()` allows passing pre-configured Air client
108//! - `SetAirClient()` allows dynamic switching at runtime
109//! - `IsAirAvailable()` performs health check on Air daemon
110//! - Providers like `SecretProvider` and `UpdateService` can delegate to Air
111//!
112//! When feature is disabled:
113//! - `AirClient` field is absent
114//! - `IsAirAvailable()` always returns `false`
115//! - Local-only service implementations are used
116//!
117//! ## VS CODE REFERENCE
118//!
119//! Patterns borrowed from VS Code's service architecture:
120//! - `vs/platform/instantiation/common/instantiation.ts` - Service collection
121//!   and instantiation
122//! - `vs/platform/workspace/common/workspace.ts` - Service locator pattern
123//! - `vs/workbench/services/extensions/common/extensions.ts` - Extension point
124//!   management
125//!
126//! Key concepts:
127//! - Service lifetime management through `Arc` reference counting
128//! - Trait-based service definitions for loose coupling
129//! - Lazy initialization on first use
130//! - Thread-safe service access
131//!
132//! ## PROVIDER TRAITS IMPLEMENTED
133//!
134//! The following 25+ provider traits are implemented using the
135//! `impl_provider!` macro:
136//! - `CommandExecutor` - Execute commands and actions
137//! - `ConfigurationProvider` - Access application configuration
138//! - `ConfigurationInspector` - Inspect configuration values
139//! - `CustomEditorProvider` - Custom document editor support
140//! - `DebugService` - Debug adapter protocol integration
141//! - `DiagnosticManager` - Diagnostic/lint/error management
142//! - `DocumentProvider` - Document lifecycle and content access
143//! - `FileSystemReader` / `FileSystemWriter` - File system operations
144//! - `IPCProvider` - Inter-process communication
145//! - `KeybindingProvider` - Keybinding resolution and execution
146//! - `LanguageFeatureProviderRegistry` - LSP and language features
147//! - `OutputChannelManager` - Output panel channel management
148//! - `SecretProvider` - Credential and secret storage
149//! - `SourceControlManagementProvider` - Git/SCM integration
150//! - `StatusBarProvider` - Status bar item management
151//! - `StorageProvider` - Key-value storage
152//! - `SynchronizationProvider` - Remote sync and collaboration
153//! - `TerminalProvider` - Integrated terminal management
154//! - `TestController` - Test discovery and execution
155//! - `TreeViewProvider` - Tree view UI components
156//! - `UserInterfaceProvider` - UI dialog and interaction services
157//! - `WebviewProvider` - Webview panel management
158//! - `WorkspaceProvider` - Workspace and folder management
159//! - `WorkspaceEditApplier` - Workspace edit application (text changes)
160//! - `ExtensionManagementService` - Extension scanning and metadata
161//! - `SearchProvider` - Search and replace functionality
162//!
163//! ## TODO
164//! - [ ] Add telemetry integration for performance monitoring
165//! - [ ] Implement proper provider health checking
166//! - [ ] Add provider dependency validation on initialization
167//! - [ ] Consider async initialization for providers
168//! - [ ] Add circuit breaker pattern for external service calls (Air)
169//! - [ ] Implement graceful degradation when providers fail
170//! - [ ] Add metrics collection for provider usage
171//! - [ ] Consider provider initialization order dependencies
172
173use std::sync::Arc;
174
175// Import Air service client when Air integration is enabled
176#[cfg(feature = "AirIntegration")]
177use AirLibrary::Vine::Generated::air::air_service_client::AirServiceClient;
178#[cfg(feature = "AirIntegration")]
179use AirLibrary::Vine::Generated::air::HealthCheckRequest;
180use CommonLibrary::{
181	Command::CommandExecutor::CommandExecutor,
182	Configuration::{ConfigurationInspector::ConfigurationInspector, ConfigurationProvider::ConfigurationProvider},
183	CustomEditor::CustomEditorProvider::CustomEditorProvider,
184	Debug::DebugService::DebugService,
185	Diagnostic::DiagnosticManager::DiagnosticManager,
186	Document::DocumentProvider::DocumentProvider,
187	Environment::{Environment::Environment, Requires::Requires},
188	Error::CommonError::CommonError,
189	ExtensionManagement::ExtensionManagementService::ExtensionManagementService,
190	FileSystem::{FileSystemReader::FileSystemReader, FileSystemWriter::FileSystemWriter},
191	IPC::IPCProvider::IPCProvider,
192	Keybinding::KeybindingProvider::KeybindingProvider,
193	LanguageFeature::LanguageFeatureProviderRegistry::LanguageFeatureProviderRegistry,
194	Output::OutputChannelManager::OutputChannelManager,
195	Search::SearchProvider::SearchProvider,
196	Secret::SecretProvider::SecretProvider,
197	SourceControlManagement::SourceControlManagementProvider::SourceControlManagementProvider,
198	StatusBar::StatusBarProvider::StatusBarProvider,
199	Storage::StorageProvider::StorageProvider,
200	Synchronization::SynchronizationProvider::SynchronizationProvider,
201	Terminal::TerminalProvider::TerminalProvider,
202	Testing::TestController::TestController,
203	TreeView::TreeViewProvider::TreeViewProvider,
204	UserInterface::UserInterfaceProvider::UserInterfaceProvider,
205	Webview::WebviewProvider::WebviewProvider,
206	Workspace::{WorkspaceEditApplier::WorkspaceEditApplier, WorkspaceProvider::WorkspaceProvider},
207};
208use async_trait::async_trait;
209use log::{info, warn};
210use serde_json::Value;
211use tauri::{AppHandle, Manager, Wry};
212
213use crate::ApplicationState::{ApplicationState, DTO::ExtensionDescriptionStateDTO::ExtensionDescriptionStateDTO};
214// Import the macro for generating trait implementations
215// Note: Macros annotated with #[macro_export] are available at crate root
216use crate::impl_provider;
217
218/// The concrete `Environment` for the Mountain application.
219#[derive(Clone)]
220pub struct MountainEnvironment {
221	pub ApplicationHandle:AppHandle<Wry>,
222
223	pub ApplicationState:Arc<ApplicationState>,
224
225	/// Optional Air client for cloud-based services.
226	/// When provided, providers like SecretProvider and UpdateService can
227	/// delegate to Air.
228	#[cfg(feature = "AirIntegration")]
229	pub AirClient:Option<AirServiceClient<tonic::transport::Channel>>,
230}
231
232impl MountainEnvironment {
233	/// Creates a new `MountainEnvironment` instance.
234	#[allow(unused_mut)]
235	pub fn Create(ApplicationHandle:AppHandle<Wry>, ApplicationState:Arc<ApplicationState>) -> Self {
236		info!("[MountainEnvironment] New instance created.");
237
238		#[cfg(feature = "AirIntegration")]
239		{
240			Self { ApplicationHandle, ApplicationState, AirClient:None }
241		}
242
243		#[cfg(not(feature = "AirIntegration"))]
244		{
245			Self { ApplicationHandle, ApplicationState }
246		}
247	}
248
249	/// Creates a new `MountainEnvironment` instance with an optional Air
250	/// client. When AirClient is provided, providers can delegate to Air for
251	/// cloud-based services.
252	#[cfg(feature = "AirIntegration")]
253	pub fn CreateWithAir(
254		ApplicationHandle:AppHandle<Wry>,
255		ApplicationState:Arc<ApplicationState>,
256		AirClient:Option<AirServiceClient<tonic::transport::Channel>>,
257	) -> Self {
258		info!(
259			"[MountainEnvironment] New instance created with Air client: {}",
260			AirClient.is_some()
261		);
262
263		Self { ApplicationHandle, ApplicationState, AirClient }
264	}
265
266	/// Updates the Air client for this environment.
267	/// This allows dynamically switching between Air and local services.
268	#[cfg(feature = "AirIntegration")]
269	pub fn SetAirClient(&mut self, AirClient:Option<AirServiceClient<tonic::transport::Channel>>) {
270		info!("[MountainEnvironment] Air client updated: {}", AirClient.is_some());
271
272		self.AirClient = AirClient;
273	}
274
275	/// Returns whether Air is available and ready.
276	#[cfg(feature = "AirIntegration")]
277	pub async fn IsAirAvailable(&self) -> bool {
278		// TODO: Implement proper health check when AirClient wrapper is available
279		// The raw gRPC client requires &mut self for health_check, but MountainEnvironment
280		// stores an immutable reference. This will be fixed when the AirClient wrapper
281		// is properly integrated.
282		if let Some(_AirClient) = &self.AirClient {
283			// For now, assume Air is available if the client exists
284			info!("[MountainEnvironment] Air client configured (health check disabled pending integration)");
285			true
286		} else {
287			info!("[MountainEnvironment] No Air client configured");
288			false
289		}
290	}
291
292	/// Returns whether Air is available and ready.
293	#[cfg(not(feature = "AirIntegration"))]
294	pub async fn IsAirAvailable(&self) -> bool { false }
295
296	/// Scans a directory for extensions and returns their package.json data
297	async fn ScanExtensionDirectory(&self, path:&std::path::PathBuf) -> Result<Vec<serde_json::Value>, CommonError> {
298		use std::fs;
299
300		let mut extensions = Vec::new();
301
302		// Check if directory exists
303		if !path.exists() || !path.is_dir() {
304			warn!("[ExtensionManagementService] Extension directory does not exist: {:?}", path);
305			return Ok(extensions);
306		}
307
308		// Read directory contents
309		let entries = fs::read_dir(path).map_err(|error| {
310			CommonError::FileSystemIO {
311				Path:path.clone(),
312				Description:format!("Failed to read extension directory: {}", error),
313			}
314		})?;
315
316		for entry in entries {
317			let entry = entry.map_err(|error| {
318				CommonError::FileSystemIO {
319					Path:path.clone(),
320					Description:format!("Failed to read directory entry: {}", error),
321				}
322			})?;
323
324			let entry_path = entry.path();
325			if entry_path.is_dir() {
326				// Look for package.json in the extension directory
327				let package_json_path = entry_path.join("package.json");
328				if package_json_path.exists() {
329					match fs::read_to_string(&package_json_path) {
330						Ok(content) => {
331							match serde_json::from_str::<Value>(&content) {
332								Ok(mut package_json) => {
333									// Add extension location information
334									if let Some(obj) = package_json.as_object_mut() {
335										obj.insert(
336											"ExtensionLocation".to_string(),
337											Value::String(entry_path.to_string_lossy().to_string()),
338										);
339									}
340									extensions.push(package_json);
341									info!("[ExtensionManagementService] Found extension at: {:?}", entry_path);
342								},
343								Err(error) => {
344									warn!(
345										"[ExtensionManagementService] Failed to parse package.json at {:?}: {}",
346										package_json_path, error
347									);
348								},
349							}
350						},
351						Err(error) => {
352							warn!(
353								"[ExtensionManagementService] Failed to read package.json at {:?}: {}",
354								package_json_path, error
355							);
356						},
357					}
358				}
359			}
360		}
361
362		Ok(extensions)
363	}
364}
365
366impl Environment for MountainEnvironment {}
367
368#[async_trait]
369impl ExtensionManagementService for MountainEnvironment {
370	async fn ScanForExtensions(&self) -> Result<(), CommonError> {
371		info!("[ExtensionManagementService] Scanning for extensions...");
372
373		// Get the extension scan paths from ApplicationState
374		let ScanPaths:Vec<std::path::PathBuf> = {
375			let ScanPathsGuard = self
376				.ApplicationState
377				.Extension
378				.Registry
379				.ExtensionScanPaths
380				.lock()
381				.map_err(|Error| CommonError::StateLockPoisoned { Context:Error.to_string() })?;
382			ScanPathsGuard.clone()
383		};
384
385		let mut extensions = Vec::new();
386
387		// Scan each extension directory
388		for path in ScanPaths {
389			if let Ok(mut scan_result) = self.ScanExtensionDirectory(&path).await {
390				extensions.append(&mut scan_result);
391			}
392		}
393
394		// Update ApplicationState with scanned extensions
395		let mut ScannedExtensionsGuard = self
396			.ApplicationState
397			.Extension
398			.ScannedExtensions
399			.ScannedExtensions
400			.lock()
401			.map_err(|Error| CommonError::StateLockPoisoned { Context:Error.to_string() })?;
402
403		ScannedExtensionsGuard.clear();
404
405		for extension in extensions {
406			if let Some(identifier) = extension.get("Identifier").and_then(|v| v.as_str()) {
407				// Convert the extension DTO to ExtensionDescriptionStateDTO
408				let extension_dto = ExtensionDescriptionStateDTO {
409					Identifier:serde_json::Value::String(identifier.to_string()),
410					Name:extension.get("Name").and_then(|v| v.as_str()).unwrap_or("Unknown").to_string(),
411					Version:extension.get("Version").and_then(|v| v.as_str()).unwrap_or("0.0.0").to_string(),
412					Publisher:extension
413						.get("Publisher")
414						.and_then(|v| v.as_str())
415						.unwrap_or("Unknown")
416						.to_string(),
417					Engines:extension.get("Engines").cloned().unwrap_or(serde_json::Value::Null),
418					Main:extension.get("Main").and_then(|v| v.as_str()).map(|s| s.to_string()),
419					Browser:extension.get("Browser").and_then(|v| v.as_str()).map(|s| s.to_string()),
420					ModuleType:extension.get("ModuleType").and_then(|v| v.as_str()).map(|s| s.to_string()),
421					IsBuiltin:extension.get("IsBuiltin").and_then(|v| v.as_bool()).unwrap_or(false),
422					IsUnderDevelopment:extension.get("IsUnderDevelopment").and_then(|v| v.as_bool()).unwrap_or(false),
423					ExtensionLocation:extension.get("ExtensionLocation").cloned().unwrap_or(serde_json::Value::Null),
424					ActivationEvents:extension
425						.get("ActivationEvents")
426						.and_then(|v| v.as_array())
427						.map(|arr| arr.iter().filter_map(|v| v.as_str().map(|s| s.to_string())).collect()),
428					Contributes:extension.get("Contributes").cloned(),
429				};
430
431				ScannedExtensionsGuard.insert(identifier.to_string(), extension_dto);
432			}
433		}
434
435		info!("[ExtensionManagementService] Found {} extensions", ScannedExtensionsGuard.len());
436		Ok(())
437	}
438
439	async fn GetExtensions(&self) -> Result<Vec<Value>, CommonError> {
440		let ScannedExtensionsGuard = self
441			.ApplicationState
442			.Extension
443			.ScannedExtensions
444			.ScannedExtensions
445			.lock()
446			.map_err(|Error| CommonError::StateLockPoisoned { Context:Error.to_string() })?;
447
448		let Extensions:Vec<Value> = ScannedExtensionsGuard
449			.values()
450			.map(|ext| serde_json::to_value(ext).unwrap_or(Value::Null))
451			.collect();
452
453		Ok(Extensions)
454	}
455
456	async fn GetExtension(&self, id:String) -> Result<Option<Value>, CommonError> {
457		let ScannedExtensionsGuard = self
458			.ApplicationState
459			.Extension
460			.ScannedExtensions
461			.ScannedExtensions
462			.lock()
463			.map_err(|Error| CommonError::StateLockPoisoned { Context:Error.to_string() })?;
464
465		if let Some(extension_dto) = ScannedExtensionsGuard.get(&id) {
466			// Convert ExtensionDescriptionStateDTO back to JSON Value
467			let mut extension_value = serde_json::Map::new();
468			extension_value.insert("Identifier".to_string(), extension_dto.Identifier.clone());
469			extension_value.insert("Name".to_string(), Value::String(extension_dto.Name.clone()));
470			extension_value.insert("Version".to_string(), Value::String(extension_dto.Version.clone()));
471			extension_value.insert("Publisher".to_string(), Value::String(extension_dto.Publisher.clone()));
472			extension_value.insert("Engines".to_string(), extension_dto.Engines.clone());
473
474			if let Some(main) = &extension_dto.Main {
475				extension_value.insert("Main".to_string(), Value::String(main.clone()));
476			}
477
478			if let Some(browser) = &extension_dto.Browser {
479				extension_value.insert("Browser".to_string(), Value::String(browser.clone()));
480			}
481
482			if let Some(module_type) = &extension_dto.ModuleType {
483				extension_value.insert("ModuleType".to_string(), Value::String(module_type.clone()));
484			}
485
486			extension_value.insert("IsBuiltin".to_string(), Value::Bool(extension_dto.IsBuiltin));
487			extension_value.insert("IsUnderDevelopment".to_string(), Value::Bool(extension_dto.IsUnderDevelopment));
488			extension_value.insert("ExtensionLocation".to_string(), extension_dto.ExtensionLocation.clone());
489
490			if let Some(activation_events) = &extension_dto.ActivationEvents {
491				let events:Vec<Value> = activation_events.iter().map(|e| Value::String(e.clone())).collect();
492				extension_value.insert("ActivationEvents".to_string(), Value::Array(events));
493			}
494
495			if let Some(contributes) = &extension_dto.Contributes {
496				extension_value.insert("Contributes".to_string(), contributes.clone());
497			}
498
499			Ok(Some(Value::Object(extension_value)))
500		} else {
501			Ok(None)
502		}
503	}
504}
505
506// --- Capability Requirement Implementations (DI) ---
507// All trait implementations are generated using the impl_provider! macro
508
509// Command and Configuration
510impl_provider!(CommandExecutor);
511impl_provider!(ConfigurationProvider);
512impl_provider!(ConfigurationInspector);
513
514// Custom Editor and Debug
515impl_provider!(CustomEditorProvider);
516impl_provider!(DebugService);
517
518// Document and Diagnostic
519impl_provider!(DocumentProvider);
520impl_provider!(DiagnosticManager);
521
522// File System
523impl_provider!(FileSystemReader);
524impl_provider!(FileSystemWriter);
525
526// IPC and Keybinding
527impl_provider!(IPCProvider);
528impl_provider!(KeybindingProvider);
529
530// Language Features and Output
531impl_provider!(LanguageFeatureProviderRegistry);
532impl_provider!(OutputChannelManager);
533
534// Secret and SCM
535impl_provider!(SecretProvider);
536impl_provider!(SourceControlManagementProvider);
537
538// Status Bar and Storage
539impl_provider!(StatusBarProvider);
540impl_provider!(StorageProvider);
541
542// Synchronization and Terminal
543impl_provider!(SynchronizationProvider);
544impl_provider!(TerminalProvider);
545
546// Test and Tree View
547impl_provider!(TestController);
548impl_provider!(TreeViewProvider);
549
550// UI and Webview
551impl_provider!(UserInterfaceProvider);
552impl_provider!(WebviewProvider);
553
554// Workspace
555impl_provider!(WorkspaceProvider);
556impl_provider!(WorkspaceEditApplier);
557
558// Extension Management and Search
559impl_provider!(ExtensionManagementService);
560impl_provider!(SearchProvider);