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
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
/*!
The components required to define a plugin API.

The types defined in this module are required in defining the plugin API, the

# Example - Define Plugin

```rust
use dygpi::plugin::Plugin;

# #[derive(Debug)] struct SoundEngine;
# #[derive(Debug)] struct MediaStream;
#[derive(Debug)]
struct SoundEffectPlugin {
    id: String,
    engine: SoundEngine,
    media: MediaStream,
};

impl Plugin for SoundEffectPlugin {
    fn plugin_id(&self) -> &String {
        &self.id
    }

    fn on_load(&self) -> dygpi::error::Result<()> {
        // connect to sound engine
        // load media stream
        Ok(())
    }

    fn on_unload(&self) -> dygpi::error::Result<()> {
        // unload media stream
        // disconnect from sound engine
        Ok(())
    }
}

impl SoundEffectPlugin {
    pub fn new(id: &str) -> Self { unimplemented!() }
    pub fn play(&self) {}
}
```

# Example - Register Plugin

```rust
use dygpi::plugin::PluginRegistrar;
# use dygpi::plugin::Plugin;
# #[derive(Debug)] struct SoundEngine;
# #[derive(Debug)] struct MediaStream;
# #[derive(Debug)]
# struct SoundEffectPlugin {
#     id: String,
#     engine: SoundEngine,
#     media: MediaStream,
# };
# impl Plugin for SoundEffectPlugin {
#     fn plugin_id(&self) -> &String {
#         &self.id
#     }
#     fn on_load(&self) -> dygpi::error::Result<()> { Ok(()) }
#     fn on_unload(&self) -> dygpi::error::Result<()> { Ok(()) }
# }
# impl SoundEffectPlugin {
#     pub fn new(id: &str) -> Self { unimplemented!() }
#     pub fn play(&self) {}
# }

const PLUGIN_ID: &str = concat!(env!("CARGO_PKG_NAME"), "::", module_path!(), "::DelayEffect");

#[no_mangle]
pub extern "C" fn register_plugins<MyPlugin>(
    registrar: &mut PluginRegistrar<SoundEffectPlugin>
) {
    registrar.register(SoundEffectPlugin::new(PLUGIN_ID));
}
```

*/

use crate::error::Result;
use std::any::Any;
use std::collections::hash_map::DefaultHasher;
use std::fmt::Debug;
use std::hash::{Hash, Hasher};
use std::sync::Arc;

// ------------------------------------------------------------------------------------------------
// Public Types
// ------------------------------------------------------------------------------------------------

///
/// This trait must be implemented by any plugin type, it not only provides a plugin id, but also
/// provides lifecycle methods which implementors can use to manage resources owned by the plugin.
pub trait Plugin: Any + Debug + Sync + Send {
    ///
    /// Return the plug-in identifier for this instance. In general a unique format that also
    /// provides some debug/trace value is to use the package/module path as shown below.
    ///
    /// ```rust
    /// const PLUGIN_ID: &str = concat!(env!("CARGO_PKG_NAME"), "::", module_path!(), "::MyPlugin");
    /// ```
    fn plugin_id(&self) -> &String;

    ///
    /// Called by the plugin manager after the registration process is complete.
    ///
    fn on_load(&self) -> Result<()>;

    ///
    /// Called by the plugin manager once a plugin has been de-registered but before the library
    /// is closed.
    ///
    fn on_unload(&self) -> Result<()>;
}

///
/// The type for the registration function that a plugin provider **MUST** include in their
/// library. This function constructs plugin instances and uses the registrar as a callback
/// into the plugin manager.
///
/// ```rust
/// use dygpi::plugin::PluginRegistrar;
/// # use dygpi::plugin::Plugin;
///
/// # #[derive(Debug)] struct SoundEngine;
/// # #[derive(Debug)] struct MediaStream;
/// # #[derive(Debug)]
/// # struct SoundEffectPlugin {
/// #     id: String,
/// #     engine: SoundEngine,
/// #     media: MediaStream,
/// # };
/// # impl Plugin for SoundEffectPlugin {
/// #     fn plugin_id(&self) -> &String {
/// #         &self.id
/// #     }
/// #     fn on_load(&self) -> dygpi::error::Result<()> { Ok(()) }
/// #     fn on_unload(&self) -> dygpi::error::Result<()> { Ok(()) }
/// # }
/// # impl SoundEffectPlugin {
/// #     pub fn new(id: &str) -> Self { unimplemented!() }
/// #     pub fn play(&self) {}
/// # }
/// # const PLUGIN_ID: &str = concat!(env!("CARGO_PKG_NAME"), "::", module_path!(), "::DelayEffect");
/// #[no_mangle]
/// pub extern "C" fn register_plugins<MyPlugin>(registrar: &mut PluginRegistrar<SoundEffectPlugin>) {
///     registrar.register(SoundEffectPlugin::new(PLUGIN_ID));
/// }
/// ```
///
pub type PluginRegistrationFn<T> = fn(registrar: &mut PluginRegistrar<T>);

///
/// The required name of the registration function (see the
/// [`PluginRegistrationFn`](type.PluginRegistrationFn.html) type).
///
pub const PLUGIN_REGISTRATION_FN_NAME: &[u8] = b"register_plugins\0";

///
/// A registrar is created by a plugin manager and provided to the library's registration
/// function to register any plugins it has.
///
#[derive(Debug)]
pub struct PluginRegistrar<T>
where
    T: Plugin,
{
    plugins: Vec<Arc<T>>,
    error: Option<Box<dyn std::error::Error>>,
}

// ------------------------------------------------------------------------------------------------
// Public Functions
// ------------------------------------------------------------------------------------------------

pub(crate) type CompatibilityFn = fn() -> u64;

pub(crate) const COMPATIBILITY_FN_NAME: &[u8] = b"compatibility_hash\0";

///
/// This function is exposed so that the version linked into a plugin provider may be compared to
/// the one linked into the plugin host.
///
#[allow(unsafe_code)]
#[no_mangle]
pub extern "C" fn compatibility_hash() -> u64 {
    const CARGO_PKG_VERSION: &str = env!("CARGO_PKG_VERSION");
    const RUSTC_VERSION: &str = env!("RUSTC_VERSION");

    debug!(
        "compatibility_hash() -> Hash({:?}, {:?})",
        CARGO_PKG_VERSION, RUSTC_VERSION
    );

    let mut s = DefaultHasher::new();
    CARGO_PKG_VERSION.hash(&mut s);
    RUSTC_VERSION.hash(&mut s);
    s.finish()
}

// ------------------------------------------------------------------------------------------------
// Implementations
// ------------------------------------------------------------------------------------------------

impl<T> PluginRegistrar<T>
where
    T: Plugin,
{
    pub(crate) fn default() -> Self {
        Self {
            plugins: Default::default(),
            error: None,
        }
    }

    ///
    /// Register a plugin, this will store the plugin in the registrar until the registration is
    /// completed. After the registration function completes, the plugin manager will add all
    /// plugins, if no errors were reported.
    ///
    pub fn register(&mut self, plugin: T) {
        if self.error.is_none() {
            self.plugins.push(Arc::new(plugin));
        }
    }

    ///
    /// Inform the registrar of an error, note that if multiple are recorded only the last will
    /// propagate out of the plugin manager.
    ///
    pub fn error(&mut self, error: Box<dyn std::error::Error>) {
        self.error = Some(error);
    }

    pub(crate) fn plugins(self) -> std::result::Result<Vec<Arc<T>>, Box<dyn std::error::Error>> {
        match self.error {
            None => Ok(self.plugins),
            Some(error) => Err(error),
        }
    }
}