use std::error::Error;
use std::ffi::CStr;
use std::fmt::{Display, Formatter};
use std::ptr::NonNull;
mod cache;
mod entry_provider;
mod loaded_entry;
#[cfg(feature = "libloading")]
mod library;
#[cfg(feature = "libloading")]
pub use library::LibraryEntry;
#[cfg(feature = "clack-plugin")]
mod clack_plugin;
#[cfg(test)]
#[allow(missing_docs)]
pub mod diva_stub;
use crate::entry::cache::CachedEntry;
use crate::factory::{Factory, plugin::PluginFactory};
pub use clack_common::entry::*;
use clack_common::factory::RawFactoryPointer;
use clack_common::utils::ClapVersion;
pub use entry_provider::EntryProvider;
#[derive(Clone)]
pub struct PluginEntry {
inner: PluginEntryInner,
}
#[derive(Clone)]
enum PluginEntryInner {
Cached(CachedEntry),
#[cfg(feature = "clack-plugin")]
FromClack(clack_plugin::ClackEntry),
}
impl PluginEntry {
#[cfg(feature = "libloading")]
pub unsafe fn load<P: AsRef<std::ffi::OsStr>>(path: P) -> Result<Self, PluginEntryError> {
use crate::entry::library::LibraryEntry;
use std::path::Path;
let path = Path::new(&path);
let (library_path, bundle_path) = LibraryEntry::resolve_path(path)?;
let library = LibraryEntry::load_from_path(library_path.as_ref())?;
Self::load_from(library, &bundle_path)
}
pub unsafe fn load_from(
provider: impl EntryProvider,
path: &CStr,
) -> Result<Self, PluginEntryError> {
let inner = cache::get_or_init(provider, path)?;
Ok(Self {
inner: PluginEntryInner::Cached(inner),
})
}
#[cfg(feature = "clack-plugin")]
pub fn load_from_clack<E: ::clack_plugin::entry::Entry>(
path: &CStr,
) -> Result<Self, PluginEntryError> {
let entry = E::new(Some(path)).map_err(|_| PluginEntryError::EntryInitFailed)?;
let inner = PluginEntryInner::FromClack(clack_plugin::ClackEntry::new(entry));
Ok(Self { inner })
}
#[inline]
pub unsafe fn load_from_raw(
inner: &'static EntryDescriptor,
plugin_path: &CStr,
) -> Result<Self, PluginEntryError> {
Self::load_from(inner, plugin_path)
}
#[inline]
pub fn raw_entry(&self) -> &EntryDescriptor {
match &self.inner {
PluginEntryInner::Cached(entry) => entry.as_ref(),
#[cfg(feature = "clack-plugin")]
PluginEntryInner::FromClack(_) => &clack_plugin::ClackEntry::DUMMY_DESCRIPTOR,
}
}
pub fn get_factory<'a, F: Factory<'a>>(&'a self) -> Option<F> {
let identifier = const { *F::IDENTIFIERS.first().unwrap() };
match &self.inner {
PluginEntryInner::Cached(entry) => {
let ptr = unsafe { entry.get().get_factory?(identifier.as_ptr()) };
let ptr = NonNull::new(ptr.cast_mut())?;
let ptr = unsafe { RawFactoryPointer::from_raw(ptr.cast()) };
Some(unsafe { F::from_raw(ptr) })
}
#[cfg(feature = "clack-plugin")]
PluginEntryInner::FromClack(clack) => clack.get_factory(),
}
}
#[inline]
pub fn get_plugin_factory(&self) -> Option<PluginFactory<'_>> {
self.get_factory()
}
#[inline]
pub fn version(&self) -> ClapVersion {
ClapVersion::from_raw(self.raw_entry().clap_version)
}
}
#[derive(Debug)]
#[non_exhaustive]
pub enum PluginEntryError {
#[cfg(feature = "libloading")]
LibraryLoadingError(libloading::Error),
#[cfg(feature = "libloading")]
InvalidNulPath(std::ffi::NulError),
Io(std::io::Error),
ResolveFailed,
NullEntryPointer,
IncompatibleClapVersion {
plugin_version: ClapVersion,
},
EntryInitFailed,
}
impl Error for PluginEntryError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
#[cfg(feature = "libloading")]
PluginEntryError::InvalidNulPath(e) => Some(e),
#[cfg(feature = "libloading")]
PluginEntryError::LibraryLoadingError(e) => Some(e),
PluginEntryError::Io(e) => Some(e),
_ => None,
}
}
}
impl Display for PluginEntryError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
PluginEntryError::EntryInitFailed => f.write_str("Plugin entry initialization failed"),
#[cfg(feature = "libloading")]
PluginEntryError::InvalidNulPath(e) => {
write!(f, "Invalid plugin descriptor path: {e}")
}
#[cfg(feature = "libloading")]
PluginEntryError::LibraryLoadingError(e) => {
write!(f, "Failed to load plugin descriptor library: {e}")
}
PluginEntryError::Io(e) => Display::fmt(e, f),
PluginEntryError::NullEntryPointer => f.write_str("Plugin entry pointer is null"),
PluginEntryError::ResolveFailed => f.write_str("Failed to resolve plugin entry path"),
PluginEntryError::IncompatibleClapVersion { plugin_version } => write!(
f,
"Incompatible CLAP version: plugin is v{}, host is v{}",
plugin_version,
ClapVersion::CURRENT
),
}
}
}
#[cfg(feature = "libloading")]
impl From<std::ffi::NulError> for PluginEntryError {
#[inline]
fn from(value: std::ffi::NulError) -> Self {
Self::InvalidNulPath(value)
}
}
impl From<std::io::Error> for PluginEntryError {
#[inline]
fn from(value: std::io::Error) -> Self {
Self::Io(value)
}
}