1use std::fmt::Display;
16
17use crate::{Plugin, PluginInstance, PluginStartArgs, PluginVTable};
18
19pub trait StructVersion {
20 fn struct_version() -> u64;
23 fn struct_features() -> &'static str;
27}
28
29#[repr(C)]
30#[derive(Debug, PartialEq, Eq, Clone)]
31pub struct PluginStructVersion {
32 pub version: u64,
33 pub name: &'static str,
34 pub features: &'static str,
35}
36
37impl PluginStructVersion {
38 pub fn new<T: StructVersion>() -> Self {
39 Self {
40 version: T::struct_version(),
41 name: std::any::type_name::<T>(),
42 features: T::struct_features(),
43 }
44 }
45}
46
47impl Display for PluginStructVersion {
48 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49 write!(
50 f,
51 " version: {}\n type: {}\n features: {}\n",
52 self.version, self.name, self.features
53 )
54 }
55}
56
57#[repr(C)]
58#[derive(Debug, PartialEq, Eq, Clone)]
59pub struct Compatibility {
60 rust_version: Option<RustVersion>,
61 vtable_version: Option<PluginStructVersion>,
62 start_args_version: Option<PluginStructVersion>,
63 instance_version: Option<PluginStructVersion>,
64 plugin_version: Option<&'static str>,
65 plugin_long_version: Option<&'static str>,
66}
67
68impl Compatibility {
69 pub fn with_plugin_version<
70 StartArgsType: PluginStartArgs,
71 InstanceType: PluginInstance,
72 PluginType: Plugin<StartArgs = StartArgsType, Instance = InstanceType>,
73 >() -> Self {
74 let rust_version = Some(RustVersion::new());
75 let vtable_version = Some(PluginStructVersion::new::<
76 PluginVTable<StartArgsType, InstanceType>,
77 >());
78 let start_args_version = Some(PluginStructVersion::new::<StartArgsType>());
79 let instance_version = Some(PluginStructVersion::new::<InstanceType>());
80 let plugin_version = Some(PluginType::PLUGIN_VERSION);
81 let plugin_long_version = Some(PluginType::PLUGIN_LONG_VERSION);
82 Self {
83 rust_version,
84 vtable_version,
85 start_args_version,
86 instance_version,
87 plugin_version,
88 plugin_long_version,
89 }
90 }
91 pub fn with_empty_plugin_version<
92 StartArgsType: PluginStartArgs,
93 InstanceType: PluginInstance,
94 >() -> Self {
95 let rust_version = Some(RustVersion::new());
96 let vtable_version = Some(PluginStructVersion::new::<
97 PluginVTable<StartArgsType, InstanceType>,
98 >());
99 let start_args_version = Some(PluginStructVersion::new::<StartArgsType>());
100 let instance_version = Some(PluginStructVersion::new::<InstanceType>());
101 Self {
102 rust_version,
103 vtable_version,
104 start_args_version,
105 instance_version,
106 plugin_version: None,
107 plugin_long_version: None,
108 }
109 }
110 pub fn plugin_version(&self) -> Option<&'static str> {
111 self.plugin_version
112 }
113 pub fn plugin_long_version(&self) -> Option<&'static str> {
114 self.plugin_long_version
115 }
116 pub fn compare(&mut self, other: &mut Self) -> bool {
122 let mut result = true;
123 Self::compare_field_fn(
124 &mut result,
125 &mut self.rust_version,
126 &mut other.rust_version,
127 RustVersion::are_compatible,
128 );
129 Self::compare_field(
130 &mut result,
131 &mut self.vtable_version,
132 &mut other.vtable_version,
133 );
134 Self::compare_field(
135 &mut result,
136 &mut self.start_args_version,
137 &mut other.start_args_version,
138 );
139 Self::compare_field(
140 &mut result,
141 &mut self.instance_version,
142 &mut other.instance_version,
143 );
144 Self::compare_field(
146 &mut result,
147 &mut self.plugin_version,
148 &mut other.plugin_version,
149 );
150 Self::compare_field(
151 &mut result,
152 &mut self.plugin_long_version,
153 &mut other.plugin_long_version,
154 );
155 result
156 }
157
158 fn compare_field_fn<T, F: Fn(&T, &T) -> bool>(
160 result: &mut bool,
161 a: &mut Option<T>,
162 b: &mut Option<T>,
163 compare: F,
164 ) {
165 let compatible = if let (Some(a), Some(b)) = (&a, &b) {
166 compare(a, b)
167 } else {
168 true
169 };
170 if compatible {
171 *a = None;
172 *b = None;
173 } else {
174 *result = false;
175 }
176 }
177 fn compare_field<T: PartialEq>(result: &mut bool, a: &mut Option<T>, b: &mut Option<T>) {
178 Self::compare_field_fn(result, a, b, |a, b| a == b);
179 }
180}
181
182impl Display for Compatibility {
183 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
184 if let Some(rust_version) = &self.rust_version {
185 writeln!(f, "Rust version:\n{}", rust_version)?;
186 }
187 if let Some(vtable_version) = &self.vtable_version {
188 writeln!(f, "VTable version:\n{}", vtable_version)?;
189 }
190 if let Some(start_args_version) = &self.start_args_version {
191 writeln!(f, "StartArgs version:\n{}", start_args_version)?;
192 }
193 if let Some(instance_version) = &self.instance_version {
194 writeln!(f, "Instance version:\n{}", instance_version)?;
195 }
196 if let Some(plugin_version) = &self.plugin_version {
197 writeln!(f, "Plugin version: {}", plugin_version)?;
198 }
199 Ok(())
200 }
201}
202
203#[repr(C)]
204#[derive(Debug, PartialEq, Eq, Clone)]
205pub struct RustVersion {
206 major: u32,
207 minor: u32,
208 patch: u32,
209 stable: bool,
210 commit: &'static str,
211}
212
213impl Display for RustVersion {
214 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
215 write!(
216 f,
217 "Rust {}.{}.{}{} commit {}",
218 self.major,
219 self.minor,
220 self.patch,
221 if self.stable { "" } else { "-nightly" },
222 self.commit
223 )
224 }
225}
226
227const RELEASE_AND_COMMIT: (&str, &str) = zenoh_macros::rustc_version_release!();
228impl RustVersion {
229 pub fn new() -> Self {
230 let (release, commit) = RELEASE_AND_COMMIT;
231 let (release, stable) = if let Some(p) = release.chars().position(|c| c == '-') {
232 (&release[..p], false)
233 } else {
234 (release, true)
235 };
236 let mut split = release.split('.').map(|s| s.trim());
237 RustVersion {
238 major: split.next().unwrap().parse().unwrap(),
239 minor: split.next().unwrap().parse().unwrap(),
240 patch: split.next().unwrap().parse().unwrap(),
241 stable,
242 commit,
243 }
244 }
245 pub fn are_compatible(a: &Self, b: &Self) -> bool {
246 if a.stable && b.stable {
247 a.major == b.major && a.minor == b.minor && a.patch == b.patch
248 } else {
249 a == b
250 }
251 }
252}
253
254impl Default for RustVersion {
255 fn default() -> Self {
256 Self::new()
257 }
258}