#![allow(non_camel_case_types, non_local_definitions)]
pub mod metrics;
pub mod types;
pub mod vtable;
pub use abi_stable;
pub use abi_stable::std_types::{
RBoxError, RHashMap, ROption, RResult, RSlice, RStr, RString, RVec, Tuple2,
};
pub use metrics::{MetricKind, MetricLabel, MetricRecorder, RecordMetricFn};
pub use types::{
ConfigValue, Diagnostic, DiagnosticSeverity, PluginContext, ProcessingResult, SpoeMessage,
SpoeValue, TxnVariable, VarScope,
};
pub use vtable::{
GET_PLUGIN_VTABLE_SYMBOL, GetPluginVTableFn, PLUGIN_API_VERSION, PLUGIN_API_VERSION_V1,
PLUGIN_API_VERSION_V2, PLUGIN_API_VERSION_V3, PluginVTable,
};
#[macro_export]
macro_rules! define_plugin {
(
$plugin_ty:ty, {
fn new() -> Self $new_body:block
fn init(&mut self, $ctx_param:ident : &PluginContext $(,)?)
-> Result<(), Box<dyn std::error::Error + Send + Sync>> $init_body:block
fn name(&self) -> &str $name_body:block
fn version(&self) -> &str $version_body:block
fn process(
&self,
$msg_param:ident : &SpoeMessage $(,)?
) -> Result<ProcessingResult, Box<dyn std::error::Error + Send + Sync>> $process_body:block
$(fn config_schema(&self) -> Option<&str> $schema_body:block)?
$(fn validate(&self, $vctx_param:ident : &PluginContext $(,)?) -> Vec<Diagnostic> $validate_body:block)?
$(fn drain(&self, $drain_timeout_param:ident : u64 $(,)?) -> bool $drain_body:block)?
$(metrics_static = $metrics_static:path;)?
}
) => {
impl $plugin_ty {
#[allow(dead_code)]
fn __new() -> Self $new_body
#[allow(clippy::unnecessary_wraps, dead_code)]
fn __init(
&mut self,
$ctx_param: &$crate::PluginContext,
) -> ::std::result::Result<(), ::std::boxed::Box<dyn ::std::error::Error + Send + Sync>>
$init_body
#[allow(dead_code)]
fn __name(&self) -> &'static str $name_body
#[allow(dead_code)]
fn __version(&self) -> &'static str $version_body
#[allow(clippy::unnecessary_wraps, dead_code)]
fn __process(
&self,
$msg_param: &$crate::SpoeMessage,
) -> ::std::result::Result<
$crate::ProcessingResult,
::std::boxed::Box<dyn ::std::error::Error + Send + Sync>,
> $process_body
$(
#[allow(clippy::unnecessary_wraps, dead_code)]
fn __config_schema(&self) -> ::std::option::Option<&'static str> $schema_body
)?
$(
#[allow(clippy::unnecessary_wraps, dead_code)]
fn __validate(
&self,
$vctx_param: &$crate::PluginContext,
) -> ::std::vec::Vec<$crate::Diagnostic> $validate_body
)?
$(
#[allow(clippy::unnecessary_wraps, dead_code)]
fn __drain(&self, $drain_timeout_param: u64) -> bool $drain_body
)?
}
const _: () = {
use ::std::os::raw::c_void;
use ::std::panic::{AssertUnwindSafe, catch_unwind};
extern "C" fn create() -> $crate::RResult<*mut c_void, $crate::RBoxError> {
match catch_unwind(|| {
let plugin: ::std::boxed::Box<$plugin_ty> =
::std::boxed::Box::new(<$plugin_ty>::__new());
::std::boxed::Box::into_raw(plugin).cast::<c_void>()
}) {
Ok(raw) => $crate::RResult::ROk(raw),
Err(_) => $crate::RResult::RErr($crate::RBoxError::new(
$crate::PluginPanicError,
)),
}
}
extern "C" fn destroy(state: *mut c_void) {
if state.is_null() {
return;
}
let _ = catch_unwind(AssertUnwindSafe(|| unsafe {
::std::mem::drop(::std::boxed::Box::from_raw(state.cast::<$plugin_ty>()));
}));
}
extern "C" fn init(
state: *mut c_void,
ctx: &$crate::PluginContext,
) -> $crate::RResult<(), $crate::RBoxError> {
let plugin = unsafe { &mut *state.cast::<$plugin_ty>() };
match catch_unwind(AssertUnwindSafe(|| plugin.__init(ctx))) {
Ok(Ok(())) => $crate::RResult::ROk(()),
Ok(Err(e)) => $crate::RResult::RErr($crate::RBoxError::from_box(e)),
Err(_) => $crate::RResult::RErr($crate::RBoxError::new(
$crate::PluginPanicError,
)),
}
}
extern "C" fn process(
state: *const c_void,
msg: &$crate::SpoeMessage,
) -> $crate::RResult<$crate::ProcessingResult, $crate::RBoxError> {
let plugin = unsafe { &*state.cast::<$plugin_ty>() };
match catch_unwind(AssertUnwindSafe(|| plugin.__process(msg))) {
Ok(Ok(result)) => $crate::RResult::ROk(result),
Ok(Err(e)) => $crate::RResult::RErr($crate::RBoxError::from_box(e)),
Err(_) => $crate::RResult::RErr($crate::RBoxError::new(
$crate::PluginPanicError,
)),
}
}
extern "C" fn name(state: *const c_void) -> $crate::RStr<'static> {
let plugin = unsafe { &*state.cast::<$plugin_ty>() };
match catch_unwind(AssertUnwindSafe(|| plugin.__name())) {
Ok(s) => $crate::RStr::from(s),
Err(_) => $crate::RStr::from("<plugin-panic>"),
}
}
extern "C" fn plugin_version(state: *const c_void) -> $crate::RStr<'static> {
let plugin = unsafe { &*state.cast::<$plugin_ty>() };
match catch_unwind(AssertUnwindSafe(|| plugin.__version())) {
Ok(s) => $crate::RStr::from(s),
Err(_) => $crate::RStr::from("<plugin-panic>"),
}
}
extern "C" fn shutdown(_state: *const c_void) {
let _ = catch_unwind(AssertUnwindSafe(|| {
let _ = _state;
}));
}
extern "C" fn config_schema(_state: *const c_void) -> $crate::ROption<$crate::RString> {
catch_unwind(AssertUnwindSafe(|| {
$crate::__define_plugin_config_schema_thunk!(_state, $plugin_ty $(, $schema_body)?)
}))
.unwrap_or($crate::ROption::RNone)
}
extern "C" fn validate(
_state: *const c_void,
_ctx: &$crate::PluginContext,
) -> $crate::RVec<$crate::Diagnostic> {
catch_unwind(AssertUnwindSafe(|| {
$crate::__define_plugin_validate_thunk!(_state, _ctx, $plugin_ty $(, $validate_body)?)
}))
.unwrap_or_else(|_| {
let mut diags = $crate::RVec::new();
diags.push($crate::Diagnostic::error(
0,
0,
"plugin's validate() panicked",
));
diags
})
}
extern "C" fn set_metric_recorder(
_state: *mut c_void,
_record_fn: $crate::RecordMetricFn,
_ctx: *const c_void,
) {
let _ = catch_unwind(AssertUnwindSafe(|| {
$crate::__define_plugin_metrics_thunk!(_state, _record_fn, _ctx $(, $metrics_static)?)
}));
}
extern "C" fn drain(_state: *const c_void, _timeout_ms: u64) -> bool {
catch_unwind(AssertUnwindSafe(|| {
$crate::__define_plugin_drain_thunk!(_state, _timeout_ms, $plugin_ty $(, $drain_body)?)
}))
.unwrap_or(false)
}
#[allow(non_upper_case_globals)]
static PLUGIN_VTABLE_INSTANCE: $crate::PluginVTable = $crate::PluginVTable {
api_version: $crate::PLUGIN_API_VERSION,
create,
destroy,
init,
process,
name,
plugin_version,
shutdown,
config_schema,
validate,
set_metric_recorder,
drain,
};
#[unsafe(no_mangle)]
pub extern "C" fn get_plugin_vtable() -> *const $crate::PluginVTable {
&PLUGIN_VTABLE_INSTANCE
}
};
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __define_plugin_config_schema_thunk {
($state:ident, $plugin_ty:ty) => {{
let _ = $state;
$crate::ROption::RNone
}};
($state:ident, $plugin_ty:ty, $body:block) => {{
let plugin = unsafe { &*$state.cast::<$plugin_ty>() };
match plugin.__config_schema() {
::std::option::Option::Some(s) => $crate::ROption::RSome($crate::RString::from(s)),
::std::option::Option::None => $crate::ROption::RNone,
}
}};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __define_plugin_validate_thunk {
($state:ident, $ctx:ident, $plugin_ty:ty) => {{
let _ = $state;
let _ = $ctx;
$crate::RVec::new()
}};
($state:ident, $ctx:ident, $plugin_ty:ty, $body:block) => {{
let plugin = unsafe { &*$state.cast::<$plugin_ty>() };
$crate::RVec::from(plugin.__validate($ctx))
}};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __define_plugin_drain_thunk {
($state:ident, $timeout_ms:ident, $plugin_ty:ty) => {{
let _ = $state;
let _ = $timeout_ms;
true
}};
($state:ident, $timeout_ms:ident, $plugin_ty:ty, $body:block) => {{
let plugin = unsafe { &*$state.cast::<$plugin_ty>() };
plugin.__drain($timeout_ms)
}};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __define_plugin_metrics_thunk {
($state:ident, $record_fn:ident, $ctx:ident) => {{
let _ = $state;
let _ = $record_fn;
let _ = $ctx;
}};
($state:ident, $record_fn:ident, $ctx:ident, $metrics_static:path) => {{
let _ = $state;
$metrics_static.install($record_fn, $ctx);
}};
}
#[derive(Debug)]
pub struct PluginPanicError;
impl std::fmt::Display for PluginPanicError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "plugin panicked during message processing")
}
}
impl std::error::Error for PluginPanicError {}