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), } } }