use serde::de::DeserializeOwned;
use serde::Serialize;
use fidius_core::descriptor::PluginDescriptor;
use crate::error::{CallError, LoadError};
use crate::executor::cdylib::CdylibExecutor;
#[cfg(feature = "python")]
use crate::executor::python::Pyo3Executor;
#[cfg(feature = "wasm")]
use crate::executor::wasm::WasmComponentExecutor;
#[cfg(any(feature = "python", feature = "wasm"))]
use crate::executor::{PluginExecutor, ValueExecutor};
use crate::types::PluginInfo;
enum Backend {
Cdylib(CdylibExecutor),
#[cfg(feature = "python")]
Python(Pyo3Executor),
#[cfg(feature = "wasm")]
Wasm(WasmComponentExecutor),
}
pub struct PluginHandle {
backend: Backend,
}
impl PluginHandle {
pub fn from_loaded(plugin: crate::loader::LoadedPlugin) -> Self {
Self {
backend: Backend::Cdylib(CdylibExecutor::from_loaded(plugin)),
}
}
pub fn from_descriptor(desc: &'static PluginDescriptor) -> Result<Self, LoadError> {
Ok(Self {
backend: Backend::Cdylib(CdylibExecutor::from_descriptor(desc)?),
})
}
pub fn find_in_process_descriptor(
plugin_name: &str,
) -> Result<&'static PluginDescriptor, LoadError> {
CdylibExecutor::find_in_process_descriptor(plugin_name)
}
#[cfg(feature = "python")]
pub fn from_python(py: fidius_python::PythonPluginHandle, info: PluginInfo) -> Self {
Self {
backend: Backend::Python(Pyo3Executor::new(py, info)),
}
}
#[cfg(feature = "wasm")]
pub fn from_wasm(executor: WasmComponentExecutor) -> Self {
Self {
backend: Backend::Wasm(executor),
}
}
pub fn call_method<I: Serialize, O: DeserializeOwned>(
&self,
index: usize,
input: &I,
) -> Result<O, CallError> {
match &self.backend {
Backend::Cdylib(e) => e.call_method(index, input),
#[cfg(feature = "python")]
Backend::Python(e) => {
let args = fidius_core::to_value(input)
.map_err(|err| CallError::Serialization(err.to_string()))?;
let out = ValueExecutor::call(e, index, args)?;
fidius_core::from_value(out)
.map_err(|err| CallError::Deserialization(err.to_string()))
}
#[cfg(feature = "wasm")]
Backend::Wasm(e) => {
let args = fidius_core::to_value(input)
.map_err(|err| CallError::Serialization(err.to_string()))?;
let out = ValueExecutor::call(e, index, args)?;
fidius_core::from_value(out)
.map_err(|err| CallError::Deserialization(err.to_string()))
}
}
}
#[cfg(feature = "streaming")]
pub async fn call_streaming<I: Serialize, O: DeserializeOwned + Serialize>(
&self,
index: usize,
input: &I,
) -> Result<crate::stream::ChunkStream, CallError> {
match &self.backend {
Backend::Cdylib(e) => {
let input_bytes = fidius_core::wire::serialize(input)
.map_err(|err| CallError::Serialization(err.to_string()))?;
e.call_streaming_raw(index, &input_bytes, cdylib_stream_decode::<O>)
}
#[cfg(feature = "python")]
Backend::Python(e) => {
let args = fidius_core::to_value(input)
.map_err(|err| CallError::Serialization(err.to_string()))?;
crate::stream::StreamExecutor::call_streaming(e, index, args).await
}
#[cfg(feature = "wasm")]
Backend::Wasm(e) => {
let args = fidius_core::to_value(input)
.map_err(|err| CallError::Serialization(err.to_string()))?;
crate::stream::StreamExecutor::call_streaming(e, index, args).await
}
}
}
pub fn call_method_raw(&self, index: usize, input: &[u8]) -> Result<Vec<u8>, CallError> {
match &self.backend {
Backend::Cdylib(e) => e.call_method_raw(index, input),
#[cfg(feature = "python")]
Backend::Python(e) => PluginExecutor::call_raw(e, index, input),
#[cfg(feature = "wasm")]
Backend::Wasm(e) => PluginExecutor::call_raw(e, index, input),
}
}
pub fn has_capability(&self, bit: u32) -> bool {
if bit >= 64 {
return false;
}
self.info().capabilities & (1u64 << bit) != 0
}
pub fn info(&self) -> &PluginInfo {
match &self.backend {
Backend::Cdylib(e) => e.info(),
#[cfg(feature = "python")]
Backend::Python(e) => PluginExecutor::info(e),
#[cfg(feature = "wasm")]
Backend::Wasm(e) => PluginExecutor::info(e),
}
}
pub fn method_metadata(&self, method_id: u32) -> Vec<(&str, &str)> {
match &self.backend {
Backend::Cdylib(e) => e.method_metadata(method_id),
#[cfg(feature = "python")]
Backend::Python(_) => Vec::new(),
#[cfg(feature = "wasm")]
Backend::Wasm(_) => Vec::new(),
}
}
pub fn trait_metadata(&self) -> Vec<(&str, &str)> {
match &self.backend {
Backend::Cdylib(e) => e.trait_metadata(),
#[cfg(feature = "python")]
Backend::Python(_) => Vec::new(),
#[cfg(feature = "wasm")]
Backend::Wasm(_) => Vec::new(),
}
}
}
#[cfg(feature = "streaming")]
fn cdylib_stream_decode<O: DeserializeOwned + Serialize>(
bytes: &[u8],
) -> Result<fidius_core::Value, CallError> {
let item: O = fidius_core::wire::deserialize(bytes)
.map_err(|e| CallError::Deserialization(e.to_string()))?;
fidius_core::to_value(&item).map_err(|e| CallError::Serialization(e.to_string()))
}