From 85193fb7a83f1b5d2cf853ed63d5bc41713d0aa8 Mon Sep 17 00:00:00 2001 From: Michael Ilyin Date: Fri, 10 Nov 2023 14:41:43 +0100 Subject: [PATCH] plugin trait sources reorganised --- plugins/zenoh-backend-traits/src/config.rs | 4 +- plugins/zenoh-backend-traits/src/lib.rs | 4 +- .../zenoh-plugin-storage-manager/src/lib.rs | 2 +- plugins/zenoh-plugin-trait/src/lib.rs | 47 +- plugins/zenoh-plugin-trait/src/loading.rs | 400 +----------------- .../src/loading/dynamic_plugin.rs | 225 ++++++++++ .../src/loading/static_plugin.rs | 114 +++++ plugins/zenoh-plugin-trait/src/plugin.rs | 110 +++++ plugins/zenoh-plugin-trait/src/vtable.rs | 32 +- zenoh/src/net/runtime/mod.rs | 4 +- zenoh/src/plugins/sealed.rs | 6 +- 11 files changed, 495 insertions(+), 453 deletions(-) create mode 100644 plugins/zenoh-plugin-trait/src/loading/dynamic_plugin.rs create mode 100644 plugins/zenoh-plugin-trait/src/loading/static_plugin.rs create mode 100644 plugins/zenoh-plugin-trait/src/plugin.rs diff --git a/plugins/zenoh-backend-traits/src/config.rs b/plugins/zenoh-backend-traits/src/config.rs index e268f1f5c4..976b1e4846 100644 --- a/plugins/zenoh-backend-traits/src/config.rs +++ b/plugins/zenoh-backend-traits/src/config.rs @@ -18,7 +18,7 @@ use serde_json::{Map, Value}; use std::convert::TryFrom; use std::time::Duration; use zenoh::{key_expr::keyexpr, prelude::OwnedKeyExpr, Result as ZResult}; -use zenoh_plugin_trait::{CompatibilityVersion, PluginStartArgs}; +use zenoh_plugin_trait::{PluginStructVersion, PluginStartArgs}; use zenoh_result::{bail, zerror, Error}; #[derive(JsonSchema, Debug, Clone, AsMut, AsRef)] @@ -69,7 +69,7 @@ pub struct ReplicaConfig { pub delta: Duration, } -impl CompatibilityVersion for VolumeConfig { +impl PluginStructVersion for VolumeConfig { fn version() -> u64 { 1 } diff --git a/plugins/zenoh-backend-traits/src/lib.rs b/plugins/zenoh-backend-traits/src/lib.rs index 4669f1eadd..03ad09253c 100644 --- a/plugins/zenoh-backend-traits/src/lib.rs +++ b/plugins/zenoh-backend-traits/src/lib.rs @@ -140,7 +140,7 @@ use zenoh::queryable::ReplyBuilder; use zenoh::time::Timestamp; use zenoh::value::Value; pub use zenoh::Result as ZResult; -use zenoh_plugin_trait::{concat_enabled_features, CompatibilityVersion, PluginControl, PluginInstance}; +use zenoh_plugin_trait::{concat_enabled_features, PluginStructVersion, PluginControl, PluginInstance}; pub mod config; use config::{StorageConfig, VolumeConfig}; @@ -224,7 +224,7 @@ pub trait Volume: Send + Sync { pub type VolumePlugin = Box; -impl CompatibilityVersion for VolumePlugin { +impl PluginStructVersion for VolumePlugin { fn version() -> u64 { 1 } diff --git a/plugins/zenoh-plugin-storage-manager/src/lib.rs b/plugins/zenoh-plugin-storage-manager/src/lib.rs index 6c4179f32f..7d1d762a16 100644 --- a/plugins/zenoh-plugin-storage-manager/src/lib.rs +++ b/plugins/zenoh-plugin-storage-manager/src/lib.rs @@ -72,7 +72,7 @@ impl Plugin for StoragesPlugin { } } -type PluginsManager = zenoh_plugin_trait::loading::PluginsManager; +type PluginsManager = zenoh_plugin_trait::PluginsManager; struct StorageRuntime(Arc>); struct StorageRuntimeInner { diff --git a/plugins/zenoh-plugin-trait/src/lib.rs b/plugins/zenoh-plugin-trait/src/lib.rs index 3ce73ea116..6c6be740d1 100644 --- a/plugins/zenoh-plugin-trait/src/lib.rs +++ b/plugins/zenoh-plugin-trait/src/lib.rs @@ -18,16 +18,17 @@ //! //! If building a plugin for [`zenohd`](https://crates.io/crates/zenoh), you should use the types exported in [`zenoh::plugins`](https://docs.rs/zenoh/latest/zenoh/plugins) to fill [`Plugin`]'s associated types. //! To check your plugin typing for `zenohd`, have your plugin implement [`zenoh::plugins::ZenohPlugin`](https://docs.rs/zenoh/latest/zenoh/plugins/struct.ZenohPlugin) -pub mod loading; -pub mod vtable; - -use std::borrow::Cow; +//! +mod loading; +mod plugin; +mod vtable; -use zenoh_result::ZResult; - -pub mod prelude { - pub use crate::{concat_enabled_features, loading::*, vtable::*, CompatibilityVersion, Plugin}; -} +pub use loading::{DeclaredPlugin, LoadedPlugin, PluginsManager, StartedPlugin}; +pub use plugin::{ + Plugin, PluginCondition, PluginConditionSetter, PluginControl, PluginInfo, PluginInstance, + PluginStartArgs, PluginState, PluginStatus, PluginStructVersion, +}; +pub use vtable::{Compatibility, PluginLoaderVersion, PluginVTable, PLUGIN_LOADER_VERSION}; #[macro_export] macro_rules! concat_enabled_features { @@ -41,29 +42,5 @@ macro_rules! concat_enabled_features { }; } -pub trait CompatibilityVersion { - /// The version of the structure implementing this trait. After any channge in the structure or it's dependencies - /// whcich may affect the ABI, this version should be incremented. - fn version() -> u64; - /// The features enabled during comiplation of the structure implementing this trait. - /// Different features between the plugin and the host may cuase ABI incompatibility even if the structure version is the same. - /// Use `concat_enabled_features!` to generate this string - fn features() -> &'static str; -} - -pub trait PluginControl { - fn plugins(&self) -> Vec>; - // fn status(&self, name: &str) -> PluginStatus; -} - -pub trait PluginStartArgs : CompatibilityVersion {} -pub trait PluginInstance : CompatibilityVersion + PluginControl + Send {} - -pub trait Plugin: Sized + 'static { - type StartArgs: PluginStartArgs; - type Instance: PluginInstance; - /// Your plugins' default name when statically linked. - const STATIC_NAME: &'static str; - /// Starts your plugin. Use `Ok` to return your plugin's control structure - fn start(name: &str, args: &Self::StartArgs) -> ZResult; -} +pub const FEATURES: &str = + concat_enabled_features!(prefix = "zenoh-plugin-trait", features = ["default"]); diff --git a/plugins/zenoh-plugin-trait/src/loading.rs b/plugins/zenoh-plugin-trait/src/loading.rs index ff170d0446..81b744807c 100644 --- a/plugins/zenoh-plugin-trait/src/loading.rs +++ b/plugins/zenoh-plugin-trait/src/loading.rs @@ -1,4 +1,3 @@ -// // Copyright (c) 2023 ZettaScale Technology // // This program and the accompanying materials are made available under the @@ -11,83 +10,14 @@ // Contributors: // ZettaScale Zenoh Team, // +mod static_plugin; +mod dynamic_plugin; + use crate::*; -use libloading::Library; -use std::{ - borrow::Cow, - marker::PhantomData, - path::{Path, PathBuf}, -}; -use vtable::{Compatibility, PluginLoaderVersion, PluginVTable, PLUGIN_LOADER_VERSION}; -use zenoh_result::{bail, ZResult}; +use zenoh_result::ZResult; use zenoh_util::LibLoader; -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum PluginState { - Declared, - Loaded, - Started, -} - -#[derive(Clone, Debug, PartialEq, Eq, Default)] -pub struct PluginCondition { - warnings: Vec>, - errors: Vec>, -} - -impl PluginCondition { - pub fn new() -> Self { - Self::default() - } - pub fn clear(&mut self) { - self.warnings.clear(); - self.errors.clear(); - } - pub fn add_error>>(&mut self, error: S) { - self.errors.push(error.into()); - } - pub fn add_warning>>(&mut self, warning: S) { - self.warnings.push(warning.into()); - } - pub fn errors(&self) -> &[Cow<'static, str>] { - &self.errors - } - pub fn warnings(&self) -> &[Cow<'static, str>] { - &self.warnings - } -} - -pub trait PluginConditionSetter { - fn add_error(self, condition: &mut PluginCondition) -> Self; - fn add_warning(self, condition: &mut PluginCondition) -> Self; -} - -impl PluginConditionSetter for core::result::Result { - fn add_error(self, condition: &mut PluginCondition) -> Self { - if let Err(e) = &self { - condition.add_error(e.to_string()); - } - self - } - fn add_warning(self, condition: &mut PluginCondition) -> Self { - if let Err(e) = &self { - condition.add_warning(e.to_string()); - } - self - } -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct PluginStatus { - pub state: PluginState, - pub condition: PluginCondition, -} - -pub trait PluginInfo { - fn name(&self) -> &str; - fn path(&self) -> &str; - fn status(&self) -> PluginStatus; -} +use self::{dynamic_plugin::{DynamicPlugin, DynamicPluginSource}, static_plugin::StaticPlugin}; pub trait DeclaredPlugin: PluginInfo { fn load(&mut self) -> ZResult<&mut dyn LoadedPlugin>; @@ -106,319 +36,11 @@ pub trait StartedPlugin: PluginInfo { fn instance_mut(&mut self) -> &mut Instance; } -struct StaticPlugin -where - P: Plugin, -{ - instance: Option, - phantom: PhantomData

, -} - -impl StaticPlugin -where - P: Plugin, -{ - fn new() -> Self { - Self { - instance: None, - phantom: PhantomData, - } - } -} - -impl PluginInfo - for StaticPlugin -where - P: Plugin, -{ - fn name(&self) -> &str { - P::STATIC_NAME - } - fn path(&self) -> &str { - "" - } - fn status(&self) -> PluginStatus { - PluginStatus { - state: self - .instance - .as_ref() - .map_or(PluginState::Loaded, |_| PluginState::Started), - condition: PluginCondition::new(), // TODO: request runnnig plugin status - } - } -} - -impl - DeclaredPlugin for StaticPlugin -where - P: Plugin, -{ - fn load(&mut self) -> ZResult<&mut dyn LoadedPlugin> { - Ok(self) - } - fn loaded(&self) -> Option<&dyn LoadedPlugin> { - Some(self) - } - fn loaded_mut(&mut self) -> Option<&mut dyn LoadedPlugin> { - Some(self) - } -} - -impl - LoadedPlugin for StaticPlugin -where - P: Plugin, -{ - fn start(&mut self, args: &StartArgs) -> ZResult<&mut dyn StartedPlugin> { - if self.instance.is_none() { - self.instance = Some(P::start(self.name(), args)?); - } - Ok(self) - } - fn started(&self) -> Option<&dyn StartedPlugin> { - if self.instance.is_some() { - Some(self) - } else { - None - } - } - fn started_mut(&mut self) -> Option<&mut dyn StartedPlugin> { - if self.instance.is_some() { - Some(self) - } else { - None - } - } -} - -impl - StartedPlugin for StaticPlugin -where - P: Plugin, -{ - fn stop(&mut self) {} - fn instance(&self) -> &Instance { - self.instance.as_ref().unwrap() - } - fn instance_mut(&mut self) -> &mut Instance { - self.instance.as_mut().unwrap() - } -} - -/// This enum contains information where to load the plugin from. -enum DynamicPluginSource { - /// Load plugin with the name in String + `.so | .dll | .dylib` - /// in LibLoader's search paths. - ByName((LibLoader, String)), - /// Load first avalilable plugin from the list of path to plugin files (absolute or relative to the current working directory) - ByPaths(Vec), -} - -impl DynamicPluginSource { - fn load(&self) -> ZResult<(Library, PathBuf)> { - match self { - DynamicPluginSource::ByName((libloader, name)) => unsafe { - libloader.search_and_load(name) - }, - DynamicPluginSource::ByPaths(paths) => { - for path in paths { - match unsafe { LibLoader::load_file(path) } { - Ok((l, p)) => return Ok((l, p)), - Err(e) => log::warn!("Plugin {} load fail: {}", path, e), - } - } - bail!("Plugin not found in {:?}", &paths) - } - } - } -} - -struct DynamicPluginStarter { - _lib: Library, - path: PathBuf, - vtable: PluginVTable, -} - -impl - DynamicPluginStarter -{ - fn get_vtable(lib: &Library, path: &Path) -> ZResult> { - log::debug!("Loading plugin {}", &path.to_str().unwrap(),); - let get_plugin_loader_version = - unsafe { lib.get:: PluginLoaderVersion>(b"get_plugin_loader_version")? }; - let plugin_loader_version = get_plugin_loader_version(); - log::debug!("Plugin loader version: {}", &plugin_loader_version); - if plugin_loader_version != PLUGIN_LOADER_VERSION { - bail!( - "Plugin loader version mismatch: host = {}, plugin = {}", - PLUGIN_LOADER_VERSION, - plugin_loader_version - ); - } - let get_compatibility = unsafe { lib.get:: Compatibility>(b"get_compatibility")? }; - let plugin_compatibility_record = get_compatibility(); - let host_compatibility_record = Compatibility::new::(); - log::debug!( - "Plugin compativilty record: {:?}", - &plugin_compatibility_record - ); - if !plugin_compatibility_record.are_compatible(&host_compatibility_record) { - bail!( - "Plugin compatibility mismatch:\n\nHost:\n{}\nPlugin:\n{}\n", - host_compatibility_record, - plugin_compatibility_record - ); - } - let load_plugin = - unsafe { lib.get:: PluginVTable>(b"load_plugin")? }; - let vtable = load_plugin(); - Ok(vtable) - } - fn new(lib: Library, path: PathBuf) -> ZResult { - let vtable = Self::get_vtable(&lib, &path)?; - Ok(Self { - _lib: lib, - path, - vtable, - }) - } - fn start(&self, name: &str, args: &StartArgs) -> ZResult { - (self.vtable.start)(name, args) - } - fn path(&self) -> &str { - self.path.to_str().unwrap() - } -} - -struct DynamicPlugin { - name: String, - condition: PluginCondition, - source: DynamicPluginSource, - starter: Option>, - instance: Option, -} - -impl - DynamicPlugin -{ - fn new(name: String, source: DynamicPluginSource) -> Self { - Self { - name, - condition: PluginCondition::new(), - source, - starter: None, - instance: None, - } - } -} - -impl PluginInfo - for DynamicPlugin -{ - fn name(&self) -> &str { - self.name.as_str() - } - fn path(&self) -> &str { - self.starter.as_ref().map_or("", |v| v.path()) - } - fn status(&self) -> PluginStatus { - PluginStatus { - state: if self.starter.is_some() { - if self.instance.is_some() { - PluginState::Started - } else { - PluginState::Loaded - } - } else { - PluginState::Declared - }, - condition: self.condition.clone(), // TODO: request condition from started plugin - } - } -} - -impl - DeclaredPlugin for DynamicPlugin -{ - fn load(&mut self) -> ZResult<&mut dyn LoadedPlugin> { - if self.starter.is_none() { - let (lib, path) = self.source.load().add_error(&mut self.condition)?; - let starter = DynamicPluginStarter::new(lib, path).add_error(&mut self.condition)?; - self.starter = Some(starter); - } - Ok(self) - } - fn loaded(&self) -> Option<&dyn LoadedPlugin> { - if self.starter.is_some() { - Some(self) - } else { - None - } - } - fn loaded_mut(&mut self) -> Option<&mut dyn LoadedPlugin> { - if self.starter.is_some() { - Some(self) - } else { - None - } - } -} - -impl - LoadedPlugin for DynamicPlugin -{ - fn start(&mut self, args: &StartArgs) -> ZResult<&mut dyn StartedPlugin> { - let starter = self - .starter - .as_ref() - .ok_or_else(|| format!("Plugin `{}` not loaded", self.name)) - .add_error(&mut self.condition)?; - let already_started = self.instance.is_some(); - if !already_started { - let instance = starter - .start(self.name(), args) - .add_error(&mut self.condition)?; - self.instance = Some(instance); - } - Ok(self) - } - fn started(&self) -> Option<&dyn StartedPlugin> { - if self.instance.is_some() { - Some(self) - } else { - None - } - } - fn started_mut(&mut self) -> Option<&mut dyn StartedPlugin> { - if self.instance.is_some() { - Some(self) - } else { - None - } - } -} - -impl - StartedPlugin for DynamicPlugin -{ - fn stop(&mut self) { - self.instance = None; - } - fn instance(&self) -> &Instance { - self.instance.as_ref().unwrap() - } - fn instance_mut(&mut self) -> &mut Instance { - self.instance.as_mut().unwrap() - } -} - struct PluginRecord( Box + Send>, ); -impl - PluginRecord -{ +impl PluginRecord { fn new + Send + 'static>(plugin: P) -> Self { Self(Box::new(plugin)) } @@ -438,8 +60,8 @@ impl PluginInfo } } -impl - DeclaredPlugin for PluginRecord +impl DeclaredPlugin + for PluginRecord { fn load(&mut self) -> ZResult<&mut dyn LoadedPlugin> { self.0.load() @@ -460,10 +82,8 @@ pub struct PluginsManager plugins: Vec>, } -impl< - StartArgs: PluginStartArgs + 'static, - Instance: PluginInstance + 'static, - > PluginsManager +impl + PluginsManager { /// Constructs a new plugin manager with dynamic library loading enabled. pub fn dynamic>(loader: LibLoader, default_lib_prefix: S) -> Self { diff --git a/plugins/zenoh-plugin-trait/src/loading/dynamic_plugin.rs b/plugins/zenoh-plugin-trait/src/loading/dynamic_plugin.rs new file mode 100644 index 0000000000..898016e1be --- /dev/null +++ b/plugins/zenoh-plugin-trait/src/loading/dynamic_plugin.rs @@ -0,0 +1,225 @@ +// Copyright (c) 2023 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +use crate::*; +use std::path::{PathBuf, Path}; + +use libloading::Library; +use zenoh_result::{ZResult, bail}; +use zenoh_util::LibLoader; + + +/// This enum contains information where to load the plugin from. +pub enum DynamicPluginSource { + /// Load plugin with the name in String + `.so | .dll | .dylib` + /// in LibLoader's search paths. + ByName((LibLoader, String)), + /// Load first avalilable plugin from the list of path to plugin files (absolute or relative to the current working directory) + ByPaths(Vec), +} + +impl DynamicPluginSource { + fn load(&self) -> ZResult<(Library, PathBuf)> { + match self { + DynamicPluginSource::ByName((libloader, name)) => unsafe { + libloader.search_and_load(name) + }, + DynamicPluginSource::ByPaths(paths) => { + for path in paths { + match unsafe { LibLoader::load_file(path) } { + Ok((l, p)) => return Ok((l, p)), + Err(e) => log::warn!("Plugin {} load fail: {}", path, e), + } + } + bail!("Plugin not found in {:?}", &paths) + } + } + } +} + +struct DynamicPluginStarter { + _lib: Library, + path: PathBuf, + vtable: PluginVTable, +} + +impl + DynamicPluginStarter +{ + fn get_vtable(lib: &Library, path: &Path) -> ZResult> { + log::debug!("Loading plugin {}", &path.to_str().unwrap(),); + let get_plugin_loader_version = + unsafe { lib.get:: PluginLoaderVersion>(b"get_plugin_loader_version")? }; + let plugin_loader_version = get_plugin_loader_version(); + log::debug!("Plugin loader version: {}", &plugin_loader_version); + if plugin_loader_version != PLUGIN_LOADER_VERSION { + bail!( + "Plugin loader version mismatch: host = {}, plugin = {}", + PLUGIN_LOADER_VERSION, + plugin_loader_version + ); + } + let get_compatibility = unsafe { lib.get:: Compatibility>(b"get_compatibility")? }; + let plugin_compatibility_record = get_compatibility(); + let host_compatibility_record = Compatibility::new::(); + log::debug!( + "Plugin compativilty record: {:?}", + &plugin_compatibility_record + ); + if !plugin_compatibility_record.are_compatible(&host_compatibility_record) { + bail!( + "Plugin compatibility mismatch:\n\nHost:\n{}\nPlugin:\n{}\n", + host_compatibility_record, + plugin_compatibility_record + ); + } + let load_plugin = + unsafe { lib.get:: PluginVTable>(b"load_plugin")? }; + let vtable = load_plugin(); + Ok(vtable) + } + fn new(lib: Library, path: PathBuf) -> ZResult { + let vtable = Self::get_vtable(&lib, &path)?; + Ok(Self { + _lib: lib, + path, + vtable, + }) + } + fn start(&self, name: &str, args: &StartArgs) -> ZResult { + (self.vtable.start)(name, args) + } + fn path(&self) -> &str { + self.path.to_str().unwrap() + } +} + +pub struct DynamicPlugin { + name: String, + condition: PluginCondition, + source: DynamicPluginSource, + starter: Option>, + instance: Option, +} + +impl DynamicPlugin { + pub fn new(name: String, source: DynamicPluginSource) -> Self { + Self { + name, + condition: PluginCondition::new(), + source, + starter: None, + instance: None, + } + } +} + +impl PluginInfo + for DynamicPlugin +{ + fn name(&self) -> &str { + self.name.as_str() + } + fn path(&self) -> &str { + self.starter.as_ref().map_or("", |v| v.path()) + } + fn status(&self) -> PluginStatus { + PluginStatus { + state: if self.starter.is_some() { + if self.instance.is_some() { + PluginState::Started + } else { + PluginState::Loaded + } + } else { + PluginState::Declared + }, + condition: self.condition.clone(), // TODO: request condition from started plugin + } + } +} + +impl DeclaredPlugin + for DynamicPlugin +{ + fn load(&mut self) -> ZResult<&mut dyn LoadedPlugin> { + if self.starter.is_none() { + let (lib, path) = self.source.load().add_error(&mut self.condition)?; + let starter = DynamicPluginStarter::new(lib, path).add_error(&mut self.condition)?; + self.starter = Some(starter); + } + Ok(self) + } + fn loaded(&self) -> Option<&dyn LoadedPlugin> { + if self.starter.is_some() { + Some(self) + } else { + None + } + } + fn loaded_mut(&mut self) -> Option<&mut dyn LoadedPlugin> { + if self.starter.is_some() { + Some(self) + } else { + None + } + } +} + +impl LoadedPlugin + for DynamicPlugin +{ + fn start(&mut self, args: &StartArgs) -> ZResult<&mut dyn StartedPlugin> { + let starter = self + .starter + .as_ref() + .ok_or_else(|| format!("Plugin `{}` not loaded", self.name)) + .add_error(&mut self.condition)?; + let already_started = self.instance.is_some(); + if !already_started { + let instance = starter + .start(self.name(), args) + .add_error(&mut self.condition)?; + self.instance = Some(instance); + } + Ok(self) + } + fn started(&self) -> Option<&dyn StartedPlugin> { + if self.instance.is_some() { + Some(self) + } else { + None + } + } + fn started_mut(&mut self) -> Option<&mut dyn StartedPlugin> { + if self.instance.is_some() { + Some(self) + } else { + None + } + } +} + +impl StartedPlugin + for DynamicPlugin +{ + fn stop(&mut self) { + self.instance = None; + } + fn instance(&self) -> &Instance { + self.instance.as_ref().unwrap() + } + fn instance_mut(&mut self) -> &mut Instance { + self.instance.as_mut().unwrap() + } +} + diff --git a/plugins/zenoh-plugin-trait/src/loading/static_plugin.rs b/plugins/zenoh-plugin-trait/src/loading/static_plugin.rs new file mode 100644 index 0000000000..eb65d0058a --- /dev/null +++ b/plugins/zenoh-plugin-trait/src/loading/static_plugin.rs @@ -0,0 +1,114 @@ +// +// Copyright (c) 2023 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +use crate::*; +use std::marker::PhantomData; +use zenoh_result::ZResult; + +pub struct StaticPlugin +where + P: Plugin, +{ + instance: Option, + phantom: PhantomData

, +} + +impl StaticPlugin +where + P: Plugin, +{ + pub fn new() -> Self { + Self { + instance: None, + phantom: PhantomData, + } + } +} + +impl PluginInfo for StaticPlugin +where + P: Plugin, +{ + fn name(&self) -> &str { + P::STATIC_NAME + } + fn path(&self) -> &str { + "" + } + fn status(&self) -> PluginStatus { + PluginStatus { + state: self + .instance + .as_ref() + .map_or(PluginState::Loaded, |_| PluginState::Started), + condition: PluginCondition::new(), // TODO: request runnnig plugin status + } + } +} + +impl DeclaredPlugin + for StaticPlugin +where + P: Plugin, +{ + fn load(&mut self) -> ZResult<&mut dyn LoadedPlugin> { + Ok(self) + } + fn loaded(&self) -> Option<&dyn LoadedPlugin> { + Some(self) + } + fn loaded_mut(&mut self) -> Option<&mut dyn LoadedPlugin> { + Some(self) + } +} + +impl LoadedPlugin + for StaticPlugin +where + P: Plugin, +{ + fn start(&mut self, args: &StartArgs) -> ZResult<&mut dyn StartedPlugin> { + if self.instance.is_none() { + self.instance = Some(P::start(self.name(), args)?); + } + Ok(self) + } + fn started(&self) -> Option<&dyn StartedPlugin> { + if self.instance.is_some() { + Some(self) + } else { + None + } + } + fn started_mut(&mut self) -> Option<&mut dyn StartedPlugin> { + if self.instance.is_some() { + Some(self) + } else { + None + } + } +} + +impl StartedPlugin + for StaticPlugin +where + P: Plugin, +{ + fn stop(&mut self) {} + fn instance(&self) -> &Instance { + self.instance.as_ref().unwrap() + } + fn instance_mut(&mut self) -> &mut Instance { + self.instance.as_mut().unwrap() + } +} diff --git a/plugins/zenoh-plugin-trait/src/plugin.rs b/plugins/zenoh-plugin-trait/src/plugin.rs new file mode 100644 index 0000000000..4ec3fa9d96 --- /dev/null +++ b/plugins/zenoh-plugin-trait/src/plugin.rs @@ -0,0 +1,110 @@ +// +// Copyright (c) 2023 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +use std::borrow::Cow; +use zenoh_result::ZResult; + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum PluginState { + Declared, + Loaded, + Started, +} + +#[derive(Clone, Debug, PartialEq, Eq, Default)] +pub struct PluginCondition { + warnings: Vec>, + errors: Vec>, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct PluginStatus { + pub state: PluginState, + pub condition: PluginCondition, +} + +pub trait PluginInfo { + fn name(&self) -> &str; + fn path(&self) -> &str; + fn status(&self) -> PluginStatus; +} + +pub trait PluginControl { + fn plugins(&self) -> Vec>; + // fn status(&self, name: &str) -> PluginStatus; +} + +pub trait PluginStructVersion { + /// The version of the structure implementing this trait. After any channge in the structure or it's dependencies + /// whcich may affect the ABI, this version should be incremented. + fn version() -> u64; + /// The features enabled during comiplation of the structure implementing this trait. + /// Different features between the plugin and the host may cuase ABI incompatibility even if the structure version is the same. + /// Use `concat_enabled_features!` to generate this string + fn features() -> &'static str; +} + +pub trait PluginStartArgs: PluginStructVersion {} + +pub trait PluginInstance: PluginStructVersion + PluginControl + Send {} + +pub trait Plugin: Sized + 'static { + type StartArgs: PluginStartArgs; + type Instance: PluginInstance; + /// Your plugins' default name when statically linked. + const STATIC_NAME: &'static str; + /// Starts your plugin. Use `Ok` to return your plugin's control structure + fn start(name: &str, args: &Self::StartArgs) -> ZResult; +} + +impl PluginCondition { + pub fn new() -> Self { + Self::default() + } + pub fn clear(&mut self) { + self.warnings.clear(); + self.errors.clear(); + } + pub fn add_error>>(&mut self, error: S) { + self.errors.push(error.into()); + } + pub fn add_warning>>(&mut self, warning: S) { + self.warnings.push(warning.into()); + } + pub fn errors(&self) -> &[Cow<'static, str>] { + &self.errors + } + pub fn warnings(&self) -> &[Cow<'static, str>] { + &self.warnings + } +} + +pub trait PluginConditionSetter { + fn add_error(self, condition: &mut PluginCondition) -> Self; + fn add_warning(self, condition: &mut PluginCondition) -> Self; +} + +impl PluginConditionSetter for core::result::Result { + fn add_error(self, condition: &mut PluginCondition) -> Self { + if let Err(e) = &self { + condition.add_error(e.to_string()); + } + self + } + fn add_warning(self, condition: &mut PluginCondition) -> Self { + if let Err(e) = &self { + condition.add_warning(e.to_string()); + } + self + } +} diff --git a/plugins/zenoh-plugin-trait/src/vtable.rs b/plugins/zenoh-plugin-trait/src/vtable.rs index 6cf16feebd..ec863d73ca 100644 --- a/plugins/zenoh-plugin-trait/src/vtable.rs +++ b/plugins/zenoh-plugin-trait/src/vtable.rs @@ -13,14 +13,11 @@ // ZettaScale Zenoh Team, // use crate::*; -pub use no_mangle::*; use zenoh_result::ZResult; use std::fmt::Display; pub type PluginLoaderVersion = u64; pub const PLUGIN_LOADER_VERSION: PluginLoaderVersion = 1; -pub const FEATURES: &str = - concat_enabled_features!(prefix = "zenoh-plugin-trait", features = ["default"]); type StartFn = fn(&str, &StartArgs) -> ZResult; @@ -28,7 +25,7 @@ type StartFn = fn(&str, &StartArgs) -> ZResult; pub struct PluginVTable { pub start: StartFn, } -impl CompatibilityVersion for PluginVTable { +impl PluginStructVersion for PluginVTable { fn version() -> u64 { 1 } @@ -46,7 +43,7 @@ pub struct StructVersion { } impl StructVersion { - pub fn new() -> Self { + pub fn new() -> Self { Self { version: T::version(), name: std::any::type_name::(), @@ -75,7 +72,7 @@ pub struct Compatibility { } impl Compatibility { - pub fn new() -> Self { + pub fn new() -> Self { let rust_version = RustVersion::new(); let vtable_version = StructVersion::new::>(); let start_args_version = StructVersion::new::(); @@ -176,30 +173,29 @@ impl PluginVTable { pub use no_mangle::*; #[cfg(feature = "no_mangle")] pub mod no_mangle { - /// This macro will add a non-mangled `load_plugin` function to the library if feature `no_mangle` is enabled (which it is by default). + /// This macro will add a non-mangled functions which provides plugin version and loads it if feature `no_mangle` is enabled (which it is by default). #[macro_export] macro_rules! declare_plugin { ($ty: path) => { #[no_mangle] - fn get_plugin_loader_version() -> $crate::prelude::PluginLoaderVersion { - $crate::prelude::PLUGIN_LOADER_VERSION + fn get_plugin_loader_version() -> $crate::PluginLoaderVersion { + $crate::PLUGIN_LOADER_VERSION } #[no_mangle] - fn get_compatibility() -> $crate::prelude::Compatibility { - // TODO: add vtable version (including type parameters) to the compatibility information - $crate::prelude::Compatibility::new::< - <$ty as $crate::prelude::Plugin>::StartArgs, - <$ty as $crate::prelude::Plugin>::Instance, + fn get_compatibility() -> $crate::Compatibility { + $crate::Compatibility::new::< + <$ty as $crate::Plugin>::StartArgs, + <$ty as $crate::Plugin>::Instance, >() } #[no_mangle] - fn load_plugin() -> $crate::prelude::PluginVTable< - <$ty as $crate::prelude::Plugin>::StartArgs, - <$ty as $crate::prelude::Plugin>::Instance, + fn load_plugin() -> $crate::PluginVTable< + <$ty as $crate::Plugin>::StartArgs, + <$ty as $crate::Plugin>::Instance, > { - $crate::prelude::PluginVTable::new::<$ty>() + $crate::PluginVTable::new::<$ty>() } }; } diff --git a/zenoh/src/net/runtime/mod.rs b/zenoh/src/net/runtime/mod.rs index 1347efdd57..054a7421d0 100644 --- a/zenoh/src/net/runtime/mod.rs +++ b/zenoh/src/net/runtime/mod.rs @@ -37,7 +37,7 @@ use stop_token::future::FutureExt; use stop_token::{StopSource, TimedOutError}; use uhlc::{HLCBuilder, HLC}; use zenoh_link::{EndPoint, Link}; -use zenoh_plugin_trait::{CompatibilityVersion, PluginStartArgs}; +use zenoh_plugin_trait::{PluginStructVersion, PluginStartArgs}; use zenoh_protocol::core::{whatami::WhatAmIMatcher, Locator, WhatAmI, ZenohId}; use zenoh_protocol::network::{NetworkBody, NetworkMessage}; use zenoh_result::{bail, ZResult}; @@ -65,7 +65,7 @@ pub struct Runtime { state: Arc, } -impl CompatibilityVersion for Runtime { +impl PluginStructVersion for Runtime { fn version() -> u64 { 1 } diff --git a/zenoh/src/plugins/sealed.rs b/zenoh/src/plugins/sealed.rs index 14c658a99c..3c61e40b75 100644 --- a/zenoh/src/plugins/sealed.rs +++ b/zenoh/src/plugins/sealed.rs @@ -33,7 +33,7 @@ pub type StartArgs = Runtime; /// A zenoh plugin, when started, must return this type. pub type RunningPlugin = Box; -impl CompatibilityVersion for RunningPlugin { +impl PluginStructVersion for RunningPlugin { fn version() -> u64 { 1 } @@ -92,9 +92,9 @@ pub trait RunningPluginTrait: Send + Sync { } /// The zenoh plugins manager. It handles the full lifetime of plugins, from loading to destruction. -pub type PluginsManager = zenoh_plugin_trait::loading::PluginsManager; +pub type PluginsManager = zenoh_plugin_trait::PluginsManager; -pub use zenoh_plugin_trait::CompatibilityVersion; +pub use zenoh_plugin_trait::PluginStructVersion; pub use zenoh_plugin_trait::Plugin; use zenoh_plugin_trait::PluginControl; use zenoh_plugin_trait::PluginInstance;