Mountain/Environment/
DebugProvider.rs

1//! # DebugProvider (Environment)
2//!
3//! RESPONSIBILITIES:
4//! - Implements [`DebugService`](CommonLibrary::Debug::DebugService) for
5//!   [`MountainEnvironment`]
6//! - Manages complete debugging session lifecycle from configuration to
7//!   termination
8//! - Orchestrates between extension host (Cocoon), debug adapter, and UI
9//! - Handles DAP (Debug Adapter Protocol) message mediation
10//!
11//! ARCHITECTURAL ROLE:
12//! - Core provider for debugging functionality, analogous to VSCode's debug
13//!   service
14//! - Uses two-stage registration: configuration providers and adapter
15//!   descriptor factories
16//! - Each debug type (node, java, rust) can have its own configuration and
17//!   adapter
18//! - Integrates with [`IPCProvider`](CommonLibrary::IPC::IPCProvider) for RPC
19//!   to Cocoon
20//!
21//! DEBUG SESSION FLOW:
22//! 1. UI calls `StartDebugging` with folder URI and configuration
23//! 2. Mountain RPCs to Cocoon to resolve debug configuration (variable
24//!    substitution)
25//! 3. Mountain RPCs to Cocoon to create debug adapter descriptor
26//! 4. Mountain spawns debug adapter process or connects to TCP server
27//! 5. Mountain mediates DAP messages between UI and debug adapter
28//! 6. UI sends DAP commands via `SendCommand` which forwards to adapter
29//! 7. Debug adapter sends DAP events/notifications back through Mountain to UI
30//! 8. Session ends on stop request or adapter process exit
31//!
32//! ERROR HANDLING:
33//! - Uses [`CommonError`](CommonLibrary::Error::CommonError) for all operations
34//! - Validates debug type is non-empty (InvalidArgument error)
35//! - TODO: Implement proper session lookup, timeout handling, and error
36//!   recovery
37//!
38//! PERFORMANCE:
39//! - Debug adapter spawning should be async with timeout protection (5000ms in
40//!   current RPC)
41//! - DAP message routing needs efficient session lookup (TODO: O(1) hash map)
42//! - Multiple simultaneous debug sessions require careful resource management
43//!
44//! VS CODE REFERENCE:
45//! - `vs/workbench/contrib/debug/browser/debugService.ts` - debug service main
46//!   logic
47//! - `vs/workbench/contrib/debug/common/debug.ts` - debug interfaces and models
48//! - `vs/workbench/contrib/debug/browser/adapter/descriptorFactory.ts` -
49//!   adapter descriptor factories
50//! - `vs/debugAdapter/common/debugProtocol.ts` - DAP protocol specification
51//!
52//! TODO:
53//! - Store debug adapter registrations in ApplicationState
54//! - Implement proper debug session tracking and management
55//! - Add debug adapter process spawning and lifecycle management
56//! - Implement proper DAP message routing and serialization
57//! - Add debug session state persistence across UI reloads
58//! - Implement debug console and variable inspection integration
59//! - Add support for multiple simultaneous debug sessions
60//! - Implement debug adapter termination and cleanup
61//! - Add debug session metrics and telemetry
62//! - Consider implementing debug configuration validation
63//! - Add support for debug adapters that communicate via TCP sockets
64//! - Implement debug adapter crash detection and recovery
65//!
66//! MODULE CONTENTS:
67//! - [`DebugService`](CommonLibrary::Debug::DebugService) implementation:
68//! - `RegisterDebugConfigurationProvider` - register config resolver
69//! - `RegisterDebugAdapterDescriptorFactory` - register adapter factory
70//! - `StartDebugging` - start debug session (partial)
71//! - `SendCommand` - send DAP command to adapter (stub)
72
73use std::sync::Arc;
74
75use CommonLibrary::{
76	Debug::DebugService::DebugService,
77	Environment::Requires::Requires,
78	Error::CommonError::CommonError,
79	IPC::{DTO::ProxyTarget::ProxyTarget, IPCProvider::IPCProvider},
80};
81use async_trait::async_trait;
82use log::{info, warn};
83use serde_json::{Value, json};
84use url::Url;
85
86use super::MountainEnvironment::MountainEnvironment;
87
88#[async_trait]
89impl DebugService for MountainEnvironment {
90	async fn RegisterDebugConfigurationProvider(
91		&self,
92
93		DebugType:String,
94
95		ProviderHandle:u32,
96
97		SideCarIdentifier:String,
98	) -> Result<(), CommonError> {
99		// Validate debug type is non-empty
100		if DebugType.is_empty() {
101			return Err(CommonError::InvalidArgument {
102				ArgumentName:"DebugType".to_string(),
103				Reason:"DebugType cannot be empty".to_string(),
104			});
105		}
106
107		info!(
108			"[DebugProvider] Registering DebugConfigurationProvider for type '{}' (handle: {}, sidecar: {})",
109			DebugType, ProviderHandle, SideCarIdentifier
110		);
111
112		// TODO: Store debug configuration provider registration in ApplicationState
113		// to enable debug adapter resolution and lifecycle management. Should:
114		// - Map debug_type to (provider_handle, sidecar_identifier) tuple
115		// - Support multiple providers per debug type with priority ordering
116		// - Validate that debug type is not already registered to prevent conflicts
117		// - Track registration source (extension ID) for debugging and cleanup
118
119		Ok(())
120	}
121
122	async fn RegisterDebugAdapterDescriptorFactory(
123		&self,
124
125		DebugType:String,
126
127		FactoryHandle:u32,
128
129		SideCarIdentifier:String,
130	) -> Result<(), CommonError> {
131		// Validate debug type is non-empty
132		if DebugType.is_empty() {
133			return Err(CommonError::InvalidArgument {
134				ArgumentName:"DebugType".to_string(),
135				Reason:"DebugType cannot be empty".to_string(),
136			});
137		}
138
139		info!(
140			"[DebugProvider] Registering DebugAdapterDescriptorFactory for type '{}' (handle: {}, sidecar: {})",
141			DebugType, FactoryHandle, SideCarIdentifier
142		);
143
144		// TODO: Store debug adapter descriptor factory registration in ApplicationState
145		// for use during debug session creation. Should:
146		// - Map debug_type to (factory_handle, sidecar_identifier)
147		// - Support multiple adapter factories with fallback chain for resilience
148		// - Allow factory override based on debug configuration version
149		// - Enable runtime lookup of appropriate factory for requested debug type
150
151		Ok(())
152	}
153
154	async fn StartDebugging(&self, _FolderURI:Option<Url>, Configuration:Value) -> Result<String, CommonError> {
155		let SessionID = uuid::Uuid::new_v4().to_string();
156		info!(
157			"[DebugProvider] Starting debug session '{}' with config: {:?}",
158			SessionID, Configuration
159		);
160
161		let IPCProvider:Arc<dyn IPCProvider> = self.Require();
162		let DebugType = Configuration
163			.get("type")
164			.and_then(Value::as_str)
165			.ok_or_else(|| {
166				CommonError::InvalidArgument {
167					ArgumentName:"Configuration".into(),
168
169					Reason:"Missing 'type' field in debug configuration.".into(),
170				}
171			})?
172			.to_string();
173
174		// TODO: Look up which sidecar (extension) handles this debug type using
175		// the registration stored in ApplicationState. The mapping should be based
176		// on previous RegisterDebugConfigurationProvider calls. Initial stub uses
177		// hardcoded "cocoon-main" until proper registration tracking is implemented.
178		let TargetSideCar = "cocoon-main".to_string();
179
180		// 1. Resolve configuration (Reverse-RPC to Cocoon)
181		info!("[DebugProvider] Resolving debug configuration for type '{}'", DebugType);
182		info!("[DebugProvider] Resolving debug configuration...");
183		let ResolveConfigMethod = format!("{}$resolveDebugConfiguration", ProxyTarget::ExtHostDebug.GetTargetPrefix());
184		let ResolvedConfig = IPCProvider
185			.SendRequestToSideCar(
186				TargetSideCar.clone(),
187				ResolveConfigMethod,
188				json!([DebugType.clone(), Configuration]),
189				5000,
190			)
191			.await?;
192
193		// 2. Get the Debug Adapter Descriptor (Reverse-RPC to Cocoon)
194		info!("[DebugProvider] Creating debug adapter descriptor...");
195		let CreateDescriptorMethod =
196			format!("{}$createDebugAdapterDescriptor", ProxyTarget::ExtHostDebug.GetTargetPrefix());
197		let Descriptor = IPCProvider
198			.SendRequestToSideCar(
199				TargetSideCar.clone(),
200				CreateDescriptorMethod,
201				json!([DebugType, &ResolvedConfig]),
202				5000,
203			)
204			.await?;
205
206		// 3. Spawn the Debug Adapter process based on the descriptor.
207		info!("[DebugProvider] Spawning Debug Adapter based on descriptor: {:?}", Descriptor);
208
209		// TODO: Implement full debug adapter spawning based on the descriptor.
210		// A complete implementation would:
211		// - Parse the DebugAdapterDescriptor (executable path, command args,
212		//   environment variables, or server port for TCP connection)
213		// - Spawn a new OS process with stdio pipes using Command or connect to a TCP
214		//   socket if using debug adapter server mode
215		// - Create a new DebugSession struct to manage the DAP (Debug Adapter Protocol)
216		//   communication stream, handling JSON-RPC message framing
217		// - Establish bidirectional JSON-RPC communication with the debug adapter
218		// - Store the active session in ApplicationState keyed by session_id for later
219		//   command routing and session management
220		// - Implement proper session cleanup on termination (kill process, close
221		//   sockets, remove from ApplicationState, emit exit events)
222		// - Handle adapter launch failures with descriptive error messages and proper
223		//   session state cleanup
224
225		info!("[DebugProvider] Debug session '{}' started (simulation).", SessionID);
226		Ok(SessionID)
227	}
228
229	async fn SendCommand(&self, SessionID:String, Command:String, Arguments:Value) -> Result<Value, CommonError> {
230		info!(
231			"[DebugProvider] SendCommand for session '{}' (command: '{}', args: {:?})",
232			SessionID, Command, Arguments
233		);
234
235		// TODO: Implement proper debug session management to route commands to
236		// active debug adapters. Should:
237		// - Look up session by SessionID in ApplicationState's debug session registry
238		// - Validate session exists and is in active state (not terminated or crashed)
239		// - Serialize command and arguments to JSON-RPC 2.0 format with proper request
240		//   sequencing (seq number)
241		// - Send the request to debug adapter via stdio pipes or TCP socket
242		// - Wait for response with appropriate timeout, handle cancellation requests
243		// - Deserialize JSON-RPC response and return the result body to the caller
244		// - Handle timeouts, adapter crashes, and protocol errors gracefully with
245		//   informative error messages and session cleanup as needed
246
247		// For now, return a placeholder response indicating debug session is active
248		let response = serde_json::json!({
249			"success": true,
250			"session_id": SessionID,
251			"command": Command,
252			"response": {
253				"type": "response",
254				"request_seq": 1,
255				"success": true,
256				"command": Command,
257				"body": {}
258			}
259		});
260
261		Ok(response)
262	}
263}