Mountain/Environment/
CommandProvider.rs

1// File: Mountain/Source/Environment/CommandProvider.rs
2// Role: Implements the `CommandExecutor` trait for the `MountainEnvironment`.
3// Responsibilities:
4//   - Contains the core logic for managing the command registry.
5//   - Dispatches command execution to either native Rust handlers or proxied
6//     sidecar handlers.
7
8//! # CommandProvider Implementation
9//!
10//! Implements the `CommandExecutor` trait for the `MountainEnvironment`. This
11//! provider contains the core logic for managing the command registry and
12//! dispatching command execution to either native Rust handlers or proxied
13//! sidecar handlers.
14
15#![allow(non_snake_case, non_camel_case_types)]
16
17use std::{future::Future, pin::Pin, sync::Arc};
18
19use Common::{
20	Command::CommandExecutor::CommandExecutor,
21	Error::CommonError::CommonError,
22	IPC::DTO::ProxyTarget::ProxyTarget,
23};
24use async_trait::async_trait;
25use log::{debug, error, info};
26use serde_json::{Value, json};
27use tauri::{AppHandle, Manager, Runtime, WebviewWindow};
28
29use super::MountainEnvironment::MountainEnvironment;
30use crate::{RunTime::ApplicationRunTime::ApplicationRunTime, Vine::Client};
31
32/// An enum representing the different ways a command can be handled.
33pub enum CommandHandler<R:Runtime + 'static> {
34	/// A command handled by a native, asynchronous Rust function.
35	Native(
36		fn(
37			AppHandle<R>,
38
39			WebviewWindow<R>,
40
41			Arc<ApplicationRunTime>,
42
43			Value,
44		) -> Pin<Box<dyn Future<Output = Result<Value, String>> + Send>>,
45	),
46
47	/// A command implemented in an extension and proxied to a sidecar.
48	Proxied { SideCarIdentifier:String, CommandIdentifier:String },
49}
50
51impl<R:Runtime> Clone for CommandHandler<R> {
52	fn clone(&self) -> Self {
53		match self {
54			Self::Native(Function) => Self::Native(*Function),
55
56			Self::Proxied { SideCarIdentifier, CommandIdentifier } => {
57				Self::Proxied {
58					SideCarIdentifier:SideCarIdentifier.clone(),
59
60					CommandIdentifier:CommandIdentifier.clone(),
61				}
62			},
63		}
64	}
65}
66
67#[async_trait]
68impl CommandExecutor for MountainEnvironment {
69	/// Executes a registered command by dispatching it to the appropriate
70	/// handler.
71	async fn ExecuteCommand(&self, CommandIdentifier:String, Argument:Value) -> Result<Value, CommonError> {
72		let HandlerInfoOption = self
73			.ApplicationState
74			.CommandRegistry
75			.lock()
76			.map_err(super::Utility::MapApplicationStateLockErrorToCommonError)?
77			.get(&CommandIdentifier)
78			.cloned();
79
80		match HandlerInfoOption {
81			Some(CommandHandler::Native(Function)) => {
82				debug!("[CommandProvider] Executing NATIVE command '{}'.", CommandIdentifier);
83
84				let RunTime:Arc<ApplicationRunTime> =
85					self.ApplicationHandle.state::<Arc<ApplicationRunTime>>().inner().clone();
86
87				let MainWindow = self.ApplicationHandle.get_webview_window("main").ok_or_else(|| {
88					CommonError::UserInterfaceInteraction {
89						Reason:"Main window not found for command execution".into(),
90					}
91				})?;
92
93				Function(self.ApplicationHandle.clone(), MainWindow, RunTime, Argument)
94					.await
95					.map_err(|Error| CommonError::CommandExecution { CommandIdentifier, Reason:Error })
96			},
97
98			Some(CommandHandler::Proxied { SideCarIdentifier, CommandIdentifier: ProxiedCommandIdentifier }) => {
99				debug!(
100					"[CommandProvider] Executing PROXIED command '{}' on sidecar '{}'.",
101					CommandIdentifier, SideCarIdentifier
102				);
103
104				let RPCParameters = json!([ProxiedCommandIdentifier, Argument]);
105
106				let RPCMethod = format!("{}$ExecuteContributedCommand", ProxyTarget::ExtHostCommands.GetTargetPrefix());
107
108				Client::SendRequest(&SideCarIdentifier, RPCMethod, RPCParameters, 30000)
109					.await
110					.map_err(|Error| CommonError::IPCError { Description:Error.to_string() })
111			},
112
113			None => {
114				error!("[CommandProvider] Command '{}' not found in registry.", CommandIdentifier);
115
116				Err(CommonError::CommandNotFound { Identifier:CommandIdentifier })
117			},
118		}
119	}
120
121	/// Registers a command contributed by a sidecar process.
122	async fn RegisterCommand(&self, SideCarIdentifier:String, CommandIdentifier:String) -> Result<(), CommonError> {
123		info!(
124			"[CommandProvider] Registering PROXY command '{}' from sidecar '{}'",
125			CommandIdentifier, SideCarIdentifier
126		);
127
128		let mut Registry = self
129			.ApplicationState
130			.CommandRegistry
131			.lock()
132			.map_err(super::Utility::MapApplicationStateLockErrorToCommonError)?;
133
134		Registry.insert(
135			CommandIdentifier.clone(),
136			CommandHandler::Proxied { SideCarIdentifier, CommandIdentifier },
137		);
138
139		Ok(())
140	}
141
142	/// Unregisters a previously registered command.
143	async fn UnregisterCommand(&self, _SideCarIdentifier:String, CommandIdentifier:String) -> Result<(), CommonError> {
144		info!("[CommandProvider] Unregistering command '{}'", CommandIdentifier);
145
146		self.ApplicationState
147			.CommandRegistry
148			.lock()
149			.map_err(super::Utility::MapApplicationStateLockErrorToCommonError)?
150			.remove(&CommandIdentifier);
151
152		Ok(())
153	}
154
155	/// Gets a list of all currently registered command IDs.
156	async fn GetAllCommands(&self) -> Result<Vec<String>, CommonError> {
157		debug!("[CommandProvider] Getting all command identifiers.");
158
159		let Registry = self
160			.ApplicationState
161			.CommandRegistry
162			.lock()
163			.map_err(super::Utility::MapApplicationStateLockErrorToCommonError)?;
164
165		Ok(Registry.keys().cloned().collect())
166	}
167}