use crate::extensions::wrapper::handle_panic;
use crate::factory::{Factory, FactoryImplementation};
use std::error::Error;
use std::ffi::{CStr, c_void};
use std::fmt::{Display, Formatter};
use std::hint::unreachable_unchecked;
use std::panic::{AssertUnwindSafe, UnwindSafe};
use std::ptr::NonNull;
use std::sync::Mutex;
pub use clack_common::entry::*;
mod single;
pub use single::{DefaultPluginFactory, SinglePluginEntry};
pub mod prelude {
pub use crate::{
entry::{Entry, EntryDescriptor, EntryFactories, EntryLoadError, SinglePluginEntry},
factory::{
Factory,
plugin::{PluginFactoryImpl, PluginFactoryWrapper},
},
host::HostInfo,
plugin::{PluginDescriptor, PluginInstance},
};
}
pub trait Entry: Sized + Send + Sync + 'static {
fn new(bundle_path: Option<&CStr>) -> Result<Self, EntryLoadError>;
fn declare_factories<'a>(&'a self, builder: &mut EntryFactories<'a>);
}
#[derive(Copy, Clone, Debug)]
pub struct EntryLoadError;
impl Display for EntryLoadError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.write_str("Entry failed to load.")
}
}
impl Error for EntryLoadError {}
#[macro_export]
macro_rules! clack_export_entry {
($entry_type:ty, $entry_lambda:expr) => {
#[allow(non_upper_case_globals, missing_docs)]
#[allow(unsafe_code)]
#[allow(warnings, unused)]
#[unsafe(no_mangle)]
pub static clap_entry: $crate::entry::EntryDescriptor =
$crate::clack_entry!($entry_type, $entry_lambda);
};
($entry_type:ty) => {
#[allow(non_upper_case_globals, missing_docs)]
#[allow(unsafe_code)]
#[allow(warnings, unused)]
#[unsafe(no_mangle)]
pub static clap_entry: $crate::entry::EntryDescriptor = $crate::clack_entry!($entry_type);
};
}
#[macro_export]
macro_rules! clack_entry {
($entry_type:ty, $entry_lambda:expr) => {
({
#[allow(unsafe_code)]
const fn _entry() -> $crate::entry::EntryDescriptor {
static HOLDER: $crate::entry::EntryHolder<$entry_type> =
$crate::entry::EntryHolder::new();
unsafe extern "C" fn init(plugin_path: *const ::core::ffi::c_char) -> bool {
HOLDER.init_with(plugin_path, $entry_lambda)
}
unsafe extern "C" fn deinit() {
HOLDER.de_init()
}
unsafe extern "C" fn get_factory(
identifier: *const ::core::ffi::c_char,
) -> *const ::core::ffi::c_void {
HOLDER.get_factory_with(identifier, $entry_lambda)
}
$crate::entry::EntryDescriptor {
clap_version: $crate::utils::ClapVersion::CURRENT.to_raw(),
init: Some(init),
deinit: Some(deinit),
get_factory: Some(get_factory),
}
}
_entry()
})
};
($entry_type:ty) => {
({
#[allow(unsafe_code)]
const fn _entry() -> $crate::entry::EntryDescriptor {
static HOLDER: $crate::entry::EntryHolder<$entry_type> =
$crate::entry::EntryHolder::new();
unsafe extern "C" fn init(plugin_path: *const ::core::ffi::c_char) -> bool {
HOLDER.init(plugin_path)
}
unsafe extern "C" fn deinit() {
HOLDER.de_init()
}
unsafe extern "C" fn get_factory(
identifier: *const ::core::ffi::c_char,
) -> *const ::core::ffi::c_void {
HOLDER.get_factory(identifier)
}
$crate::entry::EntryDescriptor {
clap_version: $crate::utils::ClapVersion::CURRENT.to_raw(),
init: Some(init),
deinit: Some(deinit),
get_factory: Some(get_factory),
}
}
_entry()
})
};
}
pub struct EntryFactories<'a> {
found: Option<NonNull<c_void>>,
requested: &'a [&'a CStr],
}
impl<'a> EntryFactories<'a> {
#[doc(hidden)]
#[inline]
pub fn new(requested: &'a [&'a CStr]) -> Self {
Self {
found: None,
requested,
}
}
#[doc(hidden)]
#[inline]
pub fn found(&self) -> Option<NonNull<c_void>> {
self.found
}
pub fn register_factory<F: FactoryImplementation<'a>>(&mut self, factory: &'a F) -> &mut Self {
if self.found.is_some() {
return self;
}
for requested in self.requested {
if F::Factory::IDENTIFIERS.contains(requested) {
self.found = Some(factory.wrapper().as_raw().as_raw().cast());
break;
}
}
self
}
}
enum EntryHolderInner<E> {
Initialized { reference_count: usize, entry: E },
Uninitialized,
}
#[doc(hidden)]
pub struct EntryHolder<E: Entry> {
inner: Mutex<EntryHolderInner<E>>,
}
use crate::entry::EntryHolderInner::*;
#[doc(hidden)]
impl<E: Entry> EntryHolder<E> {
#[allow(clippy::new_without_default)] pub const fn new() -> Self {
Self {
inner: Mutex::new(Uninitialized),
}
}
#[inline]
pub unsafe fn init(&self, plugin_path: *const core::ffi::c_char) -> bool {
self.init_with(plugin_path, |p| E::new(p))
}
pub unsafe fn init_with(
&self,
plugin_path: *const core::ffi::c_char,
entry_factory: impl FnOnce(Option<&CStr>) -> Result<E, EntryLoadError> + UnwindSafe,
) -> bool {
let Ok(Ok(mut inner)) = handle_panic(|| self.inner.lock()) else {
return false;
};
match &mut *inner {
Initialized {
reference_count, ..
} => {
*reference_count += 1;
true
}
Uninitialized => {
let plugin_path = CStr::from_ptr(plugin_path);
let plugin_path = (!plugin_path.is_empty()).then_some(plugin_path);
let entry = handle_panic(|| entry_factory(plugin_path));
if let Ok(Ok(entry)) = entry {
*inner = Initialized {
entry,
reference_count: 1,
};
true
} else {
false
}
}
}
}
pub unsafe fn de_init(&self) {
let Ok(mut inner) = self.inner.lock() else {
return;
};
if let Initialized {
reference_count, ..
} = &mut *inner
{
if *reference_count > 1 {
*reference_count -= 1;
} else {
let _ = handle_panic(AssertUnwindSafe(|| *inner = Uninitialized));
}
}
}
#[inline]
pub unsafe fn get_factory(&self, identifier: *const core::ffi::c_char) -> *const c_void {
self.get_factory_with(identifier, |p| E::new(p))
}
pub unsafe fn get_factory_with(
&self,
identifier: *const core::ffi::c_char,
entry_factory: impl FnOnce(Option<&CStr>) -> Result<E, EntryLoadError> + UnwindSafe,
) -> *const c_void {
if identifier.is_null() {
return core::ptr::null();
}
let Ok(mut inner) = self.inner.lock() else {
return core::ptr::null();
};
let entry = match &*inner {
Initialized { entry, .. } => entry,
Uninitialized => {
let entry = handle_panic(|| entry_factory(None));
if let Ok(Ok(entry)) = entry {
*inner = Initialized {
entry,
reference_count: 1,
};
let Initialized { entry, .. } = &*inner else {
unsafe {
unreachable_unchecked();
}
};
entry
} else {
return core::ptr::null();
}
}
};
let identifier = CStr::from_ptr(identifier);
let result = handle_panic(AssertUnwindSafe(|| {
let identifiers = &[identifier];
let mut builder = EntryFactories::new(identifiers);
entry.declare_factories(&mut builder);
builder.found()
}));
match result {
Ok(Some(found)) => found.as_ptr(),
_ => core::ptr::null(),
}
}
}