1use std::{future::Future, pin::Pin, sync::Arc};
45
46use CommonLibrary::{
47 Command::CommandExecutor::CommandExecutor,
48 Error::CommonError::CommonError,
49 IPC::DTO::ProxyTarget::ProxyTarget,
50};
51use async_trait::async_trait;
52use serde_json::{Value, json};
53use tauri::{AppHandle, Manager, Runtime, WebviewWindow};
54use ::Vine::Client;
55
56use super::MountainEnvironment::MountainEnvironment;
57use crate::{RunTime::ApplicationRunTime::ApplicationRunTime, dev_log};
58
59pub enum CommandHandler<R:Runtime + 'static> {
64 Native(
66 fn(
67 AppHandle<R>,
68
69 WebviewWindow<R>,
70
71 Arc<ApplicationRunTime>,
72
73 Value,
74 ) -> Pin<Box<dyn Future<Output = Result<Value, String>> + Send>>,
75 ),
76
77 Proxied { SideCarIdentifier:String, CommandIdentifier:String },
79}
80
81impl<R:Runtime> Clone for CommandHandler<R> {
82 fn clone(&self) -> Self {
83 match self {
84 Self::Native(Function) => Self::Native(*Function),
85
86 Self::Proxied { SideCarIdentifier, CommandIdentifier } => {
87 Self::Proxied {
88 SideCarIdentifier:SideCarIdentifier.clone(),
89
90 CommandIdentifier:CommandIdentifier.clone(),
91 }
92 },
93 }
94 }
95}
96
97#[async_trait]
98impl CommandExecutor for MountainEnvironment {
99 async fn ExecuteCommand(&self, CommandIdentifier:String, Argument:Value) -> Result<Value, CommonError> {
102 let HandlerInfoOption = self
103 .ApplicationState
104 .Extension
105 .Registry
106 .CommandRegistry
107 .lock()
108 .map_err(super::Utility::ErrorMapping::MapApplicationStateLockErrorToCommonError)?
109 .get(&CommandIdentifier)
110 .cloned();
111
112 match HandlerInfoOption {
113 Some(CommandHandler::Native(Function)) => {
114 dev_log!(
120 "commands-verbose",
121 "[CommandProvider] Executing NATIVE command '{}'.",
122 CommandIdentifier
123 );
124
125 let RunTime:Arc<ApplicationRunTime> =
126 self.ApplicationHandle.state::<Arc<ApplicationRunTime>>().inner().clone();
127
128 let MainWindow = self.ApplicationHandle.get_webview_window("main").ok_or_else(|| {
129 CommonError::UserInterfaceInteraction {
130 Reason:"Main window not found for command execution".into(),
131 }
132 })?;
133
134 Function(self.ApplicationHandle.clone(), MainWindow, RunTime, Argument)
135 .await
136 .map_err(|Error| CommonError::CommandExecution { CommandIdentifier, Reason:Error })
137 },
138
139 Some(CommandHandler::Proxied { SideCarIdentifier, CommandIdentifier: ProxiedCommandIdentifier }) => {
140 dev_log!(
141 "commands-verbose",
142 "[CommandProvider] Executing PROXIED command '{}' on sidecar '{}'.",
143 CommandIdentifier,
144 SideCarIdentifier
145 );
146
147 let RPCParameters = json!([ProxiedCommandIdentifier, Argument]);
148
149 let RPCMethod = format!("{}$ExecuteContributedCommand", ProxyTarget::ExtHostCommands.GetTargetPrefix());
150
151 Client::SendRequest::Fn(&SideCarIdentifier, RPCMethod, RPCParameters, 30000)
152 .await
153 .map_err(|Error| CommonError::IPCError { Description:Error.to_string() })
154 },
155
156 None => {
157 if CommandIdentifier.ends_with(".focus")
168 || CommandIdentifier.ends_with(".resetViewLocation")
169 || CommandIdentifier.ends_with(".removeView")
170 {
171 crate::IPC::DevLog::DebugOnce::Fn(
178 "commands",
179 &format!("view-action-noop:{}", CommandIdentifier),
180 &format!(
181 "[CommandProvider] View-action command '{}' not registered; treating as no-op \
182 (auto-generated by view registry in stock VS Code).",
183 CommandIdentifier
184 ),
185 );
186
187 return Ok(Value::Null);
188 }
189
190 if matches!(
209 CommandIdentifier.as_str(),
210 "getTelemetrySenderObject" | "testing.clearTestResults"
211 ) {
212 crate::IPC::DevLog::DebugOnce::Fn(
217 "commands",
218 &format!("workbench-internal-noop:{}", CommandIdentifier),
219 &format!(
220 "[CommandProvider] Workbench-internal command '{}' not registered; treating as no-op \
221 (Land has no backing service).",
222 CommandIdentifier
223 ),
224 );
225
226 return Ok(Value::Null);
227 }
228
229 if CommandIdentifier.starts_with("_typescript.")
247 || CommandIdentifier.starts_with("_extensionHost.")
248 || CommandIdentifier.starts_with("_workbench.registerWebview")
249 || CommandIdentifier.ends_with(".activationCompleted")
250 || CommandIdentifier.ends_with(".activated")
251 || CommandIdentifier.ends_with(".ready")
252 {
253 dev_log!(
254 "commands",
255 "[CommandProvider] Activation-race command '{}' not yet in registry; returning null \
256 (extension will retry post-activation).",
257 CommandIdentifier
258 );
259
260 return Ok(Value::Null);
261 }
262
263 if LookupCommandContributingExtension(self, &CommandIdentifier) {
274 dev_log!(
275 "commands",
276 "[CommandProvider] Lazy activation for command '{}' - firing onCommand:{0}",
277 CommandIdentifier
278 );
279
280 let Event = format!("onCommand:{}", CommandIdentifier);
281
282 let ActivationResult = Client::SendRequest::Fn(
283 &"cocoon-main".to_string(),
284 "$activateByEvent".to_string(),
285 json!({ "activationEvent": Event }),
286 30_000,
287 )
288 .await;
289
290 if let Err(Error) = ActivationResult {
291 dev_log!(
292 "commands",
293 "warn: [CommandProvider] onCommand:{} activation failed: {}",
294 CommandIdentifier,
295 Error
296 );
297 }
298
299 tokio::time::sleep(std::time::Duration::from_millis(20)).await;
303
304 let PostActivationHandler = self
305 .ApplicationState
306 .Extension
307 .Registry
308 .CommandRegistry
309 .lock()
310 .map_err(super::Utility::ErrorMapping::MapApplicationStateLockErrorToCommonError)?
311 .get(&CommandIdentifier)
312 .cloned();
313
314 if let Some(Handler) = PostActivationHandler {
315 match Handler {
316 CommandHandler::Native(Function) => {
317 let MainWindow =
318 self.ApplicationHandle.get_webview_window("main").ok_or_else(|| {
319 CommonError::IPCError {
320 Description:"Could not find main window for lazy-activated native command"
321 .to_string(),
322 }
323 })?;
324
325 let RunTime =
326 self.ApplicationHandle.try_state::<Arc<ApplicationRunTime>>().ok_or_else(|| {
327 CommonError::IPCError {
328 Description:"ApplicationRunTime unavailable for lazy-activated native \
329 command"
330 .to_string(),
331 }
332 })?;
333
334 return Function(
335 self.ApplicationHandle.clone(),
336 MainWindow,
337 (*RunTime).clone(),
338 Argument,
339 )
340 .await
341 .map_err(|Error| CommonError::CommandExecution { CommandIdentifier, Reason:Error });
342 },
343
344 CommandHandler::Proxied { SideCarIdentifier, CommandIdentifier: ProxiedId } => {
345 let RPCParameters = json!([ProxiedId, Argument]);
346
347 let RPCMethod = format!(
348 "{}$ExecuteContributedCommand",
349 ProxyTarget::ExtHostCommands.GetTargetPrefix()
350 );
351
352 return Client::SendRequest::Fn(&SideCarIdentifier, RPCMethod, RPCParameters, 30_000)
353 .await
354 .map_err(|Error| CommonError::IPCError { Description:Error.to_string() });
355 },
356 }
357 }
358 }
359
360 dev_log!(
361 "commands",
362 "error: [CommandProvider] Command '{}' not found in registry.",
363 CommandIdentifier
364 );
365
366 Err(CommonError::CommandNotFound { Identifier:CommandIdentifier })
367 },
368 }
369 }
370
371 async fn RegisterCommand(&self, SideCarIdentifier:String, CommandIdentifier:String) -> Result<(), CommonError> {
373 dev_log!(
374 "commands",
375 "[CommandProvider] Registering PROXY command '{}' from sidecar '{}'",
376 CommandIdentifier,
377 SideCarIdentifier
378 );
379
380 let mut Registry = self
381 .ApplicationState
382 .Extension
383 .Registry
384 .CommandRegistry
385 .lock()
386 .map_err(super::Utility::ErrorMapping::MapApplicationStateLockErrorToCommonError)?;
387
388 Registry.insert(
389 CommandIdentifier.clone(),
390 CommandHandler::Proxied { SideCarIdentifier, CommandIdentifier },
391 );
392
393 Ok(())
394 }
395
396 async fn UnregisterCommand(&self, _SideCarIdentifier:String, CommandIdentifier:String) -> Result<(), CommonError> {
398 dev_log!("commands", "[CommandProvider] Unregistering command '{}'", CommandIdentifier);
399
400 self.ApplicationState
401 .Extension
402 .Registry
403 .CommandRegistry
404 .lock()
405 .map_err(super::Utility::ErrorMapping::MapApplicationStateLockErrorToCommonError)?
406 .remove(&CommandIdentifier);
407
408 Ok(())
409 }
410
411 async fn GetAllCommands(&self) -> Result<Vec<String>, CommonError> {
413 dev_log!("commands", "[CommandProvider] Getting all command identifiers.");
414
415 let Registry = self
416 .ApplicationState
417 .Extension
418 .Registry
419 .CommandRegistry
420 .lock()
421 .map_err(super::Utility::ErrorMapping::MapApplicationStateLockErrorToCommonError)?;
422
423 Ok(Registry.keys().cloned().collect())
424 }
425}
426
427fn LookupCommandContributingExtension(Environment:&MountainEnvironment, CommandIdentifier:&str) -> bool {
434 let Event = format!("onCommand:{}", CommandIdentifier);
435
436 let Guard = match Environment
437 .ApplicationState
438 .Extension
439 .ScannedExtensions
440 .ScannedExtensions
441 .lock()
442 {
443 Ok(G) => G,
444
445 Err(_) => return false,
446 };
447
448 for Description in Guard.values() {
449 if let Some(Events) = &Description.ActivationEvents {
450 if Events.iter().any(|E| E == &Event) {
451 return true;
452 }
453 }
454 }
455
456 false
457}