1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
//! Extension for LV2 plugins to store their state.
//!
//! 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.
//!
//! ## Refering to files
//!
//! 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).
//!
//! ## Example usage
//!
//! ```
//! use lv2_atom::prelude::*;
//! use lv2_core::prelude::*;
//! use lv2_state::*;
//! use lv2_urid::*;
//! use urid::*;
//!
//! /// A plugin that stores a float value.
//! struct Stateful {
//!     internal: f32,
//!     urids: AtomURIDCollection,
//! }
//!
//! /// `Stateful`s implementation of `State`.
//! impl State for Stateful {
//!     type StateFeatures = ();
//!
//!     fn save(&self, mut store: StoreHandle, _: ()) -> Result<(), StateErr> {
//!         // Try to draft a new property and store the float inside it.
//!         store
//!             .draft(URID::new(1000).unwrap())
//!             .init(self.urids.float, self.internal)?;
//!
//!         // Commit the written property.
//!         // Otherwise, it will discarded.
//!         store.commit_all()
//!     }
//!
//!     fn restore(&mut self, store: RetrieveHandle, _: ()) -> Result<(), StateErr> {
//!         // Try to restore the property.
//!         self.internal = store
//!             .retrieve(URID::new(1000).unwrap())?
//!             .read(self.urids.float, ())?;
//!
//!         // We're done.
//!         Ok(())
//!     }
//! }
//!
//! impl Plugin for Stateful {
//!     type Ports = ();
//!     type InitFeatures = Features<'static>;
//!     type AudioFeatures = ();
//!
//!     fn new(_: &PluginInfo, features: &mut Features<'static>) -> Option<Self> {
//!         Some(Stateful {
//!             internal: 42.0,
//!             urids: features.map.populate_collection()?,
//!         })
//!     }
//!
//!     fn run(&mut self, _: &mut (), _: &mut (), _: u32) {
//!         // Set the float to a different value than the previous one.
//!         self.internal += 1.0;
//!     }
//!
//!     fn extension_data(uri: &Uri) -> Option<&'static dyn std::any::Any> {
//!         // Export the store extension. Otherwise, the host won't use it.
//!         match_extensions!(uri, StateDescriptor<Self>)
//!     }
//! }
//!
//! #[derive(FeatureCollection)]
//! pub struct Features<'a> {
//!     map: LV2Map<'a>,
//! }
//!
//! unsafe impl UriBound for Stateful {
//!     const URI: &'static [u8] = b"urn:lv2_atom:stateful\0";
//! }
//! ```
extern crate lv2_atom as atom;
extern crate lv2_core as core;
extern crate lv2_sys as sys;

mod interface;
pub use interface::*;

mod raw;
pub use raw::*;

mod storage;
pub use storage::Storage;

pub mod path;

/// Kinds of errors that may occur in the crate.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum StateErr {
    /// The kind of the error is unknown or doesn't have a representation.
    Unknown,
    /// A callback function pointer of a method is bad.
    BadCallback,
    /// Retrieved data is invalid.
    BadData,
    /// The retrieved data doesn't have the correct type.
    BadType,
    /// The flags a method was called with are invalid.
    BadFlags,
    /// A feature the plugin requested is missing.
    NoFeature,
    /// A property the plugin is requesting doesn't exist.
    NoProperty,
    /// There isn't enough memory available to execute the task.
    NoSpace,
    /// A path that's been used as a parameter is not encoded in UTF-8.
    PathNotUTF8,
    /// The host does not comply to the specification.
    HostError,
}

impl StateErr {
    /// Convert a raw status flag to a result or possible error value.
    pub fn from(value: sys::LV2_State_Status) -> Result<(), StateErr> {
        match value {
            sys::LV2_State_Status_LV2_STATE_SUCCESS => Ok(()),
            sys::LV2_State_Status_LV2_STATE_ERR_BAD_TYPE => Err(StateErr::BadType),
            sys::LV2_State_Status_LV2_STATE_ERR_BAD_FLAGS => Err(StateErr::BadFlags),
            sys::LV2_State_Status_LV2_STATE_ERR_NO_FEATURE => Err(StateErr::NoFeature),
            sys::LV2_State_Status_LV2_STATE_ERR_NO_PROPERTY => Err(StateErr::NoProperty),
            sys::LV2_State_Status_LV2_STATE_ERR_NO_SPACE => Err(StateErr::NoSpace),
            _ => Err(StateErr::Unknown),
        }
    }

    /// Convert a result to a raw status flag.
    pub fn into(result: Result<(), StateErr>) -> sys::LV2_State_Status {
        match result {
            Ok(()) => sys::LV2_State_Status_LV2_STATE_SUCCESS,
            Err(StateErr::BadType) => sys::LV2_State_Status_LV2_STATE_ERR_BAD_TYPE,
            Err(StateErr::BadFlags) => sys::LV2_State_Status_LV2_STATE_ERR_BAD_FLAGS,
            Err(StateErr::NoFeature) => sys::LV2_State_Status_LV2_STATE_ERR_NO_FEATURE,
            Err(StateErr::NoProperty) => sys::LV2_State_Status_LV2_STATE_ERR_NO_PROPERTY,
            Err(StateErr::NoSpace) => sys::LV2_State_Status_LV2_STATE_ERR_NO_SPACE,
            Err(_) => sys::LV2_State_Status_LV2_STATE_ERR_UNKNOWN,
        }
    }
}

#[cfg(test)]
mod test {
    use crate::StateErr;

    #[test]
    fn test_state_conversion() {
        assert_eq!(
            Ok(()),
            StateErr::from(sys::LV2_State_Status_LV2_STATE_SUCCESS)
        );
        assert_eq!(
            Err(StateErr::BadType),
            StateErr::from(sys::LV2_State_Status_LV2_STATE_ERR_BAD_TYPE)
        );
        assert_eq!(
            Err(StateErr::BadFlags),
            StateErr::from(sys::LV2_State_Status_LV2_STATE_ERR_BAD_FLAGS)
        );
        assert_eq!(
            Err(StateErr::NoFeature),
            StateErr::from(sys::LV2_State_Status_LV2_STATE_ERR_NO_FEATURE)
        );
        assert_eq!(
            Err(StateErr::NoProperty),
            StateErr::from(sys::LV2_State_Status_LV2_STATE_ERR_NO_PROPERTY)
        );
        assert_eq!(
            Err(StateErr::NoSpace),
            StateErr::from(sys::LV2_State_Status_LV2_STATE_ERR_NO_SPACE)
        );
        assert_eq!(
            Err(StateErr::Unknown),
            StateErr::from(std::i32::MAX as sys::LV2_State_Status)
        );

        assert_eq!(
            sys::LV2_State_Status_LV2_STATE_SUCCESS,
            StateErr::into(Ok(()))
        );
        assert_eq!(
            sys::LV2_State_Status_LV2_STATE_ERR_BAD_TYPE,
            StateErr::into(Err(StateErr::BadType))
        );
        assert_eq!(
            sys::LV2_State_Status_LV2_STATE_ERR_BAD_FLAGS,
            StateErr::into(Err(StateErr::BadFlags))
        );
        assert_eq!(
            sys::LV2_State_Status_LV2_STATE_ERR_NO_FEATURE,
            StateErr::into(Err(StateErr::NoFeature))
        );
        assert_eq!(
            sys::LV2_State_Status_LV2_STATE_ERR_NO_PROPERTY,
            StateErr::into(Err(StateErr::NoProperty))
        );
        assert_eq!(
            sys::LV2_State_Status_LV2_STATE_ERR_NO_SPACE,
            StateErr::into(Err(StateErr::NoSpace))
        );
        assert_eq!(
            sys::LV2_State_Status_LV2_STATE_ERR_UNKNOWN,
            StateErr::into(Err(StateErr::Unknown))
        );
    }
}