Skip to content

Commit

Permalink
compatibility code in separate .rs, minor changes
Browse files Browse the repository at this point in the history
  • Loading branch information
milyin committed Jan 18, 2024
1 parent be24a42 commit 3b10fd7
Show file tree
Hide file tree
Showing 9 changed files with 279 additions and 259 deletions.
6 changes: 3 additions & 3 deletions plugins/zenoh-backend-traits/src/config.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use const_format::concatcp;
//
// Copyright (c) 2023 ZettaScale Technology
//
Expand All @@ -12,13 +11,14 @@ use const_format::concatcp;
// Contributors:
// ZettaScale Zenoh Team, <[email protected]>
//
use const_format::concatcp;
use derive_more::{AsMut, AsRef};
use schemars::JsonSchema;
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::{PluginStartArgs, PluginStructVersion};
use zenoh_plugin_trait::{PluginStartArgs, StructVersion};
use zenoh_result::{bail, zerror, Error};

#[derive(JsonSchema, Debug, Clone, AsMut, AsRef)]
Expand Down Expand Up @@ -69,7 +69,7 @@ pub struct ReplicaConfig {
pub delta: Duration,
}

impl PluginStructVersion for VolumeConfig {
impl StructVersion for VolumeConfig {
fn struct_version() -> u64 {
1
}
Expand Down
4 changes: 2 additions & 2 deletions plugins/zenoh-backend-traits/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ use zenoh::queryable::ReplyBuilder;
use zenoh::time::Timestamp;
use zenoh::value::Value;
pub use zenoh::Result as ZResult;
use zenoh_plugin_trait::{PluginControl, PluginInstance, PluginStatusRec, PluginStructVersion};
use zenoh_plugin_trait::{PluginControl, PluginInstance, PluginStatusRec, StructVersion};
use zenoh_util::concat_enabled_features;

pub mod config;
Expand Down Expand Up @@ -222,7 +222,7 @@ pub trait Volume: Send + Sync {

pub type VolumeInstance = Box<dyn Volume + 'static>;

impl PluginStructVersion for VolumeInstance {
impl StructVersion for VolumeInstance {
fn struct_version() -> u64 {
1
}
Expand Down
258 changes: 258 additions & 0 deletions plugins/zenoh-plugin-trait/src/compatibility.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,258 @@
//
// 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, <[email protected]>
//

use std::fmt::Display;

use crate::{Plugin, PluginInstance, PluginStartArgs, PluginVTable};

pub trait StructVersion {
/// The version of the structure which implements this trait. After any change in the structure or its dependencies
/// which may affect the ABI, this version should be incremented.
fn struct_version() -> u64;
/// The features enabled during compilation of the structure implementing this trait.
/// Different features between the plugin and the host may cause ABI incompatibility even if the structure version is the same.
/// Use `concat_enabled_features!` to generate this string
fn struct_features() -> &'static str;
}

#[repr(C)]
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct PluginStructVersion {
pub version: u64,
pub name: &'static str,
pub features: &'static str,
}

impl PluginStructVersion {
pub fn new<T: StructVersion>() -> Self {
Self {
version: T::struct_version(),
name: std::any::type_name::<T>(),
features: T::struct_features(),
}
}
}

impl Display for PluginStructVersion {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
" version: {}\n type: {}\n features: {}\n",
self.version, self.name, self.features
)
}
}

#[repr(C)]
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct Compatibility {
rust_version: Option<RustVersion>,
vtable_version: Option<PluginStructVersion>,
start_args_version: Option<PluginStructVersion>,
instance_version: Option<PluginStructVersion>,
plugin_version: Option<&'static str>,
plugin_long_version: Option<&'static str>,
}

impl Compatibility {
pub fn with_plugin_version<
StartArgsType: PluginStartArgs,
InstanceType: PluginInstance,
PluginType: Plugin<StartArgs = StartArgsType, Instance = InstanceType>,
>() -> Self {
let rust_version = Some(RustVersion::new());
let vtable_version = Some(PluginStructVersion::new::<
PluginVTable<StartArgsType, InstanceType>,
>());
let start_args_version = Some(PluginStructVersion::new::<StartArgsType>());
let instance_version = Some(PluginStructVersion::new::<InstanceType>());
let plugin_version = Some(PluginType::PLUGIN_VERSION);
let plugin_long_version = Some(PluginType::PLUGIN_LONG_VERSION);
Self {
rust_version,
vtable_version,
start_args_version,
instance_version,
plugin_version,
plugin_long_version,
}
}
pub fn with_empty_plugin_version<
StartArgsType: PluginStartArgs,
InstanceType: PluginInstance,
>() -> Self {
let rust_version = Some(RustVersion::new());
let vtable_version = Some(PluginStructVersion::new::<
PluginVTable<StartArgsType, InstanceType>,
>());
let start_args_version = Some(PluginStructVersion::new::<StartArgsType>());
let instance_version = Some(PluginStructVersion::new::<InstanceType>());
Self {
rust_version,
vtable_version,
start_args_version,
instance_version,
plugin_version: None,
plugin_long_version: None,
}
}
pub fn plugin_version(&self) -> Option<&'static str> {
self.plugin_version
}
pub fn plugin_long_version(&self) -> Option<&'static str> {
self.plugin_long_version
}
/// Compares fields if both are Some, otherwise skips the comparison.
/// Returns true if all the comparisons returned true, otherwise false.
/// If comparison passed or skipped, the corresponding field in both structs is set to None.
/// If comparison failed, the corresponding field in both structs is kept as is.
/// This allows not only to check compatibility, but also point to exact reasons of incompatibility.
pub fn compare(&mut self, other: &mut Self) -> bool {
let mut result = true;
Self::compare_field_fn(
&mut result,
&mut self.rust_version,
&mut other.rust_version,
RustVersion::are_compatible,
);
Self::compare_field(
&mut result,
&mut self.vtable_version,
&mut other.vtable_version,
);
Self::compare_field(
&mut result,
&mut self.start_args_version,
&mut other.start_args_version,
);
Self::compare_field(
&mut result,
&mut self.instance_version,
&mut other.instance_version,
);
// TODO: here we can later implement check for plugin version range compatibility
Self::compare_field(
&mut result,
&mut self.plugin_version,
&mut other.plugin_version,
);
Self::compare_field(
&mut result,
&mut self.plugin_long_version,
&mut other.plugin_long_version,
);
result
}

// Utility function for compare single field
fn compare_field_fn<T, F: Fn(&T, &T) -> bool>(
result: &mut bool,
a: &mut Option<T>,
b: &mut Option<T>,
compare: F,
) {
let compatible = if let (Some(a), Some(b)) = (&a, &b) {
compare(a, b)
} else {
true
};
if compatible {
*a = None;
*b = None;
} else {
*result = false;
}
}
fn compare_field<T: PartialEq>(result: &mut bool, a: &mut Option<T>, b: &mut Option<T>) {
Self::compare_field_fn(result, a, b, |a, b| a == b);
}
}

impl Display for Compatibility {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(rust_version) = &self.rust_version {
writeln!(f, "Rust version:\n{}", rust_version)?;
}
if let Some(vtable_version) = &self.vtable_version {
writeln!(f, "VTable version:\n{}", vtable_version)?;
}
if let Some(start_args_version) = &self.start_args_version {
writeln!(f, "StartArgs version:\n{}", start_args_version)?;
}
if let Some(instance_version) = &self.instance_version {
writeln!(f, "Instance version:\n{}", instance_version)?;
}
if let Some(plugin_version) = &self.plugin_version {
writeln!(f, "Plugin version: {}", plugin_version)?;
}
Ok(())
}
}

#[repr(C)]
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct RustVersion {
major: u64,
minor: u64,
patch: u64,
stable: bool,
commit: &'static str,
}

impl Display for RustVersion {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Rust {}.{}.{}{} commit {}",
self.major,
self.minor,
self.patch,
if self.stable { "" } else { "-nightly" },
self.commit
)
}
}

const RELEASE_AND_COMMIT: (&str, &str) = zenoh_macros::rustc_version_release!();
impl RustVersion {
pub fn new() -> Self {
let (release, commit) = RELEASE_AND_COMMIT;
let (release, stable) = if let Some(p) = release.chars().position(|c| c == '-') {
(&release[..p], false)
} else {
(release, true)
};
let mut split = release.split('.').map(|s| s.trim());
RustVersion {
major: split.next().unwrap().parse().unwrap(),
minor: split.next().unwrap().parse().unwrap(),
patch: split.next().unwrap().parse().unwrap(),
stable,
commit,
}
}
pub fn are_compatible(a: &Self, b: &Self) -> bool {
if a.stable && b.stable {
a.major == b.major && a.minor == b.minor && a.patch == b.patch
} else {
a == b
}
}
}

impl Default for RustVersion {
fn default() -> Self {
Self::new()
}
}
6 changes: 4 additions & 2 deletions plugins/zenoh-plugin-trait/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,18 @@
//!
//! Dynamic pluign is a shared library which exports set of C-repr (unmangled) functions which allows to check plugin compatibility and create plugin instance. These functiuons are defined automatically by [`declare_plugin`](crate::declare_plugin) macro.
//!
mod compatibility;
mod manager;
mod plugin;
mod vtable;

pub use compatibility::{Compatibility, PluginStructVersion, StructVersion};
pub use manager::{DeclaredPlugin, LoadedPlugin, PluginsManager, StartedPlugin};
pub use plugin::{
Plugin, PluginConditionSetter, PluginControl, PluginInstance, PluginReport, PluginStartArgs,
PluginState, PluginStatus, PluginStatusRec, PluginStructVersion,
PluginState, PluginStatus, PluginStatusRec,
};
pub use vtable::{Compatibility, PluginLoaderVersion, PluginVTable, PLUGIN_LOADER_VERSION};
pub use vtable::{PluginLoaderVersion, PluginVTable, PLUGIN_LOADER_VERSION};
use zenoh_util::concat_enabled_features;

pub const FEATURES: &str =
Expand Down
15 changes: 3 additions & 12 deletions plugins/zenoh-plugin-trait/src/plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
// Contributors:
// ZettaScale Zenoh Team, <[email protected]>
//
use crate::StructVersion;
use serde::{Deserialize, Serialize};
use std::{borrow::Cow, ops::BitOrAssign};
use zenoh_keyexpr::keyexpr;
Expand Down Expand Up @@ -152,19 +153,9 @@ pub trait PluginControl {
}
}

pub trait PluginStructVersion {
/// The version of the structure implementing this trait. After any change in the structure or its dependencies
/// which may affect the ABI, this version should be incremented.
fn struct_version() -> u64;
/// The features enabled during compilation of the structure implementing this trait.
/// Different features between the plugin and the host may cause ABI incompatibility even if the structure version is the same.
/// Use `concat_enabled_features!` to generate this string
fn struct_features() -> &'static str;
}

pub trait PluginStartArgs: PluginStructVersion {}
pub trait PluginStartArgs: StructVersion {}

pub trait PluginInstance: PluginStructVersion + PluginControl + Send {}
pub trait PluginInstance: StructVersion + PluginControl + Send {}

/// Base plugin trait. The loaded plugin
pub trait Plugin: Sized + 'static {
Expand Down
Loading

0 comments on commit 3b10fd7

Please sign in to comment.