use crate::StateErr;
use atom::prelude::*;
use atom::space::*;
use std::collections::HashMap;
use std::ffi::c_void;
use std::marker::PhantomData;
use urid::*;
pub struct StoreHandle<'a> {
properties: HashMap<URID, SpaceElement>,
store_fn: sys::LV2_State_Store_Function,
handle: sys::LV2_State_Handle,
lifetime: PhantomData<&'a mut c_void>,
}
impl<'a> StoreHandle<'a> {
pub fn new(store_fn: sys::LV2_State_Store_Function, handle: sys::LV2_State_Handle) -> Self {
StoreHandle {
properties: HashMap::new(),
store_fn,
handle,
lifetime: PhantomData,
}
}
pub fn draft<K: ?Sized>(&mut self, property_key: URID<K>) -> StatePropertyWriter {
let property_key = property_key.into_general();
self.properties
.insert(property_key.into_general(), SpaceElement::default());
StatePropertyWriter::new(SpaceHead::new(
self.properties
.get_mut(&property_key.into_general())
.unwrap(),
))
}
pub fn commit_pair<K: ?Sized>(
store_fn: sys::LV2_State_Store_Function,
handle: sys::LV2_State_Handle,
key: URID<K>,
space: SpaceElement,
) -> Result<(), StateErr> {
let store_fn = store_fn.ok_or(StateErr::BadCallback)?;
let space: Vec<u8> = space.to_vec();
let space = Space::from_slice(space.as_ref());
let (header, data) = space
.split_type::<sys::LV2_Atom>()
.ok_or(StateErr::BadData)?;
let data = data
.split_raw(header.size as usize)
.map(|(data, _)| data)
.ok_or(StateErr::BadData)?;
let key = key.get();
let data_ptr = data as *const _ as *const c_void;
let data_size = header.size as usize;
let data_type = header.type_;
let flags: u32 = (sys::LV2_State_Flags::LV2_STATE_IS_POD
| sys::LV2_State_Flags::LV2_STATE_IS_PORTABLE)
.into();
StateErr::from(unsafe { (store_fn)(handle, key, data_ptr, data_size, data_type, flags) })
}
pub fn commit_all(&mut self) -> Result<(), StateErr> {
for (key, space) in self.properties.drain() {
Self::commit_pair(self.store_fn, self.handle, key, space)?;
}
Ok(())
}
pub fn commit<K: ?Sized>(&mut self, key: URID<K>) -> Option<Result<(), StateErr>> {
let key = key.into_general();
let space = self.properties.remove(&key)?;
Some(Self::commit_pair(self.store_fn, self.handle, key, space))
}
pub fn discard_all(&mut self) {
self.properties.clear();
}
pub fn discard<K: ?Sized>(&mut self, key: URID<K>) {
self.properties.remove(&key.into_general());
}
}
pub struct StatePropertyWriter<'a> {
head: SpaceHead<'a>,
initialized: bool,
}
impl<'a> StatePropertyWriter<'a> {
pub fn new(head: SpaceHead<'a>) -> Self {
Self {
head,
initialized: false,
}
}
pub fn init<'b, A: Atom<'a, 'b>>(
&'b mut self,
urid: URID<A>,
parameter: A::WriteParameter,
) -> Result<A::WriteHandle, StateErr> {
if !self.initialized {
self.initialized = true;
(&mut self.head as &mut dyn MutSpace)
.init(urid, parameter)
.ok_or(StateErr::Unknown)
} else {
Err(StateErr::Unknown)
}
}
}
pub struct RetrieveHandle<'a> {
retrieve_fn: sys::LV2_State_Retrieve_Function,
handle: sys::LV2_State_Handle,
lifetime: PhantomData<&'a mut c_void>,
}
impl<'a> RetrieveHandle<'a> {
pub fn new(
retrieve_fn: sys::LV2_State_Retrieve_Function,
handle: sys::LV2_State_Handle,
) -> Self {
RetrieveHandle {
retrieve_fn,
handle,
lifetime: PhantomData,
}
}
pub fn retrieve<K: ?Sized>(&self, key: URID<K>) -> Result<StatePropertyReader, StateErr> {
let mut size: usize = 0;
let mut type_: u32 = 0;
let property_ptr: *const std::ffi::c_void = unsafe {
(self.retrieve_fn.ok_or(StateErr::BadCallback)?)(
self.handle,
key.get(),
&mut size,
&mut type_,
std::ptr::null_mut(),
)
};
let type_ = URID::new(type_).ok_or(StateErr::Unknown)?;
let space = if !property_ptr.is_null() {
unsafe { std::slice::from_raw_parts(property_ptr as *const u8, size) }
} else {
return Err(StateErr::NoProperty);
};
Ok(StatePropertyReader::new(type_, Space::from_slice(space)))
}
}
pub struct StatePropertyReader<'a> {
type_: URID,
body: Space<'a>,
}
impl<'a> StatePropertyReader<'a> {
pub fn new<T: ?Sized>(type_: URID<T>, body: Space<'a>) -> Self {
Self {
type_: type_.into_general(),
body,
}
}
pub fn type_(&self) -> URID {
self.type_
}
pub fn body(&self) -> Space {
self.body
}
pub fn read<A: Atom<'a, 'a>>(
&self,
urid: URID<A>,
parameter: A::ReadParameter,
) -> Result<A::ReadHandle, StateErr> {
if urid == self.type_ {
A::read(self.body, parameter).ok_or(StateErr::Unknown)
} else {
Err(StateErr::BadType)
}
}
}
#[cfg(test)]
mod tests {
use crate::raw::*;
use crate::storage::Storage;
use atom::space::Space;
fn store(storage: &mut Storage, urids: &AtomURIDCollection) {
let mut store_handle = storage.store_handle();
store_handle
.draft(URID::new(1).unwrap())
.init(urids.int, 17)
.unwrap();
store_handle
.draft(URID::new(2).unwrap())
.init(urids.float, 1.0)
.unwrap();
store_handle.commit(URID::new(1).unwrap()).unwrap().unwrap();
let mut vector_writer = store_handle.draft(URID::new(3).unwrap());
let mut vector_writer = vector_writer.init(urids.vector(), urids.int).unwrap();
vector_writer.append(&[1, 2, 3, 4]).unwrap();
store_handle.commit_all().unwrap();
store_handle
.draft(URID::new(4).unwrap())
.init(urids.int, 0)
.unwrap();
}
fn retrieve(storage: &mut Storage, urids: &AtomURIDCollection) {
let retrieve_handle = storage.retrieve_handle();
assert_eq!(
17,
retrieve_handle
.retrieve(URID::new(1).unwrap())
.unwrap()
.read(urids.int, ())
.unwrap()
);
assert_eq!(
1.0,
retrieve_handle
.retrieve(URID::new(2).unwrap())
.unwrap()
.read(urids.float, ())
.unwrap()
);
assert_eq!(
[1, 2, 3, 4],
retrieve_handle
.retrieve(URID::new(3).unwrap())
.unwrap()
.read(urids.vector(), urids.int)
.unwrap()
);
assert!(retrieve_handle.retrieve(URID::new(4).unwrap()).is_err());
}
#[test]
fn test_storage() {
let map = HashURIDMapper::new();
let urids = AtomURIDCollection::from_map(&map).unwrap();
let mut storage = Storage::default();
store(&mut storage, &urids);
for (key, (type_, value)) in storage.iter() {
match key.get() {
1 => {
assert_eq!(urids.int, *type_);
assert_eq!(17, unsafe { *(value.as_slice() as *const _ as *const i32) });
}
2 => {
assert_eq!(urids.float, *type_);
assert_eq!(1.0, unsafe {
*(value.as_slice() as *const _ as *const f32)
});
}
3 => {
assert_eq!(urids.vector::<Int>(), *type_);
let space = Space::from_slice(value.as_slice());
let data = Vector::read(space, urids.int).unwrap();
assert_eq!([1, 2, 3, 4], data);
}
_ => panic!("Invalid key!"),
}
}
retrieve(&mut storage, &urids);
}
}