use urid::{Uri, UriBound};
mod cache;
mod core_features;
mod descriptor;
pub use cache::FeatureCache;
pub use core_features::*;
pub use descriptor::FeatureDescriptor;
use std::ffi::c_void;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ThreadingClass {
Discovery,
Instantiation,
Audio,
Other,
}
pub unsafe trait Feature: UriBound + Sized {
unsafe fn from_feature_ptr(feature: *const c_void, class: ThreadingClass) -> Option<Self>;
}
#[derive(Copy, Clone, Debug)]
pub struct MissingFeatureError {
pub(crate) uri: &'static Uri,
}
impl std::fmt::Display for MissingFeatureError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
let uri = self.uri.to_str().unwrap_or("[error while reading URI]");
write!(
f,
"Unable to instantiate plugin: missing required feature: {}",
uri
)
}
}
pub trait FeatureCollection<'a>: Sized + 'a {
fn from_cache(
cache: &mut FeatureCache<'a>,
class: ThreadingClass,
) -> Result<Self, MissingFeatureError>;
}
impl<'a> FeatureCollection<'a> for () {
#[inline]
fn from_cache(
_cache: &mut FeatureCache,
_: ThreadingClass,
) -> Result<Self, MissingFeatureError> {
Ok(())
}
}
#[cfg(test)]
#[allow(clippy::float_cmp)]
mod tests {
use crate::feature::FeatureCache;
use crate::{feature::*, plugin::*};
use std::ffi::c_void;
use std::os::raw::c_char;
use std::pin::Pin;
use urid::UriBound;
struct FeatureA<'a> {
number: &'a i32,
}
struct FeatureB<'a> {
number: &'a f32,
}
unsafe impl<'a> UriBound for FeatureA<'a> {
const URI: &'static [u8] = b"urn:lv2Feature:A\0";
}
unsafe impl<'a> Feature for FeatureA<'a> {
unsafe fn from_feature_ptr(feature: *const c_void, _: ThreadingClass) -> Option<Self> {
(feature as *const i32)
.as_ref()
.map(|number| Self { number })
}
}
unsafe impl<'a> UriBound for FeatureB<'a> {
const URI: &'static [u8] = b"urn:lv2Feature:B\0";
}
unsafe impl<'a> Feature for FeatureB<'a> {
unsafe fn from_feature_ptr(feature: *const c_void, _: ThreadingClass) -> Option<Self> {
(feature as *const f32)
.as_ref()
.map(|number| Self { number })
}
}
#[derive(FeatureCollection)]
struct Collection<'a> {
a: FeatureA<'a>,
b: FeatureB<'a>,
_c: crate::feature::IsLive,
}
struct FeatureTestSetting<'a> {
pub data_a: Pin<Box<i32>>,
pub feature_a_sys: Pin<Box<::sys::LV2_Feature>>,
pub data_b: Pin<Box<f32>>,
pub feature_b_sys: Pin<Box<::sys::LV2_Feature>>,
pub feature_c_sys: Pin<Box<::sys::LV2_Feature>>,
pub features_cache: FeatureCache<'a>,
}
impl<'a> FeatureTestSetting<'a> {
fn new() -> Self {
let mut data_a: Pin<Box<i32>> = Box::pin(42);
let feature_a_sys = Box::pin(::sys::LV2_Feature {
URI: FeatureA::URI.as_ptr() as *const c_char,
data: data_a.as_mut().get_mut() as *mut i32 as *mut c_void,
});
let mut data_b: Pin<Box<f32>> = Box::pin(17.0);
let feature_b_sys = Box::pin(::sys::LV2_Feature {
URI: FeatureB::URI.as_ptr() as *const c_char,
data: data_b.as_mut().get_mut() as *mut f32 as *mut c_void,
});
let feature_c_sys = Box::pin(::sys::LV2_Feature {
URI: crate::feature::IsLive::URI.as_ptr() as *const c_char,
data: std::ptr::null_mut(),
});
let features_list: &[*const sys::LV2_Feature] = &[
feature_a_sys.as_ref().get_ref(),
feature_b_sys.as_ref().get_ref(),
feature_c_sys.as_ref().get_ref(),
std::ptr::null(),
];
let features_cache = unsafe { FeatureCache::from_raw(features_list.as_ptr()) };
Self {
data_a,
feature_a_sys,
data_b,
feature_b_sys,
feature_c_sys,
features_cache,
}
}
}
#[test]
fn test_feature_cache() {
let setting = FeatureTestSetting::new();
let mut features_cache = setting.features_cache;
assert!(features_cache.contains::<FeatureA>());
assert!(features_cache.contains::<FeatureB>());
let retrieved_feature_a: FeatureA = features_cache
.retrieve_feature(ThreadingClass::Other)
.unwrap();
assert_eq!(*retrieved_feature_a.number, *(setting.data_a));
let retrieved_feature_b: FeatureB = features_cache
.retrieve_feature(ThreadingClass::Other)
.unwrap();
assert!(retrieved_feature_b.number - *(setting.data_b) < std::f32::EPSILON);
}
#[test]
fn test_feature_descriptor() {
let setting = FeatureTestSetting::new();
let features_cache = setting.features_cache;
let feature_descriptors: Vec<FeatureDescriptor> = features_cache.into_iter().collect();
assert_eq!(feature_descriptors.len(), 3);
let mut feature_a_found = false;
let mut feature_b_found = false;
for descriptor in feature_descriptors {
if descriptor.is_feature::<FeatureA>() {
if let Ok(retrieved_feature_a) =
descriptor.into_feature::<FeatureA>(ThreadingClass::Other)
{
assert!(*retrieved_feature_a.number == *(setting.data_a));
} else {
panic!("Feature interpretation failed!");
}
feature_a_found = true;
} else if descriptor.is_feature::<FeatureB>() {
if let Ok(retrieved_feature_b) =
descriptor.into_feature::<FeatureB>(ThreadingClass::Other)
{
assert_eq!(*retrieved_feature_b.number, *(setting.data_b));
} else {
panic!("Feature interpretation failed!");
}
feature_b_found = true;
} else if descriptor.is_feature::<crate::feature::IsLive>() {
if descriptor
.into_feature::<IsLive>(ThreadingClass::Other)
.is_err()
{
panic!("Feature interpretation failed!");
}
} else {
panic!("Invalid feature in feature iterator!");
}
}
assert!(feature_a_found && feature_b_found);
}
#[test]
fn test_feature_collection() {
let setting = FeatureTestSetting::new();
let mut features_cache = setting.features_cache;
let cache = Collection::from_cache(&mut features_cache, ThreadingClass::Other).unwrap();
assert_eq!(*cache.a.number, *setting.data_a);
assert_eq!(*cache.b.number, *setting.data_b);
}
}