summaryrefslogtreecommitdiff
path: root/crates/system_action/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/system_action/src')
-rw-r--r--crates/system_action/src/action.rs38
-rw-r--r--crates/system_action/src/action_pool.rs108
-rw-r--r--crates/system_action/src/lib.rs5
3 files changed, 151 insertions, 0 deletions
diff --git a/crates/system_action/src/action.rs b/crates/system_action/src/action.rs
new file mode 100644
index 0000000..14f1148
--- /dev/null
+++ b/crates/system_action/src/action.rs
@@ -0,0 +1,38 @@
+use tcp_connection::{error::TcpTargetError, instance::ConnectionInstance};
+
+pub trait Action<Args, Return> {
+ fn action_name() -> &'static str;
+
+ fn is_remote_action() -> bool;
+
+ fn process(
+ context: ActionContext,
+ args: Args,
+ ) -> impl std::future::Future<Output = Result<Return, TcpTargetError>> + Send;
+}
+
+pub struct ActionContext {
+ // Whether the action is executed locally or remotely
+ local: bool,
+
+ /// The connection instance in the current context,
+ /// used to interact with the machine on the other end
+ instance: ConnectionInstance,
+}
+
+impl ActionContext {
+ /// Whether the action is executed locally
+ pub fn is_local(&self) -> bool {
+ self.local
+ }
+
+ /// Whether the action is executed remotely
+ pub fn is_remote(&self) -> bool {
+ !self.local
+ }
+
+ /// Get the connection instance in the current context
+ pub fn instance(&self) -> &ConnectionInstance {
+ &self.instance
+ }
+}
diff --git a/crates/system_action/src/action_pool.rs b/crates/system_action/src/action_pool.rs
new file mode 100644
index 0000000..0a1a6c7
--- /dev/null
+++ b/crates/system_action/src/action_pool.rs
@@ -0,0 +1,108 @@
+use tcp_connection::error::TcpTargetError;
+
+use crate::action::{Action, ActionContext};
+
+/// A pool of registered actions that can be processed by name
+pub struct ActionPool {
+ /// HashMap storing action name to action implementation mapping
+ actions: std::collections::HashMap<&'static str, Box<dyn ActionErased>>,
+}
+
+impl ActionPool {
+ /// Creates a new empty ActionPool
+ pub fn new() -> Self {
+ Self {
+ actions: std::collections::HashMap::new(),
+ }
+ }
+
+ /// Registers an action type with the pool
+ ///
+ /// Usage:
+ /// ```
+ /// action_pool.register::<MyAction, MyArgs, MyReturn>();
+ /// ```
+ pub fn register<A, Args, Return>(&mut self)
+ where
+ A: Action<Args, Return> + Send + Sync + 'static,
+ Args: serde::de::DeserializeOwned + Send + Sync + 'static,
+ Return: serde::Serialize + Send + Sync + 'static,
+ {
+ let action_name = A::action_name();
+ self.actions.insert(
+ action_name,
+ Box::new(ActionWrapper::<A, Args, Return>(std::marker::PhantomData)),
+ );
+ }
+
+ /// Processes an action by name with given context and arguments
+ ///
+ /// Usage:
+ /// ```
+ /// let result = action_pool.process::<MyArgs, MyReturn>("my_action", context, args).await?;
+ /// ```
+ pub async fn process<'a, Args, Return>(
+ &'a self,
+ action_name: &'a str,
+ context: ActionContext,
+ args: Args,
+ ) -> Result<Return, TcpTargetError>
+ where
+ Args: serde::de::DeserializeOwned + Send + 'static,
+ Return: serde::Serialize + Send + 'static,
+ {
+ if let Some(action) = self.actions.get(action_name) {
+ let result = action.process_erased(context, Box::new(args)).await?;
+ let result = *result
+ .downcast::<Return>()
+ .map_err(|_| TcpTargetError::Unsupported("InvalidArguments".to_string()))?;
+ Ok(result)
+ } else {
+ Err(TcpTargetError::Unsupported("InvalidAction".to_string()))
+ }
+ }
+}
+
+/// Trait for type-erased actions that can be stored in ActionPool
+trait ActionErased: Send + Sync {
+ /// Processes the action with type-erased arguments and returns type-erased result
+ fn process_erased(
+ &self,
+ context: ActionContext,
+ args: Box<dyn std::any::Any + Send>,
+ ) -> std::pin::Pin<
+ Box<
+ dyn std::future::Future<Output = Result<Box<dyn std::any::Any + Send>, TcpTargetError>>
+ + Send,
+ >,
+ >;
+}
+
+/// Wrapper struct that implements ActionErased for concrete Action types
+struct ActionWrapper<A, Args, Return>(std::marker::PhantomData<(A, Args, Return)>);
+
+impl<A, Args, Return> ActionErased for ActionWrapper<A, Args, Return>
+where
+ A: Action<Args, Return> + Send + Sync,
+ Args: serde::de::DeserializeOwned + Send + Sync + 'static,
+ Return: serde::Serialize + Send + Sync + 'static,
+{
+ fn process_erased(
+ &self,
+ context: ActionContext,
+ args: Box<dyn std::any::Any + Send>,
+ ) -> std::pin::Pin<
+ Box<
+ dyn std::future::Future<Output = Result<Box<dyn std::any::Any + Send>, TcpTargetError>>
+ + Send,
+ >,
+ > {
+ Box::pin(async move {
+ let args = *args
+ .downcast::<Args>()
+ .map_err(|_| TcpTargetError::Unsupported("InvalidArguments".to_string()))?;
+ let result = A::process(context, args).await?;
+ Ok(Box::new(result) as Box<dyn std::any::Any + Send>)
+ })
+ }
+}
diff --git a/crates/system_action/src/lib.rs b/crates/system_action/src/lib.rs
new file mode 100644
index 0000000..07be1bb
--- /dev/null
+++ b/crates/system_action/src/lib.rs
@@ -0,0 +1,5 @@
+pub use action_system_macros::*;
+pub extern crate action_system_macros as macros;
+
+pub mod action;
+pub mod action_pool;