Skip to main content

clack_plugin/factory/
wrapper.rs

1use crate::extensions::wrapper::handle_panic;
2use crate::factory::error::FactoryWrapperError;
3use clack_common::factory::RawFactoryPointer;
4use std::panic::AssertUnwindSafe;
5use std::ptr::NonNull;
6
7/// A wrapper around a `clack` factory of a given `F` type, as well as its `Raw` CLAP representation.
8///
9/// This wrapper allows to safely create a CLAP-compatible factory pointer, as well as soundly
10/// casting that pointer back to this wrapper's type, allowing to access the `F` type using the
11/// [`handle`](FactoryWrapper::handle) function.
12#[repr(C)]
13pub struct FactoryWrapper<Raw, F> {
14    raw: Raw,
15    inner: F,
16}
17
18impl<Raw, F> FactoryWrapper<Raw, F> {
19    /// Creates a new factory wrapper from a `Raw` CLAP type instance and an associated instance of `F`.
20    #[inline]
21    pub const fn new(raw: Raw, inner: F) -> Self {
22        Self { raw, inner }
23    }
24
25    /// Returns a raw factory pointer compatible with this wrapper's `Raw` CLAP representation
26    #[inline]
27    pub fn as_raw(&self) -> RawFactoryPointer<'_, Raw> {
28        // Note that we borrow all of `Self` here, not just the raw field.
29        // This keeps the borrow alive and valid for the whole lifetime of the resulting factory pointer,
30        // which means that when casting back to `Self` we can still soundly access all of `F` as well.
31        let self_ptr: NonNull<Self> = self.into();
32
33        // SAFETY: This pointer comes from a reference, so it is always valid for the matching lifetime
34        unsafe { RawFactoryPointer::from_raw(self_ptr.cast()) }
35    }
36
37    /// Returns a raw pointer to the `Raw` CLAP representation contained in this wrapper.
38    #[inline]
39    pub fn as_raw_ptr(&self) -> *const Raw {
40        self.as_raw().as_raw().as_ptr()
41    }
42
43    /// Returns a shared reference to the `F` factory instance.
44    #[inline]
45    pub const fn factory(&self) -> &F {
46        &self.inner
47    }
48
49    /// Provides a shared reference to the `F` factory instance to the given handler closure.
50    ///
51    /// Besides providing a reference, this function does a few extra safety checks:
52    ///
53    /// * The given `Raw` factory pointer is null-checked;
54    /// * The handler is wrapped in [`std::panic::catch_unwind`];
55    /// * Any [`FactoryWrapperError`] returned by the handler is caught.
56    ///
57    /// If any of the above safety check fails, an error message is logged through stderr (as CLAP
58    /// logging facilities are not available at the factory stage).
59    ///
60    /// Note that some safety checks (e.g. the `Raw` pointer null-checks) may result in the
61    /// closure never being called, and an error being returned only. Users of this function must
62    /// not rely on the completion of this closure for safety, and must handle this function
63    /// returning `None` gracefully.
64    ///
65    /// If all goes well, the return value of the handler closure is forwarded and returned by this
66    /// function.
67    ///
68    /// # Errors
69    ///
70    /// If any safety check failed, or any error or panic occurred inside the handler closure, this
71    /// function returns `None`, and the error message is logged.
72    ///
73    /// # Safety
74    ///
75    /// The `raw` pointer must be created by the [`as_raw`](Self::as_raw) method, from an instance that
76    /// is still valid for the duration of the `handler` call.
77    ///
78    /// While this function does a couple of simple safety checks, only a few common
79    /// cases are actually covered (i.e. null checks), and those **must not** be relied upon: those
80    /// checks only exist to help debugging faulty hosts.
81    ///
82    /// # Example
83    ///
84    /// This is the implementation of the [`plugin_count`](crate::factory::plugin::PluginFactoryImpl::plugin_count)
85    /// callback's C wrapper.
86    ///
87    /// ```
88    /// use clap_sys::factory::plugin_factory::clap_plugin_factory;
89    /// use clack_plugin::plugin::{Plugin, PluginMainThread};
90    /// use clack_plugin::factory::{FactoryWrapper, plugin::PluginFactoryImpl};
91    ///
92    /// unsafe extern "C" fn get_plugin_count<F: PluginFactoryImpl>(factory: *const clap_plugin_factory) -> u32 {
93    ///    FactoryWrapper::<clap_plugin_factory, F>::handle(factory, |factory| {
94    ///        Ok(factory.plugin_count())
95    ///    })
96    ///    .unwrap_or(0)
97    /// }
98    /// ```
99    #[must_use]
100    pub unsafe fn handle<T>(
101        raw: *const Raw,
102        handler: impl FnOnce(&F) -> Result<T, FactoryWrapperError>,
103    ) -> Option<T> {
104        // SAFETY: The caller ensures this pointer comes from the reference in as_raw
105        let factory = unsafe { raw.cast::<Self>().as_ref() };
106        let result = factory.ok_or(FactoryWrapperError::NullFactoryInstance);
107
108        let result = result.and_then(|f| f.handle_panic(handler));
109
110        Self::handle_result(result)
111    }
112
113    #[inline]
114    fn handle_panic<T>(
115        &self,
116        handler: impl FnOnce(&F) -> Result<T, FactoryWrapperError>,
117    ) -> Result<T, FactoryWrapperError> {
118        match handle_panic(AssertUnwindSafe(|| handler(&self.inner))) {
119            Err(_) => Err(FactoryWrapperError::Panic),
120            Ok(Err(e)) => Err(e),
121            Ok(Ok(val)) => Ok(val),
122        }
123    }
124
125    #[inline]
126    fn handle_result<T>(result: Result<T, FactoryWrapperError>) -> Option<T> {
127        match result {
128            Ok(value) => Some(value),
129            Err(e) => {
130                eprintln!("[CLAP_PLUGIN_FACTORY_ERROR] {e}");
131
132                None
133            }
134        }
135    }
136}