1use urid::{Uri, UriBound};
3
4mod cache;
5mod core_features;
6mod descriptor;
7
8pub use cache::FeatureCache;
9pub use core_features::*;
10pub use descriptor::FeatureDescriptor;
11
12use std::ffi::c_void;
13
14#[derive(Clone, Copy, Debug, PartialEq, Eq)]
18pub enum ThreadingClass {
19 Discovery,
20 Instantiation,
21 Audio,
22 Other,
23}
24
25pub unsafe trait Feature: UriBound + Sized {
35 unsafe fn from_feature_ptr(feature: *const c_void, class: ThreadingClass) -> Option<Self>;
51}
52
53#[derive(Copy, Clone, Debug)]
55pub struct MissingFeatureError {
56 pub(crate) uri: &'static Uri,
57}
58
59impl std::fmt::Display for MissingFeatureError {
60 fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
61 let uri = self.uri.to_str().unwrap_or("[error while reading URI]");
62 write!(
63 f,
64 "Unable to instantiate plugin: missing required feature: {}",
65 uri
66 )
67 }
68}
69
70pub trait FeatureCollection<'a>: Sized + 'a {
87 fn from_cache(
89 cache: &mut FeatureCache<'a>,
90 class: ThreadingClass,
91 ) -> Result<Self, MissingFeatureError>;
92}
93
94impl<'a> FeatureCollection<'a> for () {
95 #[inline]
96 fn from_cache(
97 _cache: &mut FeatureCache,
98 _: ThreadingClass,
99 ) -> Result<Self, MissingFeatureError> {
100 Ok(())
101 }
102}
103
104#[cfg(test)]
105#[allow(clippy::float_cmp)]
106mod tests {
107 use crate::feature::FeatureCache;
108 use crate::{feature::*, plugin::*};
109 use std::ffi::c_void;
110 use std::os::raw::c_char;
111 use std::pin::Pin;
112 use urid::UriBound;
113
114 struct FeatureA<'a> {
115 number: &'a i32,
116 }
117
118 struct FeatureB<'a> {
119 number: &'a f32,
120 }
121
122 unsafe impl<'a> UriBound for FeatureA<'a> {
123 const URI: &'static [u8] = b"urn:lv2Feature:A\0";
124 }
125
126 unsafe impl<'a> Feature for FeatureA<'a> {
127 unsafe fn from_feature_ptr(feature: *const c_void, _: ThreadingClass) -> Option<Self> {
128 (feature as *const i32)
129 .as_ref()
130 .map(|number| Self { number })
131 }
132 }
133
134 unsafe impl<'a> UriBound for FeatureB<'a> {
135 const URI: &'static [u8] = b"urn:lv2Feature:B\0";
136 }
137
138 unsafe impl<'a> Feature for FeatureB<'a> {
139 unsafe fn from_feature_ptr(feature: *const c_void, _: ThreadingClass) -> Option<Self> {
140 (feature as *const f32)
141 .as_ref()
142 .map(|number| Self { number })
143 }
144 }
145
146 #[derive(FeatureCollection)]
147 struct Collection<'a> {
148 a: FeatureA<'a>,
149 b: FeatureB<'a>,
150 _c: crate::feature::IsLive,
151 }
152
153 struct FeatureTestSetting<'a> {
154 pub data_a: Pin<Box<i32>>,
155 pub feature_a_sys: Pin<Box<::sys::LV2_Feature>>,
156 pub data_b: Pin<Box<f32>>,
157 pub feature_b_sys: Pin<Box<::sys::LV2_Feature>>,
158 pub feature_c_sys: Pin<Box<::sys::LV2_Feature>>,
159 pub features_cache: FeatureCache<'a>,
160 }
161
162 impl<'a> FeatureTestSetting<'a> {
163 fn new() -> Self {
164 let mut data_a: Pin<Box<i32>> = Box::pin(42);
165 let feature_a_sys = Box::pin(::sys::LV2_Feature {
166 URI: FeatureA::URI.as_ptr() as *const c_char,
167 data: data_a.as_mut().get_mut() as *mut i32 as *mut c_void,
168 });
169
170 let mut data_b: Pin<Box<f32>> = Box::pin(17.0);
171 let feature_b_sys = Box::pin(::sys::LV2_Feature {
172 URI: FeatureB::URI.as_ptr() as *const c_char,
173 data: data_b.as_mut().get_mut() as *mut f32 as *mut c_void,
174 });
175
176 let feature_c_sys = Box::pin(::sys::LV2_Feature {
177 URI: crate::feature::IsLive::URI.as_ptr() as *const c_char,
178 data: std::ptr::null_mut(),
179 });
180
181 let features_list: &[*const sys::LV2_Feature] = &[
182 feature_a_sys.as_ref().get_ref(),
183 feature_b_sys.as_ref().get_ref(),
184 feature_c_sys.as_ref().get_ref(),
185 std::ptr::null(),
186 ];
187
188 let features_cache = unsafe { FeatureCache::from_raw(features_list.as_ptr()) };
190
191 Self {
192 data_a,
193 feature_a_sys,
194 data_b,
195 feature_b_sys,
196 feature_c_sys,
197 features_cache,
198 }
199 }
200 }
201
202 #[test]
203 fn test_feature_cache() {
204 let setting = FeatureTestSetting::new();
206 let mut features_cache = setting.features_cache;
207
208 assert!(features_cache.contains::<FeatureA>());
210 assert!(features_cache.contains::<FeatureB>());
211
212 let retrieved_feature_a: FeatureA = features_cache
213 .retrieve_feature(ThreadingClass::Other)
214 .unwrap();
215 assert_eq!(*retrieved_feature_a.number, *(setting.data_a));
216
217 let retrieved_feature_b: FeatureB = features_cache
218 .retrieve_feature(ThreadingClass::Other)
219 .unwrap();
220 assert!(retrieved_feature_b.number - *(setting.data_b) < std::f32::EPSILON);
221 }
222
223 #[test]
224 fn test_feature_descriptor() {
225 let setting = FeatureTestSetting::new();
227 let features_cache = setting.features_cache;
228
229 let feature_descriptors: Vec<FeatureDescriptor> = features_cache.into_iter().collect();
231
232 assert_eq!(feature_descriptors.len(), 3);
234
235 let mut feature_a_found = false;
236 let mut feature_b_found = false;
237 for descriptor in feature_descriptors {
238 if descriptor.is_feature::<FeatureA>() {
239 if let Ok(retrieved_feature_a) =
240 descriptor.into_feature::<FeatureA>(ThreadingClass::Other)
241 {
242 assert!(*retrieved_feature_a.number == *(setting.data_a));
243 } else {
244 panic!("Feature interpretation failed!");
245 }
246 feature_a_found = true;
247 } else if descriptor.is_feature::<FeatureB>() {
248 if let Ok(retrieved_feature_b) =
249 descriptor.into_feature::<FeatureB>(ThreadingClass::Other)
250 {
251 assert_eq!(*retrieved_feature_b.number, *(setting.data_b));
252 } else {
253 panic!("Feature interpretation failed!");
254 }
255 feature_b_found = true;
256 } else if descriptor.is_feature::<crate::feature::IsLive>() {
257 if descriptor
258 .into_feature::<IsLive>(ThreadingClass::Other)
259 .is_err()
260 {
261 panic!("Feature interpretation failed!");
262 }
263 } else {
264 panic!("Invalid feature in feature iterator!");
265 }
266 }
267 assert!(feature_a_found && feature_b_found);
268 }
269
270 #[test]
271 fn test_feature_collection() {
272 let setting = FeatureTestSetting::new();
274 let mut features_cache = setting.features_cache;
275
276 let cache = Collection::from_cache(&mut features_cache, ThreadingClass::Other).unwrap();
277 assert_eq!(*cache.a.number, *setting.data_a);
278 assert_eq!(*cache.b.number, *setting.data_b);
279 }
280}