Skip to main content

falco_plugin/base/
wrappers.rs

1use crate::base::logger::{FalcoPluginLoggerImpl, FALCO_LOGGER};
2use crate::base::schema::{ConfigSchema, ConfigSchemaType};
3use crate::base::Plugin;
4use crate::error::ffi_result::FfiResult;
5use crate::error::last_error::LastError;
6use crate::error::panic::catch_panic;
7use crate::strings::from_ptr::try_str_from_ptr;
8use crate::strings::WriteIntoCString;
9use crate::tables::TablesInput;
10use anyhow::Context;
11use falco_plugin_api::{
12    ss_plugin_init_input, ss_plugin_metric, ss_plugin_rc, ss_plugin_rc_SS_PLUGIN_FAILURE,
13    ss_plugin_rc_SS_PLUGIN_SUCCESS, ss_plugin_t,
14};
15use std::collections::BTreeMap;
16use std::ffi::{c_char, CString};
17use std::fmt::Display;
18use std::io::Write;
19use std::panic::AssertUnwindSafe;
20use std::sync::Mutex;
21
22/// Marker trait to mark a plugin as exported to the API
23///
24/// # Safety
25///
26/// Only implement this trait if you export the plugin either statically or dynamically
27/// to the plugin API. This is handled by the `plugin!` and `static_plugin!` macros, so you
28/// should never need to implement this trait manually.
29#[diagnostic::on_unimplemented(
30    message = "Plugin is not exported",
31    note = "use either `plugin!` or `static_plugin!`"
32)]
33pub unsafe trait BasePluginExported {}
34
35pub extern "C" fn plugin_get_required_api_version<
36    const MAJOR: usize,
37    const MINOR: usize,
38    const PATCH: usize,
39>() -> *const c_char {
40    static VERSIONS: Mutex<BTreeMap<(usize, usize, usize), CString>> = Mutex::new(BTreeMap::new());
41
42    let mut version = VERSIONS.lock().unwrap();
43    // we only generate the string once and never change or delete it
44    // so the pointer should remain valid for the static lifetime
45    version
46        .entry((MAJOR, MINOR, PATCH))
47        .or_insert_with(|| {
48            let version = format!("{MAJOR}.{MINOR}.{PATCH}");
49            CString::new(version).unwrap()
50        })
51        .as_ptr()
52}
53
54pub extern "C" fn plugin_get_version<T: Plugin>() -> *const c_char {
55    T::PLUGIN_VERSION.as_ptr()
56}
57
58pub extern "C" fn plugin_get_name<T: Plugin>() -> *const c_char {
59    T::NAME.as_ptr()
60}
61
62pub extern "C" fn plugin_get_description<T: Plugin>() -> *const c_char {
63    T::DESCRIPTION.as_ptr()
64}
65
66pub extern "C" fn plugin_get_contact<T: Plugin>() -> *const c_char {
67    T::CONTACT.as_ptr()
68}
69
70/// # Safety
71///
72/// init_input must be null or a valid pointer
73pub unsafe extern "C" fn plugin_init<P: Plugin>(
74    init_input: *const ss_plugin_init_input,
75    rc: *mut ss_plugin_rc,
76) -> *mut falco_plugin_api::ss_plugin_t {
77    let res = catch_panic(AssertUnwindSafe(
78        || -> Result<*mut PluginWrapper<P>, anyhow::Error> {
79            let init_input = unsafe { init_input.as_ref() }
80                .ok_or_else(|| anyhow::anyhow!("Got empty init_input"))?;
81
82            let init_config =
83                try_str_from_ptr(&init_input.config).context("Failed to get config string")?;
84
85            let config = P::ConfigType::from_str(init_config).context("Failed to parse config")?;
86            if let Some(log_fn) = init_input.log_fn {
87                let logger_impl = FalcoPluginLoggerImpl {
88                    owner: init_input.owner,
89                    logger_fn: log_fn,
90                };
91
92                *FALCO_LOGGER.inner.write().unwrap() = Some(logger_impl);
93                log::set_logger(&FALCO_LOGGER).ok();
94
95                #[cfg(debug_assertions)]
96                log::set_max_level(log::LevelFilter::Trace);
97
98                #[cfg(not(debug_assertions))]
99                log::set_max_level(log::LevelFilter::Info);
100            }
101
102            let tables_input =
103                TablesInput::try_from(init_input).context("Failed to build tables input")?;
104
105            let last_error = unsafe { LastError::from(init_input)? };
106
107            P::new(tables_input.as_ref(), config)
108                .map(|plugin| Box::into_raw(Box::new(PluginWrapper::new(plugin, last_error))))
109        },
110    ));
111
112    match res {
113        Ok(plugin) => {
114            unsafe {
115                *rc = ss_plugin_rc_SS_PLUGIN_SUCCESS;
116            }
117            plugin.cast()
118        }
119        Err(e) => {
120            let error_str = format!("{:#}", &e);
121            log::error!("Failed to initialize plugin: {error_str}");
122            let plugin = Box::new(PluginWrapper::<P>::new_error(error_str));
123            unsafe {
124                *rc = e.status_code();
125            }
126            Box::into_raw(plugin).cast()
127        }
128    }
129}
130
131/// # Safety
132///
133/// schema_type must be null or a valid pointer
134pub unsafe extern "C" fn plugin_get_init_schema<P: Plugin>(
135    schema_type: *mut falco_plugin_api::ss_plugin_schema_type,
136) -> *const c_char {
137    let schema_type = unsafe {
138        let Some(schema_type) = schema_type.as_mut() else {
139            return std::ptr::null();
140        };
141        schema_type
142    };
143    match P::ConfigType::get_schema() {
144        ConfigSchemaType::None => {
145            *schema_type = falco_plugin_api::ss_plugin_schema_type_SS_PLUGIN_SCHEMA_NONE;
146            std::ptr::null()
147        }
148        ConfigSchemaType::Json(s) => {
149            *schema_type = falco_plugin_api::ss_plugin_schema_type_SS_PLUGIN_SCHEMA_JSON;
150            s.as_ptr()
151        }
152    }
153}
154
155/// # Safety
156///
157/// `plugin` must have been created by `init()` and not destroyed since
158pub unsafe extern "C" fn plugin_destroy<P: Plugin>(plugin: *mut falco_plugin_api::ss_plugin_t) {
159    unsafe {
160        let plugin = plugin as *mut PluginWrapper<P>;
161        match catch_panic(AssertUnwindSafe(|| {
162            let _ = Box::from_raw(plugin);
163            Ok(())
164        })) {
165            Ok(()) => {}
166            Err(e) => {
167                log::error!("Failed to destroy plugin: {e}");
168            }
169        }
170    }
171}
172
173/// # Safety
174///
175/// `plugin` must be a valid pointer to `PluginWrapper<P>`
176pub unsafe extern "C" fn plugin_get_last_error<P: Plugin>(
177    plugin: *mut falco_plugin_api::ss_plugin_t,
178) -> *const c_char {
179    let plugin = plugin as *mut PluginWrapper<P>;
180    match unsafe { plugin.as_mut() } {
181        Some(plugin) => plugin.error_buf.as_ptr(),
182        None => c"no instance".as_ptr(),
183    }
184}
185
186pub unsafe extern "C" fn plugin_set_config<P: Plugin>(
187    plugin: *mut falco_plugin_api::ss_plugin_t,
188    config_input: *const falco_plugin_api::ss_plugin_set_config_input,
189) -> falco_plugin_api::ss_plugin_rc {
190    let plugin = plugin as *mut PluginWrapper<P>;
191    let plugin = unsafe {
192        let Some(plugin) = plugin.as_mut() else {
193            return ss_plugin_rc_SS_PLUGIN_FAILURE;
194        };
195        plugin
196    };
197
198    let Some(actual_plugin) = &mut plugin.plugin else {
199        return ss_plugin_rc_SS_PLUGIN_FAILURE;
200    };
201
202    let res = catch_panic(AssertUnwindSafe(|| -> Result<(), anyhow::Error> {
203        let config_input = unsafe { config_input.as_ref() }.context("Got NULL config")?;
204
205        let updated_config =
206            try_str_from_ptr(&config_input.config).context("Failed to get config string")?;
207        let config = P::ConfigType::from_str(updated_config).context("Failed to parse config")?;
208
209        actual_plugin.plugin.set_config(config)
210    }));
211
212    res.rc(&mut plugin.error_buf)
213}
214
215pub unsafe extern "C" fn plugin_get_metrics<P: Plugin>(
216    plugin: *mut ss_plugin_t,
217    num_metrics: *mut u32,
218) -> *mut ss_plugin_metric {
219    let plugin = plugin as *mut PluginWrapper<P>;
220    let num_metrics = unsafe {
221        let Some(num_metrics) = num_metrics.as_mut() else {
222            return std::ptr::null_mut();
223        };
224        num_metrics
225    };
226
227    let plugin = unsafe {
228        let Some(plugin) = plugin.as_mut() else {
229            *num_metrics = 0;
230            return std::ptr::null_mut();
231        };
232        plugin
233    };
234
235    let Some(actual_plugin) = &mut plugin.plugin else {
236        *num_metrics = 0;
237        return std::ptr::null_mut();
238    };
239
240    plugin.metric_storage.clear();
241    if let Err(e) = catch_panic(AssertUnwindSafe(|| {
242        for metric in actual_plugin.plugin.get_metrics() {
243            plugin.metric_storage.push(metric.as_raw());
244        }
245        Ok(())
246    })) {
247        e.set_last_error(&mut plugin.error_buf);
248        *num_metrics = 0;
249        return std::ptr::null_mut();
250    }
251
252    *num_metrics = plugin.metric_storage.len() as u32;
253    plugin.metric_storage.as_ptr().cast_mut()
254}
255
256pub extern "C" fn plugin_get_required_event_schema_version<T: Plugin>(
257    _plugin: *mut ss_plugin_t,
258) -> *const c_char {
259    T::SCHEMA_VERSION.as_ptr()
260}
261
262#[doc(hidden)]
263#[macro_export]
264macro_rules! wrap_ffi {
265    (
266        #[$attr:meta]
267        use $mod:path: <$ty:ty>;
268
269    $(unsafe fn $name:ident( $($param:ident: $param_ty:ty),* $(,)*) -> $ret:ty;)*
270    ) => {
271        $(
272        #[$attr]
273        pub unsafe extern "C" fn $name ( $($param: $param_ty),*) -> $ret {
274            use $mod as wrappers;
275
276            wrappers::$name::<$ty>($($param),*)
277        }
278        )*
279    }
280}
281
282/// # Register a Falco plugin
283///
284/// This macro must be called at most once in a crate (it generates public functions)
285/// with a type implementing [`Plugin`] as the sole parameter:
286///
287/// ```
288/// # use std::ffi::CStr;
289/// use falco_plugin::base::Plugin;
290/// # use falco_plugin::base::Metric;
291/// use falco_plugin::plugin;
292/// use falco_plugin::tables::TablesInput;
293///
294/// struct MyPlugin;
295/// impl Plugin for MyPlugin {
296///     // ...
297/// #    const NAME: &'static CStr = c"sample-plugin-rs";
298/// #    const PLUGIN_VERSION: &'static CStr = c"0.0.1";
299/// #    const DESCRIPTION: &'static CStr = c"A sample Falco plugin that does nothing";
300/// #    const CONTACT: &'static CStr = c"you@example.com";
301/// #    type ConfigType = ();
302/// #
303/// #    fn new(input: Option<&TablesInput>, config: Self::ConfigType)
304/// #        -> Result<Self, anyhow::Error> {
305/// #        Ok(MyPlugin)
306/// #    }
307/// #
308/// #    fn set_config(&mut self, config: Self::ConfigType) -> Result<(), anyhow::Error> {
309/// #        Ok(())
310/// #    }
311/// #
312/// #    fn get_metrics(&mut self) -> impl IntoIterator<Item=Metric> {
313/// #        []
314/// #    }
315/// }
316///
317/// plugin!(#[no_capabilities] MyPlugin);
318/// ```
319///
320/// It implements a form where you can override the required API version (for example, if
321/// you wish to advertise an older version for increased compatibility):
322///
323/// ```
324/// # use std::ffi::CStr;
325/// use falco_plugin::base::Plugin;
326/// # use falco_plugin::base::Metric;
327/// use falco_plugin::plugin;
328/// use falco_plugin::tables::TablesInput;
329///
330/// struct MyPlugin;
331/// impl Plugin for MyPlugin {
332///     // ...
333/// #    const NAME: &'static CStr = c"sample-plugin-rs";
334/// #    const PLUGIN_VERSION: &'static CStr = c"0.0.1";
335/// #    const DESCRIPTION: &'static CStr = c"A sample Falco plugin that does nothing";
336/// #    const CONTACT: &'static CStr = c"you@example.com";
337/// #    type ConfigType = ();
338/// #
339/// #    fn new(input: Option<&TablesInput>, config: Self::ConfigType)
340/// #        -> Result<Self, anyhow::Error> {
341/// #        Ok(MyPlugin)
342/// #    }
343/// #
344/// #    fn set_config(&mut self, config: Self::ConfigType) -> Result<(), anyhow::Error> {
345/// #        Ok(())
346/// #    }
347/// #
348/// #    fn get_metrics(&mut self) -> impl IntoIterator<Item=Metric> {
349/// #        []
350/// #    }
351/// }
352///
353/// // require version 3.3.0 of the API
354/// plugin!(unsafe { 3;3;0 } => #[no_capabilities] MyPlugin);
355/// ```
356///
357/// **Note**: this does not affect the actual version supported in any way. If you use this form,
358/// it's **entirely your responsibility** to ensure the advertised version is compatible with the actual
359/// version supported by this crate.
360#[macro_export]
361macro_rules! plugin {
362    (unsafe { $maj:expr; $min:expr; $patch:expr } => #[no_capabilities] $ty:ty) => {
363        unsafe impl $crate::base::wrappers::BasePluginExported for $ty {}
364
365        $crate::base_plugin_ffi_wrappers!($maj; $min; $patch => #[unsafe(no_mangle)] $ty);
366    };
367    (unsafe { $maj:expr; $min:expr; $patch:expr } => $ty:ty) => {
368        plugin!(unsafe {$maj; $min; $patch} => #[no_capabilities] $ty);
369
370        $crate::ensure_plugin_capabilities!($ty);
371    };
372    ($(#[$attr:tt])? $ty:ty) => {
373        plugin!(
374            unsafe {
375                falco_plugin::api::PLUGIN_API_VERSION_MAJOR as usize;
376                falco_plugin::api::PLUGIN_API_VERSION_MINOR as usize;
377                0
378            } => $(#[$attr])? $ty
379        );
380    };
381}
382
383/// # Automatically generate the Falco plugin API structure for static plugins
384///
385/// This macro generates a [`falco_plugin_api::plugin_api`] structure, usable as a statically
386/// linked plugin. It automatically handles all supported capabilities, so you need just one
387/// invocation, regardless of how many capabilities your plugin supports.
388///
389/// ## Basic usage
390///
391/// ```
392///# use std::ffi::CStr;
393///# use falco_plugin::base::Metric;
394/// use falco_plugin::base::Plugin;
395/// use falco_plugin::static_plugin;
396///# use falco_plugin::tables::TablesInput;
397///
398///# struct MyPlugin;
399///#
400/// impl Plugin for MyPlugin {
401///     // ...
402///#     const NAME: &'static CStr = c"sample-plugin-rs";
403///#     const PLUGIN_VERSION: &'static CStr = c"0.0.1";
404///#     const DESCRIPTION: &'static CStr = c"A sample Falco plugin that does nothing";
405///#     const CONTACT: &'static CStr = c"you@example.com";
406///#     type ConfigType = ();
407///#
408///#     fn new(input: Option<&TablesInput>, config: Self::ConfigType)
409///#         -> Result<Self, anyhow::Error> {
410///#         Ok(MyPlugin)
411///#     }
412///#
413///#     fn set_config(&mut self, config: Self::ConfigType) -> Result<(), anyhow::Error> {
414///#         Ok(())
415///#     }
416///#
417///#     fn get_metrics(&mut self) -> impl IntoIterator<Item=Metric> {
418///#         []
419///#     }
420/// }
421///
422/// static_plugin!(#[no_capabilities] MY_PLUGIN_API = MyPlugin);
423/// ```
424///
425/// This expands to:
426/// ```ignore
427/// #[unsafe(no_mangle)]
428/// static MY_PLUGIN_API: falco_plugin::api::plugin_api = /* ... */;
429/// ```
430///
431/// The symbols referred to in the API structure are still mangled according to default Rust rules.
432///
433/// ## Overriding the supported API version
434///
435/// The macro also implements a form where you can override the required API version (for example,
436/// if you wish to advertise an older version for increased compatibility):
437///
438/// ```
439///# use std::ffi::CStr;
440///# use falco_plugin::base::Metric;
441/// use falco_plugin::base::Plugin;
442/// use falco_plugin::static_plugin;
443///# use falco_plugin::tables::TablesInput;
444///
445///# struct MyPlugin;
446///#
447/// impl Plugin for MyPlugin {
448///     // ...
449///#     const NAME: &'static CStr = c"sample-plugin-rs";
450///#     const PLUGIN_VERSION: &'static CStr = c"0.0.1";
451///#     const DESCRIPTION: &'static CStr = c"A sample Falco plugin that does nothing";
452///#     const CONTACT: &'static CStr = c"you@example.com";
453///#     type ConfigType = ();
454///#
455///#     fn new(input: Option<&TablesInput>, config: Self::ConfigType)
456///#         -> Result<Self, anyhow::Error> {
457///#         Ok(MyPlugin)
458///#     }
459///#
460///#     fn set_config(&mut self, config: Self::ConfigType) -> Result<(), anyhow::Error> {
461///#         Ok(())
462///#     }
463///#
464///#     fn get_metrics(&mut self) -> impl IntoIterator<Item=Metric> {
465///#         []
466///#     }
467/// }
468///
469/// // advertise API version 3.3.0
470/// static_plugin!(MY_PLUGIN_API @ unsafe { 3;3;0 } = #[no_capabilities] MyPlugin);
471/// ```
472///
473/// **Note**: this does not affect the actual version supported in any way. If you use this form,
474/// it's **entirely your responsibility** to ensure the advertised version is compatible with the actual
475/// version supported by this crate.
476#[macro_export]
477macro_rules! static_plugin {
478    ($(#[$attr:tt])? $vis:vis $name:ident = $ty:ty) => {
479        static_plugin!(
480            $vis $name @ unsafe {
481                falco_plugin::api::PLUGIN_API_VERSION_MAJOR as usize;
482                falco_plugin::api::PLUGIN_API_VERSION_MINOR as usize;
483                0
484            }
485            = $(#[$attr])? $ty
486        );
487    };
488    ($vis:vis $name:ident @ unsafe { $maj:expr; $min:expr; $patch:expr } = #[no_capabilities] $ty:ty) => {
489        #[unsafe(no_mangle)]
490        $vis static $name: falco_plugin::api::plugin_api = const {
491            $crate::base_plugin_ffi_wrappers!($maj; $min; $patch => #[deny(dead_code)] $ty);
492            __plugin_base_api()
493        };
494
495        // a static plugin automatically exports all capabilities
496        unsafe impl $crate::base::wrappers::BasePluginExported for $ty {}
497        unsafe impl $crate::async_event::wrappers::AsyncPluginExported for $ty {}
498        unsafe impl $crate::extract::wrappers::ExtractPluginExported for $ty {}
499        unsafe impl $crate::listen::wrappers::CaptureListenPluginExported for $ty {}
500        unsafe impl $crate::parse::wrappers::ParsePluginExported for $ty {}
501        unsafe impl $crate::source::wrappers::SourcePluginExported for $ty {}
502    };
503    ($vis:vis $name:ident @ unsafe { $maj:expr; $min:expr; $patch:expr } = $ty:ty) => {
504        static_plugin!($vis $name @ unsafe { $maj; $min; $patch } = #[no_capabilities] $ty);
505
506        $crate::ensure_plugin_capabilities!($ty);
507    }
508}
509
510#[doc(hidden)]
511#[macro_export]
512macro_rules! ensure_plugin_capabilities {
513    ($ty:ty) => {
514        const _: () = {
515            use $crate::async_event::wrappers::AsyncPluginFallbackApi;
516            use $crate::extract::wrappers::ExtractPluginFallbackApi;
517            use $crate::listen::wrappers::CaptureListenFallbackApi;
518            use $crate::parse::wrappers::ParsePluginFallbackApi;
519            use $crate::source::wrappers::SourcePluginFallbackApi;
520
521            let impls_async =
522                $crate::async_event::wrappers::AsyncPluginApi::<$ty>::IMPLEMENTS_ASYNC;
523            let impls_extract =
524                $crate::extract::wrappers::ExtractPluginApi::<$ty>::IMPLEMENTS_EXTRACT;
525            let impls_listen =
526                $crate::listen::wrappers::CaptureListenApi::<$ty>::IMPLEMENTS_LISTEN;
527            let impls_parse =
528                $crate::parse::wrappers::ParsePluginApi::<$ty>::IMPLEMENTS_PARSE;
529            let impls_source =
530                $crate::source::wrappers::SourcePluginApi::<$ty>::IMPLEMENTS_SOURCE;
531
532            assert!(
533                impls_async || impls_extract || impls_listen || impls_parse || impls_source,
534                "Plugin must implement at least one capability. If you really want a plugin without capabilities, use the #[no_capabilities] attribute"
535            );
536        };
537    };
538}
539
540#[doc(hidden)]
541#[macro_export]
542macro_rules! base_plugin_ffi_wrappers {
543    ($maj:expr; $min:expr; $patch:expr => #[$attr:meta] $ty:ty) => {
544        #[$attr]
545        pub extern "C" fn plugin_get_required_api_version() -> *const std::ffi::c_char {
546            $crate::base::wrappers::plugin_get_required_api_version::<
547                { $maj },
548                { $min },
549                { $patch },
550            >()
551        }
552
553        $crate::wrap_ffi! {
554            #[$attr]
555            use $crate::base::wrappers: <$ty>;
556
557            unsafe fn plugin_get_version() -> *const std::ffi::c_char;
558            unsafe fn plugin_get_name() -> *const std::ffi::c_char;
559            unsafe fn plugin_get_description() -> *const std::ffi::c_char;
560            unsafe fn plugin_get_contact() -> *const std::ffi::c_char;
561            unsafe fn plugin_get_init_schema(schema_type: *mut u32) -> *const std::ffi::c_char;
562            unsafe fn plugin_init(
563                args: *const falco_plugin::api::ss_plugin_init_input,
564                rc: *mut i32,
565            ) -> *mut falco_plugin::api::ss_plugin_t;
566            unsafe fn plugin_destroy(plugin: *mut falco_plugin::api::ss_plugin_t) -> ();
567            unsafe fn plugin_get_last_error(
568                plugin: *mut falco_plugin::api::ss_plugin_t,
569            ) -> *const std::ffi::c_char;
570            unsafe fn plugin_set_config(
571                plugin: *mut falco_plugin::api::ss_plugin_t,
572                config_input: *const falco_plugin::api::ss_plugin_set_config_input,
573            ) -> falco_plugin::api::ss_plugin_rc;
574            unsafe fn plugin_get_metrics(
575                plugin: *mut falco_plugin::api::ss_plugin_t,
576                num_metrics: *mut u32,
577            ) -> *mut falco_plugin::api::ss_plugin_metric;
578            unsafe fn plugin_get_required_event_schema_version(
579                plugin: *mut falco_plugin::api::ss_plugin_t
580            ) -> *const std::ffi::c_char;
581        }
582
583        #[allow(dead_code)]
584        pub const fn __plugin_base_api() -> falco_plugin::api::plugin_api {
585            use $crate::async_event::wrappers::AsyncPluginFallbackApi;
586            use $crate::extract::wrappers::ExtractPluginFallbackApi;
587            use $crate::listen::wrappers::CaptureListenFallbackApi;
588            use $crate::parse::wrappers::ParsePluginFallbackApi;
589            use $crate::source::wrappers::SourcePluginFallbackApi;
590            falco_plugin::api::plugin_api {
591                get_required_api_version: Some(plugin_get_required_api_version),
592                get_version: Some(plugin_get_version),
593                get_name: Some(plugin_get_name),
594                get_description: Some(plugin_get_description),
595                get_contact: Some(plugin_get_contact),
596                get_init_schema: Some(plugin_get_init_schema),
597                init: Some(plugin_init),
598                destroy: Some(plugin_destroy),
599                get_last_error: Some(plugin_get_last_error),
600                __bindgen_anon_1:
601                    $crate::source::wrappers::SourcePluginApi::<$ty>::SOURCE_API,
602                __bindgen_anon_2:
603                    $crate::extract::wrappers::ExtractPluginApi::<$ty>::EXTRACT_API,
604                __bindgen_anon_3:
605                    $crate::parse::wrappers::ParsePluginApi::<$ty>::PARSE_API,
606                __bindgen_anon_4:
607                    $crate::async_event::wrappers::AsyncPluginApi::<$ty>::ASYNC_API,
608                __bindgen_anon_5:
609                    $crate::listen::wrappers::CaptureListenApi::<$ty>::LISTEN_API,
610                set_config: Some(plugin_set_config),
611                get_metrics: Some(plugin_get_metrics),
612                get_required_event_schema_version: Some(plugin_get_required_event_schema_version),
613            }
614        }
615    };
616}
617
618pub(crate) struct ActualPlugin<P: Plugin> {
619    pub(crate) plugin: P,
620    pub(crate) last_error: LastError,
621}
622
623// TODO(sdk): convert this into traits?
624//       this may make it hard to make the lifetimes line up
625//       (will end up with multiple mutable references)
626#[doc(hidden)]
627#[allow(missing_debug_implementations)]
628pub struct PluginWrapper<P: Plugin> {
629    pub(crate) plugin: Option<ActualPlugin<P>>,
630    pub(crate) error_buf: CString,
631    pub(crate) field_storage: bumpalo::Bump,
632    pub(crate) string_storage: CString,
633    pub(crate) metric_storage: Vec<ss_plugin_metric>,
634}
635
636impl<P: Plugin> PluginWrapper<P> {
637    pub fn new(plugin: P, last_error: LastError) -> Self {
638        Self {
639            plugin: Some(ActualPlugin { plugin, last_error }),
640            error_buf: Default::default(),
641            field_storage: bumpalo::Bump::new(),
642            string_storage: Default::default(),
643            metric_storage: Default::default(),
644        }
645    }
646
647    pub fn new_error(err: impl Display) -> Self {
648        let mut plugin = Self {
649            plugin: None,
650            error_buf: Default::default(),
651            field_storage: bumpalo::Bump::new(),
652            string_storage: Default::default(),
653            metric_storage: vec![],
654        };
655
656        plugin
657            .error_buf
658            .write_into(|buf| write!(buf, "{err}"))
659            .unwrap_or_else(|err| panic!("Failed to write error message (was: {err})"));
660
661        plugin
662    }
663}