lv2_state/lib.rs
1//! Extension for LV2 plugins to store their state.
2//!
3//! This is a rather classic extension to LV2 plugins: There is a trait called [`State`](trait.State.html) which requires the methods [`save`](trait.State.html#tymethod.save) and [`restore`](trait.State.html#tymethiod.restore) to be implemented. These methods will be called by the host to save and restore the state of the plugin.
4//!
5//! ## Refering to files
6//!
7//! This crate also includes features to create files in a unique namespace for the plugin instance. However, these files will not persist across `save`/`restore` calls and therefore need to be safed together with the state as well. For more information, see the documentation of the [`path` module](path/index.html).
8//!
9//! ## Example usage
10//!
11//! ```
12//! use lv2_atom::prelude::*;
13//! use lv2_core::prelude::*;
14//! use lv2_state::*;
15//! use lv2_urid::*;
16//! use urid::*;
17//!
18//! /// A plugin that stores a float value.
19//! struct Stateful {
20//! internal: f32,
21//! urids: AtomURIDCollection,
22//! }
23//!
24//! /// `Stateful`s implementation of `State`.
25//! impl State for Stateful {
26//! type StateFeatures = ();
27//!
28//! fn save(&self, mut store: StoreHandle, _: ()) -> Result<(), StateErr> {
29//! // Try to draft a new property and store the float inside it.
30//! store
31//! .draft(URID::new(1000).unwrap())
32//! .init(self.urids.float, self.internal)?;
33//!
34//! // Commit the written property.
35//! // Otherwise, it will discarded.
36//! store.commit_all()
37//! }
38//!
39//! fn restore(&mut self, store: RetrieveHandle, _: ()) -> Result<(), StateErr> {
40//! // Try to restore the property.
41//! self.internal = store
42//! .retrieve(URID::new(1000).unwrap())?
43//! .read(self.urids.float, ())?;
44//!
45//! // We're done.
46//! Ok(())
47//! }
48//! }
49//!
50//! impl Plugin for Stateful {
51//! type Ports = ();
52//! type InitFeatures = Features<'static>;
53//! type AudioFeatures = ();
54//!
55//! fn new(_: &PluginInfo, features: &mut Features<'static>) -> Option<Self> {
56//! Some(Stateful {
57//! internal: 42.0,
58//! urids: features.map.populate_collection()?,
59//! })
60//! }
61//!
62//! fn run(&mut self, _: &mut (), _: &mut (), _: u32) {
63//! // Set the float to a different value than the previous one.
64//! self.internal += 1.0;
65//! }
66//!
67//! fn extension_data(uri: &Uri) -> Option<&'static dyn std::any::Any> {
68//! // Export the store extension. Otherwise, the host won't use it.
69//! match_extensions!(uri, StateDescriptor<Self>)
70//! }
71//! }
72//!
73//! #[derive(FeatureCollection)]
74//! pub struct Features<'a> {
75//! map: LV2Map<'a>,
76//! }
77//!
78//! unsafe impl UriBound for Stateful {
79//! const URI: &'static [u8] = b"urn:lv2_atom:stateful\0";
80//! }
81//! ```
82extern crate lv2_atom as atom;
83extern crate lv2_core as core;
84extern crate lv2_sys as sys;
85
86mod interface;
87pub use interface::*;
88
89mod raw;
90pub use raw::*;
91
92mod storage;
93pub use storage::Storage;
94
95pub mod path;
96
97/// Kinds of errors that may occur in the crate.
98#[derive(Clone, Copy, Debug, PartialEq, Eq)]
99pub enum StateErr {
100 /// The kind of the error is unknown or doesn't have a representation.
101 Unknown,
102 /// A callback function pointer of a method is bad.
103 BadCallback,
104 /// Retrieved data is invalid.
105 BadData,
106 /// The retrieved data doesn't have the correct type.
107 BadType,
108 /// The flags a method was called with are invalid.
109 BadFlags,
110 /// A feature the plugin requested is missing.
111 NoFeature,
112 /// A property the plugin is requesting doesn't exist.
113 NoProperty,
114 /// There isn't enough memory available to execute the task.
115 NoSpace,
116 /// A path that's been used as a parameter is not encoded in UTF-8.
117 PathNotUTF8,
118 /// The host does not comply to the specification.
119 HostError,
120}
121
122impl StateErr {
123 /// Convert a raw status flag to a result or possible error value.
124 pub fn from(value: sys::LV2_State_Status) -> Result<(), StateErr> {
125 match value {
126 sys::LV2_State_Status_LV2_STATE_SUCCESS => Ok(()),
127 sys::LV2_State_Status_LV2_STATE_ERR_BAD_TYPE => Err(StateErr::BadType),
128 sys::LV2_State_Status_LV2_STATE_ERR_BAD_FLAGS => Err(StateErr::BadFlags),
129 sys::LV2_State_Status_LV2_STATE_ERR_NO_FEATURE => Err(StateErr::NoFeature),
130 sys::LV2_State_Status_LV2_STATE_ERR_NO_PROPERTY => Err(StateErr::NoProperty),
131 sys::LV2_State_Status_LV2_STATE_ERR_NO_SPACE => Err(StateErr::NoSpace),
132 _ => Err(StateErr::Unknown),
133 }
134 }
135
136 /// Convert a result to a raw status flag.
137 pub fn into(result: Result<(), StateErr>) -> sys::LV2_State_Status {
138 match result {
139 Ok(()) => sys::LV2_State_Status_LV2_STATE_SUCCESS,
140 Err(StateErr::BadType) => sys::LV2_State_Status_LV2_STATE_ERR_BAD_TYPE,
141 Err(StateErr::BadFlags) => sys::LV2_State_Status_LV2_STATE_ERR_BAD_FLAGS,
142 Err(StateErr::NoFeature) => sys::LV2_State_Status_LV2_STATE_ERR_NO_FEATURE,
143 Err(StateErr::NoProperty) => sys::LV2_State_Status_LV2_STATE_ERR_NO_PROPERTY,
144 Err(StateErr::NoSpace) => sys::LV2_State_Status_LV2_STATE_ERR_NO_SPACE,
145 Err(_) => sys::LV2_State_Status_LV2_STATE_ERR_UNKNOWN,
146 }
147 }
148}
149
150#[cfg(test)]
151mod test {
152 use crate::StateErr;
153
154 #[test]
155 fn test_state_conversion() {
156 assert_eq!(
157 Ok(()),
158 StateErr::from(sys::LV2_State_Status_LV2_STATE_SUCCESS)
159 );
160 assert_eq!(
161 Err(StateErr::BadType),
162 StateErr::from(sys::LV2_State_Status_LV2_STATE_ERR_BAD_TYPE)
163 );
164 assert_eq!(
165 Err(StateErr::BadFlags),
166 StateErr::from(sys::LV2_State_Status_LV2_STATE_ERR_BAD_FLAGS)
167 );
168 assert_eq!(
169 Err(StateErr::NoFeature),
170 StateErr::from(sys::LV2_State_Status_LV2_STATE_ERR_NO_FEATURE)
171 );
172 assert_eq!(
173 Err(StateErr::NoProperty),
174 StateErr::from(sys::LV2_State_Status_LV2_STATE_ERR_NO_PROPERTY)
175 );
176 assert_eq!(
177 Err(StateErr::NoSpace),
178 StateErr::from(sys::LV2_State_Status_LV2_STATE_ERR_NO_SPACE)
179 );
180 assert_eq!(
181 Err(StateErr::Unknown),
182 StateErr::from(std::i32::MAX as sys::LV2_State_Status)
183 );
184
185 assert_eq!(
186 sys::LV2_State_Status_LV2_STATE_SUCCESS,
187 StateErr::into(Ok(()))
188 );
189 assert_eq!(
190 sys::LV2_State_Status_LV2_STATE_ERR_BAD_TYPE,
191 StateErr::into(Err(StateErr::BadType))
192 );
193 assert_eq!(
194 sys::LV2_State_Status_LV2_STATE_ERR_BAD_FLAGS,
195 StateErr::into(Err(StateErr::BadFlags))
196 );
197 assert_eq!(
198 sys::LV2_State_Status_LV2_STATE_ERR_NO_FEATURE,
199 StateErr::into(Err(StateErr::NoFeature))
200 );
201 assert_eq!(
202 sys::LV2_State_Status_LV2_STATE_ERR_NO_PROPERTY,
203 StateErr::into(Err(StateErr::NoProperty))
204 );
205 assert_eq!(
206 sys::LV2_State_Status_LV2_STATE_ERR_NO_SPACE,
207 StateErr::into(Err(StateErr::NoSpace))
208 );
209 assert_eq!(
210 sys::LV2_State_Status_LV2_STATE_ERR_UNKNOWN,
211 StateErr::into(Err(StateErr::Unknown))
212 );
213 }
214}