use clap_sys::plugin::clap_plugin_descriptor;
use clap_sys::version::{CLAP_VERSION, clap_version};
use std::ffi::{CStr, CString};
use std::marker::PhantomData;
use std::mem::ManuallyDrop;
use std::os::raw::c_char;
#[repr(C)]
#[derive(Clone)]
pub struct PluginDescriptor {
clap_version: clap_version,
id: OwnedCString,
name: OwnedCString,
vendor: OwnedCString,
url: OwnedCString,
manual_url: OwnedCString,
support_url: OwnedCString,
version: OwnedCString,
description: OwnedCString,
features: OwnedCStringArray,
}
impl PluginDescriptor {
#[inline]
pub fn new(id: &str, name: &str) -> Self {
Self {
clap_version: CLAP_VERSION,
id: OwnedCString::empty(),
name: OwnedCString::empty(),
vendor: OwnedCString::empty(),
url: OwnedCString::empty(),
manual_url: OwnedCString::empty(),
support_url: OwnedCString::empty(),
version: OwnedCString::empty(),
description: OwnedCString::empty(),
features: OwnedCStringArray::empty(),
}
.with_id(id)
.with_name(name)
}
pub const unsafe fn from_raw(raw: &clap_plugin_descriptor) -> &Self {
unsafe { &*(raw as *const clap_plugin_descriptor as *const Self) }
}
#[inline]
pub fn as_raw(&self) -> &clap_plugin_descriptor {
unsafe { &*(self as *const Self as *const clap_plugin_descriptor) }
}
#[inline]
pub fn id(&self) -> Option<&CStr> {
self.id.get()
}
pub fn with_id(mut self, id: &str) -> Self {
if id.is_empty() {
panic!("Plugin ID must not be blank!");
}
let id = CString::new(id).expect("Invalid Plugin ID");
self.id.set(Some(id));
self
}
#[inline]
pub fn name(&self) -> Option<&CStr> {
self.name.get()
}
pub fn with_name(mut self, name: &str) -> Self {
if name.is_empty() {
panic!("Plugin name must not be blank!");
}
let name = CString::new(name).expect("Invalid Plugin name");
self.name.set(Some(name));
self
}
#[inline]
pub fn vendor(&self) -> Option<&CStr> {
self.vendor.get()
}
pub fn with_vendor(mut self, vendor: &str) -> Self {
self.vendor.set_str(vendor);
self
}
#[inline]
pub fn url(&self) -> Option<&CStr> {
self.url.get()
}
pub fn with_url(mut self, url: &str) -> Self {
self.url.set_str(url);
self
}
#[inline]
pub fn manual_url(&self) -> Option<&CStr> {
self.manual_url.get()
}
pub fn with_manual_url(mut self, manual_url: &str) -> Self {
self.manual_url.set_str(manual_url);
self
}
#[inline]
pub fn support_url(&self) -> Option<&CStr> {
self.support_url.get()
}
pub fn with_support_url(mut self, support_url: &str) -> Self {
self.support_url.set_str(support_url);
self
}
#[inline]
pub fn version(&self) -> Option<&CStr> {
self.version.get()
}
pub fn with_version(mut self, version: &str) -> Self {
self.version.set_str(version);
self
}
#[inline]
pub fn description(&self) -> Option<&CStr> {
self.description.get()
}
pub fn with_description(mut self, description: &str) -> Self {
self.description.set_str(description);
self
}
#[inline]
pub fn features(&self) -> FeaturesIter<'_> {
self.features.get()
}
pub fn with_features<'a>(mut self, features: impl IntoIterator<Item = &'a CStr>) -> Self {
self.features.set(features);
self
}
}
const _: () = {
assert!(align_of::<clap_plugin_descriptor>() == align_of::<PluginDescriptor>());
assert!(size_of::<clap_plugin_descriptor>() == size_of::<PluginDescriptor>());
};
static EMPTY: &CStr = c"";
#[repr(C)]
struct OwnedCString(*const c_char);
unsafe impl Send for OwnedCString {}
unsafe impl Sync for OwnedCString {}
impl OwnedCString {
#[inline]
pub fn new(string: Option<CString>) -> Self {
let mut new = Self::empty();
new.set(string);
new
}
#[inline]
fn into_raw(self) -> *const c_char {
let s = ManuallyDrop::new(self);
s.0
}
#[inline]
unsafe fn from_raw(ptr: *const c_char) -> Self {
Self(ptr)
}
#[inline]
pub const fn null() -> Self {
Self(std::ptr::null())
}
#[inline]
pub const fn empty() -> Self {
Self(EMPTY.as_ptr())
}
#[inline]
pub fn get(&self) -> Option<&CStr> {
if Self::is_allocated(self.0) {
let str = unsafe { CStr::from_ptr(self.0) };
if str.is_empty() {
return None;
}
Some(str)
} else {
None
}
}
#[inline]
pub fn set(&mut self, new: Option<CString>) {
let old_ptr = core::mem::replace(&mut self.0, EMPTY.as_ptr());
unsafe { Self::deallocate(old_ptr) };
if let Some(new) = new {
if !new.is_empty() {
self.0 = new.into_raw();
}
}
}
#[inline]
pub fn set_str(&mut self, string: &str) {
if string.is_empty() {
self.set(None);
return;
}
let string = CString::new(string).expect("Invalid plugin descriptor string.");
self.set(Some(string));
}
#[inline]
fn is_allocated(ptr: *const c_char) -> bool {
!ptr.is_null() && ptr != EMPTY.as_ptr()
}
#[inline]
unsafe fn deallocate(old_ptr: *const c_char) {
if Self::is_allocated(old_ptr) {
let _ = unsafe { CString::from_raw(old_ptr.cast_mut()) };
}
}
}
impl Clone for OwnedCString {
#[inline]
fn clone(&self) -> Self {
Self::new(self.get().map(|s| s.to_owned()))
}
}
impl Drop for OwnedCString {
#[inline]
fn drop(&mut self) {
let old_ptr = core::mem::replace(&mut self.0, EMPTY.as_ptr());
unsafe { Self::deallocate(old_ptr) };
}
}
#[repr(C)]
struct OwnedCStringArray(*const *const c_char);
unsafe impl Send for OwnedCStringArray {}
unsafe impl Sync for OwnedCStringArray {}
static EMPTY_FEATURES: &[OwnedCString; 1] = &[OwnedCString::null()];
impl OwnedCStringArray {
#[inline]
fn is_allocated(ptr: *const *const c_char) -> bool {
!ptr.is_null() && ptr != empty_features()
}
#[inline]
pub const fn empty() -> Self {
Self(empty_features())
}
#[inline]
pub const fn get(&self) -> FeaturesIter<'_> {
FeaturesIter {
ptr: CArrayIter(self.0),
_marker: PhantomData,
}
}
#[inline]
pub fn set<'a>(&mut self, features: impl IntoIterator<Item = &'a CStr>) {
let old_ptr = core::mem::replace(&mut self.0, empty_features());
unsafe { Self::deallocate(old_ptr) };
self.0 = Self::allocate(features).cast::<*const c_char>();
}
fn allocate<'a>(features: impl IntoIterator<Item = &'a CStr>) -> *const OwnedCString {
let mut strings = Vec::with_capacity(1);
for feature in features {
strings.push(OwnedCString::new(Some(feature.to_owned())).into_raw());
}
if strings.is_empty() {
return EMPTY_FEATURES.as_ptr().cast_mut();
}
strings.push(core::ptr::null());
let strings = strings.into_boxed_slice();
let strings = Box::into_raw(strings);
strings.cast()
}
unsafe fn deallocate(ptr: *const *const c_char) {
if !Self::is_allocated(ptr) {
return;
}
let mut len = 1;
for str in unsafe { CArrayIter::new(ptr) } {
let _ = unsafe { OwnedCString::from_raw(str) };
len += 1;
}
let _ = unsafe { Box::from_raw(core::ptr::slice_from_raw_parts_mut(ptr.cast_mut(), len)) };
}
}
impl Clone for OwnedCStringArray {
fn clone(&self) -> Self {
let mut new = Self::empty();
new.set(self.get());
new
}
}
impl Drop for OwnedCStringArray {
#[inline]
fn drop(&mut self) {
let old_ptr = core::mem::replace(&mut self.0, empty_features());
unsafe { Self::deallocate(old_ptr) };
}
}
const fn empty_features() -> *const *const c_char {
EMPTY_FEATURES.as_ptr().cast()
}
pub struct FeaturesIter<'a> {
ptr: CArrayIter,
_marker: PhantomData<&'a OwnedCStringArray>,
}
impl<'a> Iterator for FeaturesIter<'a> {
type Item = &'a CStr;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
let next = self.ptr.next()?;
if next.is_null() {
return None;
}
Some(unsafe { CStr::from_ptr(next) })
}
}
struct CArrayIter(*const *const c_char);
impl CArrayIter {
#[inline]
unsafe fn new(ptr: *const *const c_char) -> Self {
Self(ptr)
}
}
impl Iterator for CArrayIter {
type Item = *const c_char;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
let current = *unsafe { self.0.as_ref() }?;
if current.is_null() {
return None;
}
self.0 = self.0.wrapping_add(1);
Some(current)
}
}