Skip to main content

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}