Skip to content

Commit efbd941

Browse files
committed
Broken WIP (waiting on atom-soundness)
1 parent 97a49c1 commit efbd941

File tree

13 files changed

+392
-371
lines changed

13 files changed

+392
-371
lines changed

buf-size/src/options.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,18 @@ macro_rules! make_option {
1717
const URI: &'static [u8] = $uri;
1818
}
1919

20-
impl<'a> lv2_options::OptionType<'a> for $name {
20+
impl lv2_options::OptionType for $name {
2121
type AtomType = lv2_atom::scalar::Int;
2222

2323
#[inline]
24-
fn from_option_value(
24+
fn from_option_value<'a>(
2525
value: <lv2_atom::scalar::Int as Atom<'a, 'a>>::ReadHandle,
2626
) -> Option<Self> {
2727
Some(Self((*value)))
2828
}
2929

3030
#[inline]
31-
fn as_option_value(&'a self) -> <lv2_atom::scalar::Int as Atom<'a, 'a>>::ReadHandle {
31+
fn as_option_value<'a>(&'a self) -> <lv2_atom::scalar::Int as Atom<'a, 'a>>::ReadHandle {
3232
&self.0
3333
}
3434
}

options/src/collection.rs

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
use urid::{URIDCollection, Map};
2+
use crate::request::OptionRequestList;
3+
use crate::{OptionsError, OptionValue};
4+
use crate::list::OptionsList;
5+
use crate::option::request::OptionRequest;
6+
7+
pub trait OptionsCollection: Sized {
8+
type Serializer;
9+
10+
#[inline]
11+
fn new_serializer<'a, M: Map + ?Sized>(map: &M) -> Option<OptionsSerializer<Self>>
12+
where Self::Serializer: OptionsSerializationContext<'a, Self> { // FIXME
13+
Some(OptionsSerializer { inner: Self::Serializer::from_map(map)? })
14+
}
15+
}
16+
17+
#[doc(hidden)]
18+
pub mod implementation {
19+
use crate::{OptionType, OptionsError, OptionValue};
20+
use std::marker::PhantomData;
21+
use urid::{URID, URIDCollection, Map};
22+
use crate::collection::{OptionsSerializationContext, OptionsCollection};
23+
use crate::option::request::OptionRequest;
24+
use lv2_atom::{Atom, BackAsSpace};
25+
use lv2_atom::scalar::ScalarAtom;
26+
27+
pub struct OptionTypeSerializationContext<O: OptionType> {
28+
option_urid: URID<O>,
29+
option_type_atom_urid: URID<O::AtomType>
30+
}
31+
32+
impl<'a, O: OptionType> OptionsCollection for O
33+
where <O as OptionType>::AtomType: BackAsSpace<'a>,
34+
<<O as OptionType>::AtomType as Atom<'a, 'a>>::ReadParameter: Default {
35+
type Serializer = OptionTypeSerializationContext<O>;
36+
}
37+
38+
impl<O: OptionType> URIDCollection for OptionTypeSerializationContext<O> {
39+
#[inline]
40+
fn from_map<M: Map + ?Sized>(map: &M) -> Option<Self> {
41+
Some(Self {
42+
option_urid: map.populate_collection()?,
43+
option_type_atom_urid: map.populate_collection()?,
44+
})
45+
}
46+
}
47+
48+
impl<'a, O: OptionType> OptionsSerializationContext<'a, O> for OptionTypeSerializationContext<O>
49+
where <O as OptionType>::AtomType: BackAsSpace<'a>,
50+
<<O as OptionType>::AtomType as Atom<'a, 'a>>::ReadParameter: Default {
51+
#[inline]
52+
fn deserialize_new(&self, option: &'a OptionValue) -> Option<O> {
53+
option.read(self.option_urid, self.option_type_atom_urid, Default::default())
54+
}
55+
56+
fn deserialize_to(&self, options: &mut O, option: &OptionValue) -> Result<(), OptionsError> {
57+
todo!()
58+
}
59+
60+
fn respond_to_request<'r>(&self, options: &'r O, requests: &'r mut OptionRequest) -> Result<(), OptionsError> {
61+
todo!()
62+
}
63+
}
64+
}
65+
66+
pub struct OptionsSerializer<T: OptionsCollection> {
67+
inner: T::Serializer
68+
}
69+
70+
impl<T: OptionsCollection> OptionsSerializer<T> {
71+
pub fn deserialize_new(&self, list: &OptionsList) -> Option<T> {
72+
todo!()
73+
}
74+
75+
pub fn deserialize_to(&self, options: &mut T, list: &OptionsList) -> Result<(), OptionsError> {
76+
todo!()
77+
}
78+
79+
pub fn respond_to_requests<'a>(&self, options: &T, requests: &mut OptionRequestList) -> Result<(), OptionsError> {
80+
todo!()
81+
}
82+
}
83+
84+
pub trait OptionsSerializationContext<'a, T: OptionsCollection>: URIDCollection {
85+
fn deserialize_new(&self, option: &'a OptionValue) -> Option<T>;
86+
87+
fn deserialize_to(&self, options: &mut T, option: &OptionValue) -> Result<(), OptionsError>;
88+
89+
fn respond_to_request<'r>(&self, options: &'r T, request: &'r mut OptionRequest) -> Result<(), OptionsError>;
90+
}

options/src/extensions.rs

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! Contains the [`OptionsInterface`](crate::extensions::OptionsInterface) extension interface.
22
use crate::features::OptionsList;
3-
use crate::{OptionsError, OptionsWriter};
3+
use crate::OptionsError;
44
use lv2_core::feature::Feature;
55
use lv2_core::plugin::PluginInstance;
66
use lv2_core::prelude::Plugin;
@@ -9,6 +9,7 @@ use std::ffi::c_void;
99
use std::marker::PhantomData;
1010
use std::panic::AssertUnwindSafe;
1111
use urid::UriBound;
12+
use crate::option::request::OptionRequestList;
1213

1314
/// An interface to allow dynamically setting options from the host.
1415
///
@@ -18,11 +19,10 @@ use urid::UriBound;
1819
///
1920
/// ```
2021
/// # use lv2_core::prelude::*;
21-
/// # use lv2_options::{OptionType, OptionsWriter, OptionsError, Subject};
22-
/// # use lv2_options::features::OptionsList;
23-
/// # use lv2_options::extensions::{OptionsDescriptor, OptionsInterface};
22+
/// # use lv2_options::OptionType;
23+
/// # use lv2_options::prelude::*;
2424
/// #
25-
/// # use urid::{URID, Uri};
25+
/// # use urid::{URID, Uri, URIDCollection, uri, Map, UriBound};
2626
/// # use std::any::Any;
2727
/// #
2828
/// # impl<'a> OptionType<'a> for SomeIntOption {
@@ -111,7 +111,7 @@ pub trait OptionsInterface: Plugin {
111111
/// If the given options are unknown or somehow invalid, the appropriate [`OptionsError`] is returned.
112112
///
113113
/// See the documentation for the [`OptionsInterface`] type for an example of how to implement this method.
114-
fn get(&self, writer: OptionsWriter) -> Result<(), OptionsError>;
114+
fn get(&self, requests: OptionRequestList) -> Result<(), OptionsError>;
115115

116116
/// Allows the host to set the plugin's values for the given options.
117117
///
@@ -121,6 +121,7 @@ pub trait OptionsInterface: Plugin {
121121
fn set(&mut self, options: OptionsList) -> Result<(), OptionsError>;
122122
}
123123

124+
/// The Extension Descriptor associated to [`OptionsInterface`].
124125
pub struct OptionsDescriptor<P: OptionsInterface> {
125126
plugin: PhantomData<P>,
126127
}
@@ -140,16 +141,14 @@ impl<P: OptionsInterface> OptionsDescriptor<P> {
140141
}
141142
.plugin_handle();
142143

143-
let options =
144-
match OptionsList::from_feature_ptr(options_list.cast(), ThreadingClass::Instantiation)
145-
{
146-
Some(options) => options,
147-
None => return lv2_sys::LV2_Options_Status_LV2_OPTIONS_ERR_UNKNOWN,
148-
};
144+
let options_list = match options_list.as_mut() {
145+
Some(options_list) => options_list,
146+
None => return lv2_sys::LV2_Options_Status_LV2_OPTIONS_ERR_UNKNOWN,
147+
};
149148

150-
let writer = OptionsWriter::new(options);
149+
let requests = OptionRequestList::from_mut(options_list);
151150

152-
match std::panic::catch_unwind(AssertUnwindSafe(|| instance.get(writer))) {
151+
match std::panic::catch_unwind(AssertUnwindSafe(|| instance.get(requests))) {
153152
Ok(r) => OptionsError::result_into_raw(r),
154153
Err(_) => lv2_sys::LV2_Options_Status_LV2_OPTIONS_ERR_UNKNOWN,
155154
}

options/src/lib.rs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,25 @@
1717
//! See the [LV2 Options documentation](http://lv2plug.in/ns/ext/options) for more information.
1818
1919
pub use option::error::OptionsError;
20+
pub use option::OptionType;
21+
pub use option::request;
2022
pub use option::subject::Subject;
2123
pub use option::value::OptionValue;
22-
pub use option::writer::{OptionsProcessor, OptionsWriter};
23-
pub use option::OptionType;
2424

2525
pub mod extensions;
2626
mod option;
27+
pub mod list;
28+
pub mod collection;
2729

2830
/// Contains the [`OptionsList`](features::OptionsList) feature.
2931
pub mod features {
30-
pub use crate::option::list::OptionsList;
32+
pub use crate::list::OptionsList;
3133
}
34+
35+
/// Prelude of `lv2_options` for wildcard usage.
36+
pub mod prelude {
37+
pub use crate::list::OptionsList;
38+
pub use crate::OptionsError;
39+
pub use crate::Subject;
40+
pub use crate::extensions::{OptionsDescriptor, OptionsInterface};
41+
}

options/src/list.rs

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
use crate::OptionValue;
2+
use core::ffi::c_void;
3+
use lv2_core::feature::{Feature, ThreadingClass};
4+
use urid::UriBound;
5+
6+
/// A read-only list of [`OptionValue`]s, sent from the host.
7+
///
8+
/// This list type doesn't own the contained values: they are simply borrowed from the host.
9+
/// Cloning this struct only clones the list, not its contents.
10+
#[derive(Copy, Clone)]
11+
pub struct OptionsList<'a> {
12+
options_list: &'a lv2_sys::LV2_Options_Option,
13+
}
14+
15+
impl<'f> OptionsList<'f> {
16+
/// Returns an iterator over the slice.
17+
#[inline]
18+
pub fn iter(&self) -> OptionsListIter<'f> {
19+
OptionsListIter { current: self.options_list }
20+
}
21+
}
22+
23+
unsafe impl<'a> UriBound for OptionsList<'a> {
24+
const URI: &'static [u8] = lv2_sys::LV2_OPTIONS__options;
25+
}
26+
27+
unsafe impl<'a> Feature for OptionsList<'a> {
28+
#[inline]
29+
unsafe fn from_feature_ptr(feature: *const c_void, _class: ThreadingClass) -> Option<Self> {
30+
// SAFETY: Type and contents of feature data pointer is guaranteed by caller
31+
Some(OptionsList {
32+
options_list: (feature as *const _ as *mut lv2_sys::LV2_Options_Option).as_ref()?,
33+
})
34+
}
35+
}
36+
37+
impl<'a> IntoIterator for &'a OptionsList<'a> {
38+
type Item = &'a OptionValue;
39+
type IntoIter = OptionsListIter<'a>;
40+
41+
#[inline]
42+
fn into_iter(self) -> Self::IntoIter {
43+
self.iter()
44+
}
45+
}
46+
47+
pub struct OptionsListIter<'a> {
48+
current: &'a lv2_sys::LV2_Options_Option
49+
}
50+
51+
impl<'a> Iterator for OptionsListIter<'a> {
52+
type Item = &'a OptionValue;
53+
54+
#[inline]
55+
fn next(&mut self) -> Option<Self::Item> {
56+
let item = self.current;
57+
if item.key == 0 {
58+
return None;
59+
}
60+
61+
// SAFETY: list is guaranteed by the host to end with a zeroed struct.
62+
// Therefore the pointer always points to a valid lv2_sys::LV2_Options_Option value
63+
// (only its contents may be incorrect if zeroed)
64+
unsafe {
65+
self.current = &*((self.current as *const lv2_sys::LV2_Options_Option).add(1));
66+
}
67+
68+
Some(OptionValue::from_ref(item))
69+
}
70+
}

options/src/option.rs

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,47 @@ use lv2_atom::Atom;
22
use urid::UriBound;
33

44
pub mod error;
5-
pub mod list;
65
pub mod subject;
76
pub mod value;
8-
pub mod writer;
7+
pub mod request;
98

10-
pub trait OptionType<'a>: UriBound + Sized + 'a {
11-
type AtomType: Atom<'a, 'a>;
9+
/// A trait representing an LV2 Option type.
10+
///
11+
/// # Example
12+
///
13+
/// This example implements a simple option type named "MyIntOption" backed by an Int atom.
14+
///
15+
/// ```
16+
/// use lv2_options::OptionType;
17+
/// use urid::*;
18+
///
19+
/// #[uri("urn:lv2_options:test:SomeIntOption")]
20+
/// pub struct SomeIntOption(i32);
21+
///
22+
/// impl OptionType for SomeIntOption {
23+
/// type AtomType = lv2_atom::scalar::Int;
24+
///
25+
/// fn from_option_value(value: &i32) -> Option<Self> {
26+
/// Some(Self(*value))
27+
/// }
28+
///
29+
/// fn as_option_value(&self) -> &i32 {
30+
/// &self.0
31+
/// }
32+
/// }
33+
/// ```
34+
pub trait OptionType: UriBound + Sized {
35+
type AtomType: UriBound;
1236

13-
fn from_option_value(
14-
value: <Self::AtomType as Atom<'a, 'a>>::ReadHandle,
15-
) -> core::option::Option<Self>;
37+
/// Creates a new instance of this Option type from a given atom value.
38+
///
39+
/// This method may return `None` if the Atom's value is invalid for this option type.
40+
///
41+
/// This method is used to store option data when received by the host.
42+
fn from_option_value<'a>(value: <Self::AtomType as Atom<'a, 'a>>::ReadHandle) -> Option<Self> where Self::AtomType: Atom<'a, 'a>;
1643

17-
fn as_option_value(&'a self) -> <Self::AtomType as Atom<'a, 'a>>::ReadHandle;
44+
/// Returns this Option's value as a reference to its Atom type.
45+
///
46+
/// This method is used to send the option's value to the host when it is requested.
47+
fn as_option_value<'a>(&'a self) -> <Self::AtomType as Atom<'a, 'a>>::ReadHandle where Self::AtomType: Atom<'a, 'a>;
1848
}

options/src/option/error.rs

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ use std::error::Error;
22
use std::fmt::{Display, Formatter};
33

44
/// Errors raised when interacting with LV2 Options.
5-
#[derive(Debug, Copy, Clone, Eq, PartialEq)] // TODO: better Debug/Display implementation
5+
///
6+
/// See the [LV2 Documentation](https://lv2plug.in/doc/html/group__options.html#ga94d649a74ab340dfc6c6751dbe92ca07)
7+
/// for more information.
8+
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
69
#[non_exhaustive]
710
pub enum OptionsError {
811
Unknown = lv2_sys::LV2_Options_Status_LV2_OPTIONS_ERR_UNKNOWN as isize,
@@ -16,22 +19,35 @@ impl OptionsError {
1619
pub(crate) fn result_into_raw(value: Result<(), OptionsError>) -> lv2_sys::LV2_Options_Status {
1720
match value {
1821
Ok(()) => lv2_sys::LV2_Options_Status_LV2_OPTIONS_SUCCESS,
19-
_ => lv2_sys::LV2_Options_Status_LV2_OPTIONS_ERR_UNKNOWN, // TODO
22+
Err(OptionsError::BadSubject) => lv2_sys::LV2_Options_Status_LV2_OPTIONS_ERR_BAD_SUBJECT,
23+
Err(OptionsError::BadKey) => lv2_sys::LV2_Options_Status_LV2_OPTIONS_ERR_BAD_KEY,
24+
Err(OptionsError::BadValue) => lv2_sys::LV2_Options_Status_LV2_OPTIONS_ERR_BAD_VALUE,
25+
Err(_) => lv2_sys::LV2_Options_Status_LV2_OPTIONS_ERR_UNKNOWN,
2026
}
2127
}
2228

2329
#[inline]
2430
pub(crate) fn from_raw(status: lv2_sys::LV2_Options_Status) -> Result<(), OptionsError> {
2531
match status {
2632
lv2_sys::LV2_Options_Status_LV2_OPTIONS_SUCCESS => Ok(()),
27-
_ => Err(OptionsError::Unknown), // TODO
33+
lv2_sys::LV2_Options_Status_LV2_OPTIONS_ERR_BAD_SUBJECT => Err(OptionsError::BadSubject),
34+
lv2_sys::LV2_Options_Status_LV2_OPTIONS_ERR_BAD_KEY => Err(OptionsError::BadKey),
35+
lv2_sys::LV2_Options_Status_LV2_OPTIONS_ERR_BAD_VALUE => Err(OptionsError::BadValue),
36+
_ => Err(OptionsError::Unknown),
2837
}
2938
}
3039
}
3140

3241
impl Display for OptionsError {
33-
fn fmt(&self, _f: &mut Formatter<'_>) -> std::fmt::Result {
34-
todo!()
42+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
43+
let msg = match self {
44+
OptionsError::Unknown => "Unknown error while reading/writing Option",
45+
OptionsError::BadSubject => "Unknown Option subject",
46+
OptionsError::BadKey => "Unknown Option key",
47+
OptionsError::BadValue => "Invalid Option value"
48+
};
49+
50+
write!(f, "{}", msg)
3551
}
3652
}
3753

0 commit comments

Comments
 (0)