use super::error::UdfError;
use super::plugin::{check_arity, UdfDestroyFn, UdfPlugin};
use crate::executor::expression::{ExpressionError, ExpressionErrorType};
use libloading::Library;
use linkrs_core::Value;
use std::panic::{catch_unwind, AssertUnwindSafe};
use std::sync::Arc;
pub struct DynamicPlugin {
raw: *mut dyn UdfPlugin,
destroy: UdfDestroyFn,
_lib: Arc<Library>,
path: String,
}
unsafe impl Send for DynamicPlugin {}
unsafe impl Sync for DynamicPlugin {}
impl DynamicPlugin {
pub unsafe fn from_raw(
raw: *mut dyn UdfPlugin,
destroy: UdfDestroyFn,
lib: Arc<Library>,
path: String,
) -> Result<Self, UdfError> {
if raw.is_null() {
return Err(UdfError::NullPlugin(path));
}
Ok(Self {
raw,
destroy,
_lib: lib,
path,
})
}
fn inner(&self) -> &dyn UdfPlugin {
unsafe { &*self.raw }
}
}
impl Drop for DynamicPlugin {
fn drop(&mut self) {
unsafe {
(self.destroy)(self.raw);
}
}
}
impl std::fmt::Debug for DynamicPlugin {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("DynamicPlugin")
.field("name", &self.inner().name())
.field("path", &self.path)
.finish()
}
}
impl UdfPlugin for DynamicPlugin {
fn name(&self) -> &str {
self.inner().name()
}
fn description(&self) -> &str {
self.inner().description()
}
fn min_arity(&self) -> usize {
self.inner().min_arity()
}
fn max_arity(&self) -> usize {
self.inner().max_arity()
}
fn is_pure(&self) -> bool {
self.inner().is_pure()
}
fn execute(&self, args: &[Value]) -> Result<Value, ExpressionError> {
check_arity(self.inner(), args)?;
let name = self.inner().name().to_string();
match catch_unwind(AssertUnwindSafe(|| self.inner().execute(args))) {
Ok(result) => result,
Err(payload) => {
let detail = payload
.downcast_ref::<String>()
.cloned()
.or_else(|| payload.downcast_ref::<&str>().map(|s| s.to_string()))
.unwrap_or_else(|| "unknown panic".to_string());
Err(ExpressionError::new(
ExpressionErrorType::FunctionExecutionError,
UdfError::ExecutionPanic(name, detail).to_string(),
))
}
}
}
}
pub fn execute_isolated(plugin: &dyn UdfPlugin, args: &[Value]) -> Result<Value, ExpressionError> {
check_arity(plugin, args)?;
let name = plugin.name().to_string();
match catch_unwind(AssertUnwindSafe(|| plugin.execute(args))) {
Ok(result) => result,
Err(payload) => {
let detail = payload
.downcast_ref::<String>()
.cloned()
.or_else(|| payload.downcast_ref::<&str>().map(|s| s.to_string()))
.unwrap_or_else(|| "unknown panic".to_string());
Err(ExpressionError::new(
ExpressionErrorType::FunctionExecutionError,
UdfError::ExecutionPanic(name, detail).to_string(),
))
}
}
}