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