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}