use async_trait::async_trait; use galaxy_js::{JsFunctionId, SerializedJsValue, TypedJsFunctionRef}; use serde::de::DeserializeOwned; use serde::Serialize; #[derive(thiserror::Error, Debug)] pub enum JsExecutionError { #[error("Could not execute JS due to serialization error: {0:?}")] Serialization(bincode::Error), #[error("Could not execute JS due to deserialization error: {0:?}")] Deserialization(bincode::Error), #[error("Internal error occurred: {0}")] Internal(String), } /// Trait to be implemented by callers using V2 command signatures. V2 command signatures are /// defined in JavaScript and may contain JS functions, which are internally represented with /// `TypedJsFunctionRef`s. #[async_trait] pub trait JsExecutionContext: Send + Sync { async fn call_js_function( &self, input: SerializedJsValue, function_id: JsFunctionId, ) -> Result; } /// Helper function for making typed JS function calls. pub(crate) async fn call_js_function( input: &I, js_function_ref: &TypedJsFunctionRef, js_ctx: &dyn JsExecutionContext, ) -> Result where I: Serialize, O: DeserializeOwned, { let serialized_input = SerializedJsValue::from_value(input).map_err(JsExecutionError::Serialization)?; let serialized_output = js_ctx .call_js_function(serialized_input, js_function_ref.id) .await?; let output: O = serialized_output .to_value() .map_err(JsExecutionError::Deserialization)?; Ok(output) }