use crate::api::{ConfigItem, LogLevel, ValueList};
use crate::errors::NotImplemented;
use bitflags::bitflags;
use chrono::Duration;
use std::error;
use std::panic::{RefUnwindSafe, UnwindSafe};
bitflags! {
#[derive(Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct PluginCapabilities: u32 {
const READ = 0b0000_0001;
const LOG = 0b0000_0010;
const WRITE = 0b0000_0100;
const FLUSH = 0b0000_1000;
}
}
bitflags! {
#[derive(Default)]
pub struct PluginManagerCapabilities: u32 {
const INIT = 0b0000_0001;
}
}
pub enum PluginRegistration {
Single(Box<dyn Plugin>),
Multiple(Vec<(String, Box<dyn Plugin>)>),
}
impl PluginCapabilities {
pub fn has_read(self) -> bool {
self.intersects(PluginCapabilities::READ)
}
pub fn has_log(self) -> bool {
self.intersects(PluginCapabilities::LOG)
}
pub fn has_write(self) -> bool {
self.intersects(PluginCapabilities::WRITE)
}
pub fn has_flush(self) -> bool {
self.intersects(PluginCapabilities::FLUSH)
}
}
pub trait PluginManager {
fn name() -> &'static str;
fn capabilities() -> PluginManagerCapabilities {
PluginManagerCapabilities::default()
}
fn plugins(
_config: Option<&[ConfigItem<'_>]>,
) -> Result<PluginRegistration, Box<dyn error::Error>>;
fn initialize() -> Result<(), Box<dyn error::Error>> {
Err(NotImplemented.into())
}
fn shutdown() -> Result<(), Box<dyn error::Error>> {
Ok(())
}
}
pub trait Plugin: Send + Sync + UnwindSafe + RefUnwindSafe {
fn capabilities(&self) -> PluginCapabilities {
PluginCapabilities::default()
}
fn log(&self, _lvl: LogLevel, _msg: &str) -> Result<(), Box<dyn error::Error>> {
Err(NotImplemented.into())
}
fn read_values(&self) -> Result<(), Box<dyn error::Error>> {
Err(NotImplemented.into())
}
fn write_values(&self, _list: ValueList<'_>) -> Result<(), Box<dyn error::Error>> {
Err(NotImplemented.into())
}
fn flush(
&self,
_timeout: Option<Duration>,
_identifier: Option<&str>,
) -> Result<(), Box<dyn error::Error>> {
Err(NotImplemented.into())
}
}
#[macro_export]
macro_rules! collectd_plugin {
($type:ty) => {
static CONFIG_SEEN: ::std::sync::atomic::AtomicBool =
::std::sync::atomic::AtomicBool::new(false);
#[no_mangle]
pub extern "C" fn module_register() {
use std::ffi::CString;
use $crate::bindings::{
plugin_register_complex_config, plugin_register_init, plugin_register_shutdown,
};
$crate::internal::register_panic_handler();
let s = CString::new(<$type as $crate::PluginManager>::name())
.expect("Plugin name to not contain nulls");
unsafe {
plugin_register_complex_config(s.as_ptr(), Some(collectd_plugin_complex_config));
plugin_register_init(s.as_ptr(), Some(collectd_plugin_init));
plugin_register_shutdown(s.as_ptr(), Some(collectd_plugin_shutdown));
}
}
extern "C" fn collectd_plugin_init() -> ::std::os::raw::c_int {
$crate::internal::plugin_init::<$type>(&CONFIG_SEEN)
}
extern "C" fn collectd_plugin_shutdown() -> ::std::os::raw::c_int {
$crate::internal::plugin_shutdown::<$type>()
}
unsafe extern "C" fn collectd_plugin_complex_config(
config: *mut $crate::bindings::oconfig_item_t,
) -> ::std::os::raw::c_int {
$crate::internal::plugin_complex_config::<$type>(&CONFIG_SEEN, config)
}
};
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_plugin_capabilities() {
let capabilities = PluginCapabilities::READ | PluginCapabilities::WRITE;
assert_eq!(capabilities.has_read(), true);
assert_eq!(capabilities.has_write(), true);
let capabilities = PluginCapabilities::READ;
assert_eq!(capabilities.has_read(), true);
assert_eq!(capabilities.has_write(), false);
}
}