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::{
190		FileSystemReader::FileSystemReader,
191		FileSystemWriter::FileSystemWriter,
192		FileWatcherProvider::FileWatcherProvider,
193	},
194	IPC::IPCProvider::IPCProvider,
195	Keybinding::KeybindingProvider::KeybindingProvider,
196	LanguageFeature::LanguageFeatureProviderRegistry::LanguageFeatureProviderRegistry,
197	Output::OutputChannelManager::OutputChannelManager,
198	Search::SearchProvider::SearchProvider,
199	Secret::SecretProvider::SecretProvider,
200	SourceControlManagement::SourceControlManagementProvider::SourceControlManagementProvider,
201	StatusBar::StatusBarProvider::StatusBarProvider,
202	Storage::StorageProvider::StorageProvider,
203	Synchronization::SynchronizationProvider::SynchronizationProvider,
204	Terminal::TerminalProvider::TerminalProvider,
205	Testing::TestController::TestController,
206	TreeView::TreeViewProvider::TreeViewProvider,
207	UserInterface::UserInterfaceProvider::UserInterfaceProvider,
208	Webview::WebviewProvider::WebviewProvider,
209	Workspace::{WorkspaceEditApplier::WorkspaceEditApplier, WorkspaceProvider::WorkspaceProvider},
210};
211use async_trait::async_trait;
212use serde_json::Value;
213use tauri::{AppHandle, Wry};
214
215use crate::{
216	ApplicationState::{
217		DTO::ExtensionDescriptionStateDTO::ExtensionDescriptionStateDTO,
218		State::ApplicationState::ApplicationState,
219	},
220	dev_log,
221};
222// Import the macro for generating trait implementations
223// Note: Macros annotated with #[macro_export] are available at crate root
224use crate::impl_provider;
225
226/// The concrete `Environment` for the Mountain application.
227#[derive(Clone)]
228pub struct MountainEnvironment {
229	pub ApplicationHandle:AppHandle<Wry>,
230
231	pub ApplicationState:Arc<ApplicationState>,
232
233	/// Optional Air client for cloud-based services.
234	/// When provided, providers like SecretProvider and UpdateService can
235	/// delegate to Air.
236	#[cfg(feature = "AirIntegration")]
237	pub AirClient:Option<AirServiceClient<tonic::transport::Channel>>,
238}
239
240impl MountainEnvironment {
241	/// Creates a new `MountainEnvironment` instance.
242	#[allow(unused_mut)]
243	pub fn Create(ApplicationHandle:AppHandle<Wry>, ApplicationState:Arc<ApplicationState>) -> Self {
244		dev_log!("lifecycle", "[MountainEnvironment] New instance created.");
245
246		#[cfg(feature = "AirIntegration")]
247		{
248			Self { ApplicationHandle, ApplicationState, AirClient:None }
249		}
250
251		#[cfg(not(feature = "AirIntegration"))]
252		{
253			Self { ApplicationHandle, ApplicationState }
254		}
255	}
256
257	/// Creates a new `MountainEnvironment` instance with an optional Air
258	/// client. When AirClient is provided, providers can delegate to Air for
259	/// cloud-based services.
260	#[cfg(feature = "AirIntegration")]
261	pub fn CreateWithAir(
262		ApplicationHandle:AppHandle<Wry>,
263		ApplicationState:Arc<ApplicationState>,
264		AirClient:Option<AirServiceClient<tonic::transport::Channel>>,
265	) -> Self {
266		dev_log!(
267			"lifecycle",
268			"[MountainEnvironment] New instance created with Air client: {}",
269			AirClient.is_some()
270		);
271
272		Self { ApplicationHandle, ApplicationState, AirClient }
273	}
274
275	/// Updates the Air client for this environment.
276	/// This allows dynamically switching between Air and local services.
277	#[cfg(feature = "AirIntegration")]
278	pub fn SetAirClient(&mut self, AirClient:Option<AirServiceClient<tonic::transport::Channel>>) {
279		dev_log!("lifecycle", "[MountainEnvironment] Air client updated: {}", AirClient.is_some());
280
281		self.AirClient = AirClient;
282	}
283
284	/// Returns whether Air is available and ready.
285	#[cfg(feature = "AirIntegration")]
286	pub async fn IsAirAvailable(&self) -> bool {
287		// TODO: Implement proper health check when AirClient wrapper is available
288		// The raw gRPC client requires &mut self for health_check, but
289		// MountainEnvironment stores an immutable reference. This will be fixed when
290		// the AirClient wrapper is properly integrated.
291		if let Some(_AirClient) = &self.AirClient {
292			// For now, assume Air is available if the client exists
293			dev_log!(
294				"lifecycle",
295				"[MountainEnvironment] Air client configured (health check disabled pending integration)"
296			);
297			true
298		} else {
299			dev_log!("lifecycle", "[MountainEnvironment] No Air client configured");
300			false
301		}
302	}
303
304	/// Returns whether Air is available and ready.
305	#[cfg(not(feature = "AirIntegration"))]
306	pub async fn IsAirAvailable(&self) -> bool { false }
307
308	/// Scans a directory for extensions and returns their package.json data
309	async fn ScanExtensionDirectory(&self, path:&std::path::PathBuf) -> Result<Vec<serde_json::Value>, CommonError> {
310		use std::fs;
311
312		let mut extensions = Vec::new();
313
314		// Check if directory exists
315		if !path.exists() || !path.is_dir() {
316			dev_log!(
317				"lifecycle",
318				"warn: [ExtensionManagementService] Extension directory does not exist: {:?}",
319				path
320			);
321			return Ok(extensions);
322		}
323
324		// Read directory contents
325		let entries = fs::read_dir(path).map_err(|error| {
326			CommonError::FileSystemIO {
327				Path:path.clone(),
328				Description:format!("Failed to read extension directory: {}", error),
329			}
330		})?;
331
332		for entry in entries {
333			let entry = entry.map_err(|error| {
334				CommonError::FileSystemIO {
335					Path:path.clone(),
336					Description:format!("Failed to read directory entry: {}", error),
337				}
338			})?;
339
340			let entry_path = entry.path();
341			if entry_path.is_dir() {
342				// Look for package.json in the extension directory
343				let package_json_path = entry_path.join("package.json");
344				if package_json_path.exists() {
345					match fs::read_to_string(&package_json_path) {
346						Ok(content) => {
347							match serde_json::from_str::<Value>(&content) {
348								Ok(mut package_json) => {
349									// Add extension location information
350									if let Some(obj) = package_json.as_object_mut() {
351										obj.insert(
352											"ExtensionLocation".to_string(),
353											Value::String(entry_path.to_string_lossy().to_string()),
354										);
355									}
356									extensions.push(package_json);
357									dev_log!(
358										"lifecycle",
359										"[ExtensionManagementService] Found extension at: {:?}",
360										entry_path
361									);
362								},
363								Err(error) => {
364									dev_log!(
365										"lifecycle",
366										"warn: [ExtensionManagementService] Failed to parse package.json at {:?}: {}",
367										package_json_path,
368										error
369									);
370								},
371							}
372						},
373						Err(error) => {
374							dev_log!(
375								"lifecycle",
376								"warn: [ExtensionManagementService] Failed to read package.json at {:?}: {}",
377								package_json_path,
378								error
379							);
380						},
381					}
382				}
383			}
384		}
385
386		Ok(extensions)
387	}
388}
389
390impl Environment for MountainEnvironment {}
391
392#[async_trait]
393impl ExtensionManagementService for MountainEnvironment {
394	async fn ScanForExtensions(&self) -> Result<(), CommonError> {
395		dev_log!("lifecycle", "[ExtensionManagementService] Scanning for extensions...");
396
397		// Get the extension scan paths from ApplicationState
398		let ScanPaths:Vec<std::path::PathBuf> = {
399			let ScanPathsGuard = self
400				.ApplicationState
401				.Extension
402				.Registry
403				.ExtensionScanPaths
404				.lock()
405				.map_err(|Error| CommonError::StateLockPoisoned { Context:Error.to_string() })?;
406			ScanPathsGuard.clone()
407		};
408
409		let mut extensions = Vec::new();
410
411		// Scan each extension directory
412		for path in ScanPaths {
413			if let Ok(mut scan_result) = self.ScanExtensionDirectory(&path).await {
414				extensions.append(&mut scan_result);
415			}
416		}
417
418		// Update ApplicationState with scanned extensions
419		let mut ScannedExtensionsGuard = self
420			.ApplicationState
421			.Extension
422			.ScannedExtensions
423			.ScannedExtensions
424			.lock()
425			.map_err(|Error| CommonError::StateLockPoisoned { Context:Error.to_string() })?;
426
427		ScannedExtensionsGuard.clear();
428
429		for extension in extensions {
430			// The scanner returns camelCase JSON (serde rename_all = "camelCase").
431			// Deserialize directly into ExtensionDescriptionStateDTO.
432			match serde_json::from_value::<ExtensionDescriptionStateDTO>(extension.clone()) {
433				Ok(Dto) => {
434					// Use identifier.value or fall back to name
435					let Key = Dto
436						.Identifier
437						.as_object()
438						.and_then(|O| O.get("value"))
439						.and_then(|V| V.as_str())
440						.unwrap_or(&Dto.Name)
441						.to_string();
442					if !Key.is_empty() {
443						ScannedExtensionsGuard.insert(Key, Dto);
444					}
445				},
446				Err(Error) => {
447					let Name = extension.get("name").and_then(|V| V.as_str()).unwrap_or("?");
448					dev_log!(
449						"lifecycle",
450						"warn: [ExtensionManagementService] Failed to parse extension '{}': {}",
451						Name,
452						Error
453					);
454				},
455			}
456		}
457
458		dev_log!(
459			"lifecycle",
460			"[ExtensionManagementService] Found {} extensions",
461			ScannedExtensionsGuard.len()
462		);
463		Ok(())
464	}
465
466	async fn GetExtensions(&self) -> Result<Vec<Value>, CommonError> {
467		let ScannedExtensionsGuard = self
468			.ApplicationState
469			.Extension
470			.ScannedExtensions
471			.ScannedExtensions
472			.lock()
473			.map_err(|Error| CommonError::StateLockPoisoned { Context:Error.to_string() })?;
474
475		let GuardLen = ScannedExtensionsGuard.len();
476
477		let Extensions:Vec<Value> = ScannedExtensionsGuard
478			.values()
479			.map(|ext| serde_json::to_value(ext).unwrap_or(Value::Null))
480			.collect();
481
482		let SerializedCount = Extensions.iter().filter(|v| !v.is_null()).count();
483
484		dev_log!(
485			"lifecycle",
486			"[MountainEnvironment] GetExtensions: ScannedExtensions map={} entries, serialized={} non-null",
487			GuardLen,
488			SerializedCount
489		);
490
491		Ok(Extensions)
492	}
493
494	async fn GetExtension(&self, id:String) -> Result<Option<Value>, CommonError> {
495		let ScannedExtensionsGuard = self
496			.ApplicationState
497			.Extension
498			.ScannedExtensions
499			.ScannedExtensions
500			.lock()
501			.map_err(|Error| CommonError::StateLockPoisoned { Context:Error.to_string() })?;
502
503		if let Some(extension_dto) = ScannedExtensionsGuard.get(&id) {
504			// Convert ExtensionDescriptionStateDTO back to JSON Value
505			let mut extension_value = serde_json::Map::new();
506			extension_value.insert("Identifier".to_string(), extension_dto.Identifier.clone());
507			extension_value.insert("Name".to_string(), Value::String(extension_dto.Name.clone()));
508			extension_value.insert("Version".to_string(), Value::String(extension_dto.Version.clone()));
509			extension_value.insert("Publisher".to_string(), Value::String(extension_dto.Publisher.clone()));
510			extension_value.insert("Engines".to_string(), extension_dto.Engines.clone());
511
512			if let Some(main) = &extension_dto.Main {
513				extension_value.insert("Main".to_string(), Value::String(main.clone()));
514			}
515
516			if let Some(browser) = &extension_dto.Browser {
517				extension_value.insert("Browser".to_string(), Value::String(browser.clone()));
518			}
519
520			if let Some(module_type) = &extension_dto.ModuleType {
521				extension_value.insert("ModuleType".to_string(), Value::String(module_type.clone()));
522			}
523
524			extension_value.insert("IsBuiltin".to_string(), Value::Bool(extension_dto.IsBuiltin));
525			extension_value.insert("IsUnderDevelopment".to_string(), Value::Bool(extension_dto.IsUnderDevelopment));
526			extension_value.insert("ExtensionLocation".to_string(), extension_dto.ExtensionLocation.clone());
527
528			if let Some(activation_events) = &extension_dto.ActivationEvents {
529				let events:Vec<Value> = activation_events.iter().map(|e| Value::String(e.clone())).collect();
530				extension_value.insert("ActivationEvents".to_string(), Value::Array(events));
531			}
532
533			if let Some(contributes) = &extension_dto.Contributes {
534				extension_value.insert("Contributes".to_string(), contributes.clone());
535			}
536
537			Ok(Some(Value::Object(extension_value)))
538		} else {
539			Ok(None)
540		}
541	}
542}
543
544// --- Capability Requirement Implementations (DI) ---
545// All trait implementations are generated using the impl_provider! macro
546
547// Command and Configuration
548impl_provider!(CommandExecutor);
549impl_provider!(ConfigurationProvider);
550impl_provider!(ConfigurationInspector);
551
552// Custom Editor and Debug
553impl_provider!(CustomEditorProvider);
554impl_provider!(DebugService);
555
556// Document and Diagnostic
557impl_provider!(DocumentProvider);
558impl_provider!(DiagnosticManager);
559
560// File System
561impl_provider!(FileSystemReader);
562impl_provider!(FileSystemWriter);
563impl_provider!(FileWatcherProvider);
564
565// IPC and Keybinding
566impl_provider!(IPCProvider);
567impl_provider!(KeybindingProvider);
568
569// Language Features and Output
570impl_provider!(LanguageFeatureProviderRegistry);
571impl_provider!(OutputChannelManager);
572
573// Secret and SCM
574impl_provider!(SecretProvider);
575impl_provider!(SourceControlManagementProvider);
576
577// Status Bar and Storage
578impl_provider!(StatusBarProvider);
579impl_provider!(StorageProvider);
580
581// Synchronization and Terminal
582impl_provider!(SynchronizationProvider);
583impl_provider!(TerminalProvider);
584
585// Test and Tree View
586impl_provider!(TestController);
587impl_provider!(TreeViewProvider);
588
589// UI and Webview
590impl_provider!(UserInterfaceProvider);
591impl_provider!(WebviewProvider);
592
593// Workspace
594impl_provider!(WorkspaceProvider);
595impl_provider!(WorkspaceEditApplier);
596
597// Extension Management and Search
598impl_provider!(ExtensionManagementService);
599impl_provider!(SearchProvider);